From 509e02db33faf9d6cc2a19112382e765ed5b9553 Mon Sep 17 00:00:00 2001
From: Christophe Geuzaine <cgeuzaine@ulg.ac.be>
Date: Wed, 21 Sep 2005 17:29:42 +0000
Subject: [PATCH] moving all external code to contrib subdirectory

---
 Common/Makefile                               |     5 +-
 Fltk/Makefile                                 |     5 +-
 Geo/Makefile                                  |     7 +-
 Graphics/Makefile                             |     5 +-
 Makefile                                      |    22 +-
 Mesh/Makefile                                 |    11 +-
 Parser/Makefile                               |     5 +-
 Plugin/Makefile                               |     7 +-
 configure                                     |   185 +-
 configure.in                                  |    75 +-
 contrib/ANN/Copyright.txt                     |    47 +
 contrib/ANN/License.txt                       |   450 +
 contrib/ANN/Makefile                          |   114 +
 contrib/ANN/include/ANN/ANN.h                 |   829 +
 contrib/ANN/include/ANN/ANNperf.h             |   226 +
 contrib/ANN/include/ANN/ANNx.h                |   170 +
 contrib/ANN/src/ANN.cpp                       |   198 +
 contrib/ANN/src/bd_fix_rad_search.cpp         |    61 +
 contrib/ANN/src/bd_pr_search.cpp              |    62 +
 contrib/ANN/src/bd_search.cpp                 |    61 +
 contrib/ANN/src/bd_tree.cpp                   |   417 +
 contrib/ANN/src/bd_tree.h                     |   100 +
 contrib/ANN/src/brute.cpp                     |   109 +
 contrib/ANN/src/kd_dump.cpp                   |   444 +
 contrib/ANN/src/kd_fix_rad_search.cpp         |   183 +
 contrib/ANN/src/kd_fix_rad_search.h           |    44 +
 contrib/ANN/src/kd_pr_search.cpp              |   219 +
 contrib/ANN/src/kd_pr_search.h                |    49 +
 contrib/ANN/src/kd_search.cpp                 |   210 +
 contrib/ANN/src/kd_search.h                   |    48 +
 contrib/ANN/src/kd_split.cpp                  |   428 +
 contrib/ANN/src/kd_split.h                    |    85 +
 contrib/ANN/src/kd_tree.cpp                   |   405 +
 contrib/ANN/src/kd_tree.h                     |   197 +
 contrib/ANN/src/kd_util.cpp                   |   439 +
 contrib/ANN/src/kd_util.h                     |   124 +
 contrib/ANN/src/perf.cpp                      |   134 +
 contrib/ANN/src/pr_queue.h                    |   125 +
 contrib/ANN/src/pr_queue_k.h                  |   118 +
 contrib/MathEval/Makefile                     |    79 +
 contrib/MathEval/README                       |    25 +
 contrib/MathEval/common.h                     |    37 +
 contrib/MathEval/matheval.cpp                 |   248 +
 contrib/MathEval/matheval.h                   |    89 +
 contrib/MathEval/node.cpp                     |   653 +
 contrib/MathEval/node.h                       |   111 +
 contrib/MathEval/parser.tab.cpp               |  1044 +
 contrib/MathEval/parser.tab.hpp               |    11 +
 contrib/MathEval/parser.y                     |   126 +
 contrib/MathEval/scanner.l                    |   130 +
 contrib/MathEval/scanner.yy.cpp               |  1716 ++
 contrib/MathEval/symbol_table.cpp             |   212 +
 contrib/MathEval/symbol_table.h               |    79 +
 contrib/MathEval/xmath.cpp                    |   101 +
 contrib/MathEval/xmath.h                      |    35 +
 contrib/Metis/Makefile                        |   103 +
 contrib/Metis/balance.c                       |   278 +
 contrib/Metis/bucketsort.c                    |    43 +
 contrib/Metis/ccgraph.c                       |   599 +
 contrib/Metis/coarsen.c                       |    83 +
 contrib/Metis/compress.c                      |   256 +
 contrib/Metis/debug.c                         |   239 +
 contrib/Metis/defs.h                          |   161 +
 contrib/Metis/estmem.c                        |   157 +
 contrib/Metis/fm.c                            |   194 +
 contrib/Metis/fortran.c                       |   141 +
 contrib/Metis/frename.c                       |   312 +
 contrib/Metis/graph.c                         |   616 +
 contrib/Metis/initpart.c                      |   422 +
 contrib/Metis/kmetis.c                        |   129 +
 contrib/Metis/kvmetis.c                       |   130 +
 contrib/Metis/kwayfm.c                        |   672 +
 contrib/Metis/kwayrefine.c                    |   392 +
 contrib/Metis/kwayvolfm.c                     |  1778 ++
 contrib/Metis/kwayvolrefine.c                 |   460 +
 contrib/Metis/macros.h                        |   143 +
 contrib/Metis/match.c                         |   267 +
 contrib/Metis/mbalance.c                      |   260 +
 contrib/Metis/mbalance2.c                     |   328 +
 contrib/Metis/mcoarsen.c                      |    91 +
 contrib/Metis/memory.c                        |   208 +
 contrib/Metis/mesh.c                          |   398 +
 contrib/Metis/meshpart.c                      |   204 +
 contrib/Metis/metis.h                         |    37 +
 contrib/Metis/mfm.c                           |   344 +
 contrib/Metis/mfm2.c                          |   349 +
 contrib/Metis/mincover.c                      |   259 +
 contrib/Metis/minitpart.c                     |   355 +
 contrib/Metis/minitpart2.c                    |   368 +
 contrib/Metis/mkmetis.c                       |   123 +
 contrib/Metis/mkwayfmh.c                      |   677 +
 contrib/Metis/mkwayrefine.c                   |   297 +
 contrib/Metis/mmatch.c                        |   506 +
 contrib/Metis/mmd.c                           |   593 +
 contrib/Metis/mpmetis.c                       |   402 +
 contrib/Metis/mrefine.c                       |   219 +
 contrib/Metis/mrefine2.c                      |    55 +
 contrib/Metis/mutil.c                         |   101 +
 contrib/Metis/myqsort.c                       |   547 +
 contrib/Metis/ometis.c                        |   764 +
 contrib/Metis/parmetis.c                      |   371 +
 contrib/Metis/pmetis.c                        |   341 +
 contrib/Metis/pqueue.c                        |   579 +
 contrib/Metis/proto.h                         |   505 +
 contrib/Metis/refine.c                        |   204 +
 contrib/Metis/rename.h                        |   418 +
 contrib/Metis/separator.c                     |   284 +
 contrib/Metis/sfm.c                           |  1069 +
 contrib/Metis/srefine.c                       |   169 +
 contrib/Metis/stat.c                          |   287 +
 contrib/Metis/struct.h                        |   251 +
 contrib/Metis/subdomains.c                    |  1295 +
 contrib/Metis/timing.c                        |    74 +
 contrib/Metis/util.c                          |   519 +
 contrib/NR/Makefile                           |    77 +
 contrib/NR/brent.cpp                          |    77 +
 contrib/NR/dpythag.cpp                        |    14 +
 contrib/NR/dsvdcmp.cpp                        |   183 +
 contrib/NR/fdjac.cpp                          |    29 +
 contrib/NR/fmin.cpp                           |    20 +
 contrib/NR/lnsrch.cpp                         |    65 +
 contrib/NR/lubksb.cpp                         |    23 +
 contrib/NR/ludcmp.cpp                         |    60 +
 contrib/NR/mnbrak.cpp                         |    67 +
 contrib/NR/newt.cpp                           |    92 +
 contrib/NR/nrutil.cpp                         |   620 +
 contrib/NR/nrutil.h                           |   109 +
 contrib/Netgen/COPYING.LIB                    |   504 +
 contrib/Netgen/Makefile                       |  4412 +++
 contrib/Netgen/README                         |    72 +
 contrib/Netgen/VERSION                        |     1 +
 contrib/Netgen/libsrc/Makefile                |    38 +
 contrib/Netgen/libsrc/csg/Makefile            |    26 +
 contrib/Netgen/libsrc/csg/algprim.cpp         |  1389 +
 contrib/Netgen/libsrc/csg/algprim.hpp         |   338 +
 contrib/Netgen/libsrc/csg/brick.cpp           |   409 +
 contrib/Netgen/libsrc/csg/brick.hpp           |    98 +
 contrib/Netgen/libsrc/csg/bspline2d.cpp       |   242 +
 contrib/Netgen/libsrc/csg/csg.hpp             |    48 +
 contrib/Netgen/libsrc/csg/csgeom.cpp          |  1020 +
 contrib/Netgen/libsrc/csg/csgeom.hpp          |   257 +
 contrib/Netgen/libsrc/csg/csgparser.cpp       |  1156 +
 .../Netgen/libsrc/csg/csgparser_dalibor.cpp   |  1111 +
 contrib/Netgen/libsrc/csg/csgscanner.cpp      |   205 +
 contrib/Netgen/libsrc/csg/curve2d.cpp         |    78 +
 contrib/Netgen/libsrc/csg/curve2d.hpp         |    59 +
 contrib/Netgen/libsrc/csg/edgeflw.cpp         |  1524 ++
 contrib/Netgen/libsrc/csg/edgeflw.hpp         |    99 +
 contrib/Netgen/libsrc/csg/edgeflw2.cpp        |  1404 +
 contrib/Netgen/libsrc/csg/edgeflw_new.cpp     |  1553 ++
 contrib/Netgen/libsrc/csg/edgeflw_old.cpp     |  1405 +
 contrib/Netgen/libsrc/csg/explicitcurve2d.cpp |   160 +
 contrib/Netgen/libsrc/csg/explicitcurve2d.hpp |   109 +
 contrib/Netgen/libsrc/csg/extrusion.cpp       |   175 +
 contrib/Netgen/libsrc/csg/extrusion.hpp       |    89 +
 contrib/Netgen/libsrc/csg/gencyl.cpp          |   209 +
 contrib/Netgen/libsrc/csg/gencyl.hpp          |    64 +
 contrib/Netgen/libsrc/csg/genmesh.cpp         |   684 +
 contrib/Netgen/libsrc/csg/geometry.cpp        |  1792 ++
 contrib/Netgen/libsrc/csg/geometry.h          |    48 +
 contrib/Netgen/libsrc/csg/geometry.ll         |    94 +
 contrib/Netgen/libsrc/csg/geometry.yy         |   585 +
 contrib/Netgen/libsrc/csg/geoml.hpp           |    16 +
 contrib/Netgen/libsrc/csg/identify.cpp        |  1508 +
 contrib/Netgen/libsrc/csg/identify.hpp        |   180 +
 contrib/Netgen/libsrc/csg/lex.yy.cpp          |  1834 ++
 contrib/Netgen/libsrc/csg/manifold.cpp        |    14 +
 contrib/Netgen/libsrc/csg/manifold.hpp        |    22 +
 contrib/Netgen/libsrc/csg/meshsurf.cpp        |   178 +
 contrib/Netgen/libsrc/csg/meshsurf.hpp        |    85 +
 contrib/Netgen/libsrc/csg/polyhedra.cpp       |   358 +
 contrib/Netgen/libsrc/csg/polyhedra.hpp       |    78 +
 contrib/Netgen/libsrc/csg/revolution.cpp      |   151 +
 contrib/Netgen/libsrc/csg/revolution.hpp      |    65 +
 contrib/Netgen/libsrc/csg/singularref.cpp     |   117 +
 contrib/Netgen/libsrc/csg/singularref.hpp     |    68 +
 contrib/Netgen/libsrc/csg/solid.cpp           |  1217 +
 contrib/Netgen/libsrc/csg/solid.hpp           |   195 +
 contrib/Netgen/libsrc/csg/specpoin.cpp        |  1231 +
 contrib/Netgen/libsrc/csg/specpoin.hpp        |   150 +
 contrib/Netgen/libsrc/csg/specpoin_new.cpp    |  1367 +
 contrib/Netgen/libsrc/csg/specpoin_old.cpp    |  1370 +
 contrib/Netgen/libsrc/csg/spline3d.cpp        |   355 +
 contrib/Netgen/libsrc/csg/spline3d.hpp        |    92 +
 contrib/Netgen/libsrc/csg/surface.cpp         |   392 +
 contrib/Netgen/libsrc/csg/surface.hpp         |   301 +
 contrib/Netgen/libsrc/csg/triapprox.cpp       |    59 +
 contrib/Netgen/libsrc/csg/triapprox.hpp       |    57 +
 contrib/Netgen/libsrc/general/Makefile        |    12 +
 contrib/Netgen/libsrc/general/array.cpp       |    75 +
 contrib/Netgen/libsrc/general/array.hpp       |   478 +
 contrib/Netgen/libsrc/general/autoptr.hpp     |    31 +
 contrib/Netgen/libsrc/general/bitarray.cpp    |   132 +
 contrib/Netgen/libsrc/general/bitarray.hpp    |   207 +
 contrib/Netgen/libsrc/general/dynamicmem.cpp  |   117 +
 contrib/Netgen/libsrc/general/dynamicmem.hpp  |    94 +
 contrib/Netgen/libsrc/general/flags.cpp       |   330 +
 contrib/Netgen/libsrc/general/flags.hpp       |    83 +
 contrib/Netgen/libsrc/general/hashtabl.cpp    |   294 +
 contrib/Netgen/libsrc/general/hashtabl.hpp    |  1000 +
 contrib/Netgen/libsrc/general/moveablemem.cpp |   249 +
 contrib/Netgen/libsrc/general/moveablemem.hpp |    97 +
 contrib/Netgen/libsrc/general/myadt.hpp       |    43 +
 contrib/Netgen/libsrc/general/mystring.cpp    |   386 +
 contrib/Netgen/libsrc/general/mystring.hpp    |   209 +
 contrib/Netgen/libsrc/general/ngexception.cpp |    33 +
 contrib/Netgen/libsrc/general/ngexception.hpp |    30 +
 contrib/Netgen/libsrc/general/optmem.cpp      |    63 +
 contrib/Netgen/libsrc/general/optmem.hpp      |    52 +
 contrib/Netgen/libsrc/general/parthreads.cpp  |    40 +
 contrib/Netgen/libsrc/general/parthreads.hpp  |   126 +
 contrib/Netgen/libsrc/general/seti.cpp        |    70 +
 contrib/Netgen/libsrc/general/seti.hpp        |    45 +
 contrib/Netgen/libsrc/general/sort.cpp        |    75 +
 contrib/Netgen/libsrc/general/sort.hpp        |    19 +
 contrib/Netgen/libsrc/general/spbita2d.cpp    |   172 +
 contrib/Netgen/libsrc/general/spbita2d.hpp    |    56 +
 contrib/Netgen/libsrc/general/stack.hpp       |   112 +
 contrib/Netgen/libsrc/general/stack.icc       |    67 +
 contrib/Netgen/libsrc/general/symbolta.cpp    |    52 +
 contrib/Netgen/libsrc/general/symbolta.hpp    |   158 +
 contrib/Netgen/libsrc/general/table.cpp       |   167 +
 contrib/Netgen/libsrc/general/table.hpp       |   212 +
 contrib/Netgen/libsrc/general/template.hpp    |   448 +
 contrib/Netgen/libsrc/geom2d/Makefile         |    12 +
 contrib/Netgen/libsrc/geom2d/genmesh2d.cpp    |   145 +
 contrib/Netgen/libsrc/geom2d/geom2dmesh.cpp   |    55 +
 contrib/Netgen/libsrc/geom2d/geom2dmesh.hpp   |    38 +
 contrib/Netgen/libsrc/geom2d/geometry2d.hpp   |    20 +
 contrib/Netgen/libsrc/geom2d/spline2d.cpp     |   395 +
 contrib/Netgen/libsrc/geom2d/spline2d.hpp     |   204 +
 .../Netgen/libsrc/geom2d/splinegeometry2.cpp  |   346 +
 .../Netgen/libsrc/geom2d/splinegeometry2.hpp  |    83 +
 contrib/Netgen/libsrc/gprim/Makefile          |    14 +
 contrib/Netgen/libsrc/gprim/adtree.cpp        |  2245 ++
 contrib/Netgen/libsrc/gprim/adtree.hpp        |   477 +
 contrib/Netgen/libsrc/gprim/geom2d.cpp        |   485 +
 contrib/Netgen/libsrc/gprim/geom2d.hpp        |   870 +
 contrib/Netgen/libsrc/gprim/geom3d.cpp        |   650 +
 contrib/Netgen/libsrc/gprim/geom3d.hpp        |   732 +
 contrib/Netgen/libsrc/gprim/geomfuncs.cpp     |   111 +
 contrib/Netgen/libsrc/gprim/geomfuncs.hpp     |   136 +
 contrib/Netgen/libsrc/gprim/geomobjects.hpp   |   346 +
 contrib/Netgen/libsrc/gprim/geomobjects2.hpp  |   366 +
 contrib/Netgen/libsrc/gprim/geomops.hpp       |   391 +
 contrib/Netgen/libsrc/gprim/geomops2.hpp      |   428 +
 contrib/Netgen/libsrc/gprim/geomtest3d.cpp    |  1223 +
 contrib/Netgen/libsrc/gprim/geomtest3d.hpp    |    80 +
 contrib/Netgen/libsrc/gprim/gprim.hpp         |    26 +
 contrib/Netgen/libsrc/gprim/testgeom.cpp      |    20 +
 contrib/Netgen/libsrc/gprim/transform3d.cpp   |   173 +
 contrib/Netgen/libsrc/gprim/transform3d.hpp   |   174 +
 contrib/Netgen/libsrc/include/FlexLexer.h     |   184 +
 contrib/Netgen/libsrc/include/csg.hpp         |     1 +
 contrib/Netgen/libsrc/include/geometry2d.hpp  |     1 +
 contrib/Netgen/libsrc/include/gprim.hpp       |     1 +
 contrib/Netgen/libsrc/include/incvis.hpp      |    33 +
 contrib/Netgen/libsrc/include/linalg.hpp      |     1 +
 contrib/Netgen/libsrc/include/meshing.hpp     |     1 +
 contrib/Netgen/libsrc/include/myadt.hpp       |     1 +
 contrib/Netgen/libsrc/include/mydefs.hpp      |    29 +
 contrib/Netgen/libsrc/include/mystdlib.h      |    69 +
 contrib/Netgen/libsrc/include/occgeom.hpp     |     1 +
 contrib/Netgen/libsrc/include/opti.hpp        |     1 +
 contrib/Netgen/libsrc/include/stepgeom.hpp    |    10 +
 contrib/Netgen/libsrc/include/stepreader.hpp  |     1 +
 contrib/Netgen/libsrc/include/stlgeom.hpp     |     1 +
 contrib/Netgen/libsrc/include/visual.hpp      |     1 +
 contrib/Netgen/libsrc/interface/Makefile      |     7 +
 .../libsrc/interface/importsolution.cpp       |   121 +
 .../Netgen/libsrc/interface/nginterface.cpp   |  1476 +
 contrib/Netgen/libsrc/interface/nginterface.h |   245 +
 contrib/Netgen/libsrc/interface/nglib.cpp     |   573 +
 contrib/Netgen/libsrc/interface/nglib.h       |   208 +
 contrib/Netgen/libsrc/interface/printdest.cpp |    11 +
 contrib/Netgen/libsrc/interface/readuser.cpp  |   394 +
 .../Netgen/libsrc/interface/writeabaqus.cpp   |   237 +
 .../Netgen/libsrc/interface/writediffpack.cpp |   296 +
 .../Netgen/libsrc/interface/writeelmer.cpp    |   127 +
 contrib/Netgen/libsrc/interface/writefeap.cpp |   220 +
 .../Netgen/libsrc/interface/writefluent.cpp   |   193 +
 contrib/Netgen/libsrc/interface/writegmsh.cpp |   200 +
 .../Netgen/libsrc/interface/writepermas.cpp   |   208 +
 .../Netgen/libsrc/interface/writepermas2.cpp  |   173 +
 .../Netgen/libsrc/interface/writetecplot.cpp  |   127 +
 .../Netgen/libsrc/interface/writetochnog.cpp  |   108 +
 contrib/Netgen/libsrc/interface/writeuser.cpp |   899 +
 contrib/Netgen/libsrc/interface/writeuser.hpp |   114 +
 .../Netgen/libsrc/interface/wuchemnitz.cpp    |   309 +
 contrib/Netgen/libsrc/linalg/Makefile         |    13 +
 contrib/Netgen/libsrc/linalg/basemat.cpp      |   472 +
 contrib/Netgen/libsrc/linalg/basemat.hpp      |   109 +
 contrib/Netgen/libsrc/linalg/densemat.cpp     |  1443 +
 contrib/Netgen/libsrc/linalg/densemat.hpp     |   260 +
 contrib/Netgen/libsrc/linalg/linalg.hpp       |    35 +
 contrib/Netgen/libsrc/linalg/polynomial.cpp   |   216 +
 contrib/Netgen/libsrc/linalg/polynomial.hpp   |    45 +
 contrib/Netgen/libsrc/linalg/sparsmat.cpp     |  1707 ++
 contrib/Netgen/libsrc/linalg/sparsmat.hpp     |   265 +
 contrib/Netgen/libsrc/linalg/vector.cpp       |   787 +
 contrib/Netgen/libsrc/linalg/vector.hpp       |   485 +
 contrib/Netgen/libsrc/makefile.inc            |    48 +
 contrib/Netgen/libsrc/makefile.mach.FREEBSD   |    26 +
 contrib/Netgen/libsrc/makefile.mach.INTEL     |    34 +
 contrib/Netgen/libsrc/makefile.mach.LINUX     |    31 +
 .../Netgen/libsrc/makefile.mach.LINUXGCC33    |    33 +
 contrib/Netgen/libsrc/makefile.mach.SGI       |    19 +
 contrib/Netgen/libsrc/makefile.mach.SGIGCC    |    53 +
 contrib/Netgen/libsrc/makefile.mach.SUN       |    16 +
 contrib/Netgen/libsrc/meshing/Makefile        |    16 +
 contrib/Netgen/libsrc/meshing/adfront2.cpp    |   526 +
 contrib/Netgen/libsrc/meshing/adfront2.hpp    |   337 +
 contrib/Netgen/libsrc/meshing/adfront3.cpp    |   883 +
 contrib/Netgen/libsrc/meshing/adfront3.hpp    |   276 +
 contrib/Netgen/libsrc/meshing/bisect.cpp      |  2371 ++
 contrib/Netgen/libsrc/meshing/bisect.hpp      |    77 +
 .../Netgen/libsrc/meshing/boundarylayer.cpp   |    91 +
 .../Netgen/libsrc/meshing/boundarylayer.hpp   |     9 +
 contrib/Netgen/libsrc/meshing/clusters.cpp    |   260 +
 contrib/Netgen/libsrc/meshing/clusters.hpp    |    42 +
 contrib/Netgen/libsrc/meshing/curvedelems.cpp |  2037 ++
 contrib/Netgen/libsrc/meshing/curvedelems.hpp |   837 +
 .../Netgen/libsrc/meshing/curvedelems2.cpp    |   752 +
 contrib/Netgen/libsrc/meshing/delaunay.cpp    |  1683 ++
 contrib/Netgen/libsrc/meshing/findip.cpp      |   115 +
 contrib/Netgen/libsrc/meshing/findip.hpp      |   108 +
 contrib/Netgen/libsrc/meshing/geomsearch.cpp  |   263 +
 contrib/Netgen/libsrc/meshing/geomsearch.hpp  |   116 +
 contrib/Netgen/libsrc/meshing/global.cpp      |    53 +
 contrib/Netgen/libsrc/meshing/global.hpp      |    54 +
 contrib/Netgen/libsrc/meshing/hpref_prism.hpp |   300 +
 contrib/Netgen/libsrc/meshing/hpref_quad.hpp  |  2129 ++
 contrib/Netgen/libsrc/meshing/hpref_tet.hpp   |  2842 ++
 contrib/Netgen/libsrc/meshing/hpref_trig.hpp  |   750 +
 .../Netgen/libsrc/meshing/hprefinement.cpp    |  2605 ++
 .../Netgen/libsrc/meshing/hprefinement.hpp    |   227 +
 contrib/Netgen/libsrc/meshing/improve2.cpp    |   798 +
 contrib/Netgen/libsrc/meshing/improve2.hpp    |    91 +
 contrib/Netgen/libsrc/meshing/improve2gen.cpp |   441 +
 contrib/Netgen/libsrc/meshing/improve3.cpp    |  1947 ++
 contrib/Netgen/libsrc/meshing/improve3.hpp    |    50 +
 contrib/Netgen/libsrc/meshing/localh.cpp      |   682 +
 contrib/Netgen/libsrc/meshing/localh.hpp      |   145 +
 contrib/Netgen/libsrc/meshing/meshclass.cpp   |  4744 ++++
 contrib/Netgen/libsrc/meshing/meshclass.hpp   |   620 +
 contrib/Netgen/libsrc/meshing/meshfunc.cpp    |   688 +
 contrib/Netgen/libsrc/meshing/meshfunc.hpp    |    41 +
 contrib/Netgen/libsrc/meshing/meshfunc2d.cpp  |    61 +
 contrib/Netgen/libsrc/meshing/meshing.hpp     |    60 +
 contrib/Netgen/libsrc/meshing/meshing2.cpp    |  1864 ++
 contrib/Netgen/libsrc/meshing/meshing2.hpp    |   149 +
 contrib/Netgen/libsrc/meshing/meshing3.cpp    |  1260 +
 contrib/Netgen/libsrc/meshing/meshing3.hpp    |   128 +
 contrib/Netgen/libsrc/meshing/meshtool.cpp    |   978 +
 contrib/Netgen/libsrc/meshing/meshtool.hpp    |    83 +
 contrib/Netgen/libsrc/meshing/meshtype.cpp    |  2233 ++
 contrib/Netgen/libsrc/meshing/meshtype.hpp    |  1025 +
 contrib/Netgen/libsrc/meshing/msghandler.cpp  |   193 +
 contrib/Netgen/libsrc/meshing/msghandler.hpp  |    52 +
 contrib/Netgen/libsrc/meshing/netrule2.cpp    |   226 +
 contrib/Netgen/libsrc/meshing/netrule3.cpp    |  1138 +
 contrib/Netgen/libsrc/meshing/parser2.cpp     |   559 +
 contrib/Netgen/libsrc/meshing/parser3.cpp     |   987 +
 contrib/Netgen/libsrc/meshing/prism2rls.cpp   |   457 +
 contrib/Netgen/libsrc/meshing/prism2rls_2.cpp |   446 +
 contrib/Netgen/libsrc/meshing/pyramid2rls.cpp |   309 +
 contrib/Netgen/libsrc/meshing/pyramidrls.cpp  |   263 +
 contrib/Netgen/libsrc/meshing/quadrls.cpp     |   765 +
 contrib/Netgen/libsrc/meshing/refine.cpp      |   721 +
 contrib/Netgen/libsrc/meshing/ruler2.cpp      |   646 +
 contrib/Netgen/libsrc/meshing/ruler2.hpp      |   166 +
 contrib/Netgen/libsrc/meshing/ruler3.cpp      |  1176 +
 contrib/Netgen/libsrc/meshing/ruler3.hpp      |   210 +
 contrib/Netgen/libsrc/meshing/secondorder.cpp |   496 +
 contrib/Netgen/libsrc/meshing/smoothing2.cpp  |   922 +
 contrib/Netgen/libsrc/meshing/smoothing3.cpp  |  1546 ++
 contrib/Netgen/libsrc/meshing/specials.cpp    |   193 +
 contrib/Netgen/libsrc/meshing/specials.hpp    |    16 +
 contrib/Netgen/libsrc/meshing/tetrarls.cpp    |  1466 +
 contrib/Netgen/libsrc/meshing/topology.cpp    |  1595 ++
 contrib/Netgen/libsrc/meshing/topology.hpp    |   113 +
 contrib/Netgen/libsrc/meshing/triarls.cpp     |   468 +
 contrib/Netgen/libsrc/meshing/zrefine.cpp     |   735 +
 contrib/Netgen/libsrc/occ/Makefile            |    11 +
 contrib/Netgen/libsrc/occ/occgenmesh.cpp      |  1245 +
 contrib/Netgen/libsrc/occ/occgeom.cpp         |  1102 +
 contrib/Netgen/libsrc/occ/occgeom.hpp         |   279 +
 contrib/Netgen/libsrc/occ/occmeshsurf.cpp     |   592 +
 contrib/Netgen/libsrc/occ/occmeshsurf.hpp     |   201 +
 contrib/Netgen/libsrc/opti/Makefile           |    10 +
 contrib/Netgen/libsrc/opti/bfgs.cpp           |   367 +
 contrib/Netgen/libsrc/opti/linopt.cpp         |    73 +
 contrib/Netgen/libsrc/opti/linsearch.cpp      |   346 +
 contrib/Netgen/libsrc/opti/opti.hpp           |   142 +
 contrib/Netgen/libsrc/stlgeom/Makefile        |    11 +
 .../Netgen/libsrc/stlgeom/meshstlsurface.cpp  |  1130 +
 .../Netgen/libsrc/stlgeom/meshstlsurface.hpp  |   121 +
 contrib/Netgen/libsrc/stlgeom/stlgeom.cpp     |  3476 +++
 contrib/Netgen/libsrc/stlgeom/stlgeom.hpp     |   450 +
 .../Netgen/libsrc/stlgeom/stlgeomchart.cpp    |   801 +
 contrib/Netgen/libsrc/stlgeom/stlgeommesh.cpp |  1592 ++
 contrib/Netgen/libsrc/stlgeom/stlline.cpp     |   780 +
 contrib/Netgen/libsrc/stlgeom/stlline.hpp     |   188 +
 contrib/Netgen/libsrc/stlgeom/stltool.cpp     |  1288 +
 contrib/Netgen/libsrc/stlgeom/stltool.hpp     |   271 +
 contrib/Netgen/libsrc/stlgeom/stltopology.cpp |  1067 +
 contrib/Netgen/libsrc/stlgeom/stltopology.hpp |   362 +
 contrib/Netgen/libsrc/visualization/Makefile  |    13 +
 .../Netgen/libsrc/visualization/meshdoc.cpp   |   615 +
 .../Netgen/libsrc/visualization/meshdoc.hpp   |    37 +
 .../Netgen/libsrc/visualization/mvdraw.cpp    |  1335 +
 .../Netgen/libsrc/visualization/mvdraw.hpp    |   370 +
 .../Netgen/libsrc/visualization/soldata.hpp   |    45 +
 .../libsrc/visualization/stlmeshing.cpp       |  1074 +
 .../Netgen/libsrc/visualization/vispar.hpp    |    89 +
 .../Netgen/libsrc/visualization/visual.hpp    |    26 +
 contrib/Netgen/libsrc/visualization/vscsg.cpp |   199 +
 .../Netgen/libsrc/visualization/vsmesh.cpp    |  3114 +++
 contrib/Netgen/libsrc/visualization/vsocc.cpp |   743 +
 .../libsrc/visualization/vssolution.cpp       |  3005 ++
 .../libsrc/visualization/vssolution.hpp       |   220 +
 contrib/Netgen/nglib_addon.cpp                |   105 +
 contrib/Netgen/nglib_addon.h                  |     8 +
 contrib/Netgen/nglib_addon.o                  |   Bin 0 -> 291636 bytes
 contrib/Tetgen/LICENSE                        |    65 +
 contrib/Tetgen/Makefile                       |    53 +
 contrib/Tetgen/predicates.cxx                 |  4176 +++
 contrib/Tetgen/tetgen.cxx                     | 22807 ++++++++++++++++
 contrib/Tetgen/tetgen.h                       |  1764 ++
 contrib/Triangle/Makefile                     |    55 +
 contrib/Triangle/README                       |    74 +
 contrib/Triangle/triangle.c                   | 16008 +++++++++++
 contrib/Triangle/triangle.h                   |   282 +
 433 files changed, 221397 insertions(+), 157 deletions(-)
 create mode 100644 contrib/ANN/Copyright.txt
 create mode 100644 contrib/ANN/License.txt
 create mode 100644 contrib/ANN/Makefile
 create mode 100644 contrib/ANN/include/ANN/ANN.h
 create mode 100644 contrib/ANN/include/ANN/ANNperf.h
 create mode 100644 contrib/ANN/include/ANN/ANNx.h
 create mode 100644 contrib/ANN/src/ANN.cpp
 create mode 100644 contrib/ANN/src/bd_fix_rad_search.cpp
 create mode 100644 contrib/ANN/src/bd_pr_search.cpp
 create mode 100644 contrib/ANN/src/bd_search.cpp
 create mode 100644 contrib/ANN/src/bd_tree.cpp
 create mode 100644 contrib/ANN/src/bd_tree.h
 create mode 100644 contrib/ANN/src/brute.cpp
 create mode 100644 contrib/ANN/src/kd_dump.cpp
 create mode 100644 contrib/ANN/src/kd_fix_rad_search.cpp
 create mode 100644 contrib/ANN/src/kd_fix_rad_search.h
 create mode 100644 contrib/ANN/src/kd_pr_search.cpp
 create mode 100644 contrib/ANN/src/kd_pr_search.h
 create mode 100644 contrib/ANN/src/kd_search.cpp
 create mode 100644 contrib/ANN/src/kd_search.h
 create mode 100644 contrib/ANN/src/kd_split.cpp
 create mode 100644 contrib/ANN/src/kd_split.h
 create mode 100644 contrib/ANN/src/kd_tree.cpp
 create mode 100644 contrib/ANN/src/kd_tree.h
 create mode 100644 contrib/ANN/src/kd_util.cpp
 create mode 100644 contrib/ANN/src/kd_util.h
 create mode 100644 contrib/ANN/src/perf.cpp
 create mode 100644 contrib/ANN/src/pr_queue.h
 create mode 100644 contrib/ANN/src/pr_queue_k.h
 create mode 100644 contrib/MathEval/Makefile
 create mode 100644 contrib/MathEval/README
 create mode 100644 contrib/MathEval/common.h
 create mode 100644 contrib/MathEval/matheval.cpp
 create mode 100644 contrib/MathEval/matheval.h
 create mode 100644 contrib/MathEval/node.cpp
 create mode 100644 contrib/MathEval/node.h
 create mode 100644 contrib/MathEval/parser.tab.cpp
 create mode 100644 contrib/MathEval/parser.tab.hpp
 create mode 100644 contrib/MathEval/parser.y
 create mode 100644 contrib/MathEval/scanner.l
 create mode 100644 contrib/MathEval/scanner.yy.cpp
 create mode 100644 contrib/MathEval/symbol_table.cpp
 create mode 100644 contrib/MathEval/symbol_table.h
 create mode 100644 contrib/MathEval/xmath.cpp
 create mode 100644 contrib/MathEval/xmath.h
 create mode 100644 contrib/Metis/Makefile
 create mode 100644 contrib/Metis/balance.c
 create mode 100644 contrib/Metis/bucketsort.c
 create mode 100644 contrib/Metis/ccgraph.c
 create mode 100644 contrib/Metis/coarsen.c
 create mode 100644 contrib/Metis/compress.c
 create mode 100644 contrib/Metis/debug.c
 create mode 100644 contrib/Metis/defs.h
 create mode 100644 contrib/Metis/estmem.c
 create mode 100644 contrib/Metis/fm.c
 create mode 100644 contrib/Metis/fortran.c
 create mode 100644 contrib/Metis/frename.c
 create mode 100644 contrib/Metis/graph.c
 create mode 100644 contrib/Metis/initpart.c
 create mode 100644 contrib/Metis/kmetis.c
 create mode 100644 contrib/Metis/kvmetis.c
 create mode 100644 contrib/Metis/kwayfm.c
 create mode 100644 contrib/Metis/kwayrefine.c
 create mode 100644 contrib/Metis/kwayvolfm.c
 create mode 100644 contrib/Metis/kwayvolrefine.c
 create mode 100644 contrib/Metis/macros.h
 create mode 100644 contrib/Metis/match.c
 create mode 100644 contrib/Metis/mbalance.c
 create mode 100644 contrib/Metis/mbalance2.c
 create mode 100644 contrib/Metis/mcoarsen.c
 create mode 100644 contrib/Metis/memory.c
 create mode 100644 contrib/Metis/mesh.c
 create mode 100644 contrib/Metis/meshpart.c
 create mode 100644 contrib/Metis/metis.h
 create mode 100644 contrib/Metis/mfm.c
 create mode 100644 contrib/Metis/mfm2.c
 create mode 100644 contrib/Metis/mincover.c
 create mode 100644 contrib/Metis/minitpart.c
 create mode 100644 contrib/Metis/minitpart2.c
 create mode 100644 contrib/Metis/mkmetis.c
 create mode 100644 contrib/Metis/mkwayfmh.c
 create mode 100644 contrib/Metis/mkwayrefine.c
 create mode 100644 contrib/Metis/mmatch.c
 create mode 100644 contrib/Metis/mmd.c
 create mode 100644 contrib/Metis/mpmetis.c
 create mode 100644 contrib/Metis/mrefine.c
 create mode 100644 contrib/Metis/mrefine2.c
 create mode 100644 contrib/Metis/mutil.c
 create mode 100644 contrib/Metis/myqsort.c
 create mode 100644 contrib/Metis/ometis.c
 create mode 100644 contrib/Metis/parmetis.c
 create mode 100644 contrib/Metis/pmetis.c
 create mode 100644 contrib/Metis/pqueue.c
 create mode 100644 contrib/Metis/proto.h
 create mode 100644 contrib/Metis/refine.c
 create mode 100644 contrib/Metis/rename.h
 create mode 100644 contrib/Metis/separator.c
 create mode 100644 contrib/Metis/sfm.c
 create mode 100644 contrib/Metis/srefine.c
 create mode 100644 contrib/Metis/stat.c
 create mode 100644 contrib/Metis/struct.h
 create mode 100644 contrib/Metis/subdomains.c
 create mode 100644 contrib/Metis/timing.c
 create mode 100644 contrib/Metis/util.c
 create mode 100644 contrib/NR/Makefile
 create mode 100644 contrib/NR/brent.cpp
 create mode 100644 contrib/NR/dpythag.cpp
 create mode 100644 contrib/NR/dsvdcmp.cpp
 create mode 100644 contrib/NR/fdjac.cpp
 create mode 100644 contrib/NR/fmin.cpp
 create mode 100644 contrib/NR/lnsrch.cpp
 create mode 100644 contrib/NR/lubksb.cpp
 create mode 100644 contrib/NR/ludcmp.cpp
 create mode 100644 contrib/NR/mnbrak.cpp
 create mode 100644 contrib/NR/newt.cpp
 create mode 100644 contrib/NR/nrutil.cpp
 create mode 100644 contrib/NR/nrutil.h
 create mode 100644 contrib/Netgen/COPYING.LIB
 create mode 100644 contrib/Netgen/Makefile
 create mode 100644 contrib/Netgen/README
 create mode 100644 contrib/Netgen/VERSION
 create mode 100644 contrib/Netgen/libsrc/Makefile
 create mode 100644 contrib/Netgen/libsrc/csg/Makefile
 create mode 100644 contrib/Netgen/libsrc/csg/algprim.cpp
 create mode 100644 contrib/Netgen/libsrc/csg/algprim.hpp
 create mode 100644 contrib/Netgen/libsrc/csg/brick.cpp
 create mode 100644 contrib/Netgen/libsrc/csg/brick.hpp
 create mode 100644 contrib/Netgen/libsrc/csg/bspline2d.cpp
 create mode 100644 contrib/Netgen/libsrc/csg/csg.hpp
 create mode 100644 contrib/Netgen/libsrc/csg/csgeom.cpp
 create mode 100644 contrib/Netgen/libsrc/csg/csgeom.hpp
 create mode 100644 contrib/Netgen/libsrc/csg/csgparser.cpp
 create mode 100644 contrib/Netgen/libsrc/csg/csgparser_dalibor.cpp
 create mode 100644 contrib/Netgen/libsrc/csg/csgscanner.cpp
 create mode 100644 contrib/Netgen/libsrc/csg/curve2d.cpp
 create mode 100644 contrib/Netgen/libsrc/csg/curve2d.hpp
 create mode 100644 contrib/Netgen/libsrc/csg/edgeflw.cpp
 create mode 100644 contrib/Netgen/libsrc/csg/edgeflw.hpp
 create mode 100644 contrib/Netgen/libsrc/csg/edgeflw2.cpp
 create mode 100644 contrib/Netgen/libsrc/csg/edgeflw_new.cpp
 create mode 100644 contrib/Netgen/libsrc/csg/edgeflw_old.cpp
 create mode 100644 contrib/Netgen/libsrc/csg/explicitcurve2d.cpp
 create mode 100644 contrib/Netgen/libsrc/csg/explicitcurve2d.hpp
 create mode 100644 contrib/Netgen/libsrc/csg/extrusion.cpp
 create mode 100644 contrib/Netgen/libsrc/csg/extrusion.hpp
 create mode 100644 contrib/Netgen/libsrc/csg/gencyl.cpp
 create mode 100644 contrib/Netgen/libsrc/csg/gencyl.hpp
 create mode 100644 contrib/Netgen/libsrc/csg/genmesh.cpp
 create mode 100644 contrib/Netgen/libsrc/csg/geometry.cpp
 create mode 100644 contrib/Netgen/libsrc/csg/geometry.h
 create mode 100644 contrib/Netgen/libsrc/csg/geometry.ll
 create mode 100644 contrib/Netgen/libsrc/csg/geometry.yy
 create mode 100644 contrib/Netgen/libsrc/csg/geoml.hpp
 create mode 100644 contrib/Netgen/libsrc/csg/identify.cpp
 create mode 100644 contrib/Netgen/libsrc/csg/identify.hpp
 create mode 100644 contrib/Netgen/libsrc/csg/lex.yy.cpp
 create mode 100644 contrib/Netgen/libsrc/csg/manifold.cpp
 create mode 100644 contrib/Netgen/libsrc/csg/manifold.hpp
 create mode 100644 contrib/Netgen/libsrc/csg/meshsurf.cpp
 create mode 100644 contrib/Netgen/libsrc/csg/meshsurf.hpp
 create mode 100644 contrib/Netgen/libsrc/csg/polyhedra.cpp
 create mode 100644 contrib/Netgen/libsrc/csg/polyhedra.hpp
 create mode 100644 contrib/Netgen/libsrc/csg/revolution.cpp
 create mode 100644 contrib/Netgen/libsrc/csg/revolution.hpp
 create mode 100644 contrib/Netgen/libsrc/csg/singularref.cpp
 create mode 100644 contrib/Netgen/libsrc/csg/singularref.hpp
 create mode 100644 contrib/Netgen/libsrc/csg/solid.cpp
 create mode 100644 contrib/Netgen/libsrc/csg/solid.hpp
 create mode 100644 contrib/Netgen/libsrc/csg/specpoin.cpp
 create mode 100644 contrib/Netgen/libsrc/csg/specpoin.hpp
 create mode 100644 contrib/Netgen/libsrc/csg/specpoin_new.cpp
 create mode 100644 contrib/Netgen/libsrc/csg/specpoin_old.cpp
 create mode 100644 contrib/Netgen/libsrc/csg/spline3d.cpp
 create mode 100644 contrib/Netgen/libsrc/csg/spline3d.hpp
 create mode 100644 contrib/Netgen/libsrc/csg/surface.cpp
 create mode 100644 contrib/Netgen/libsrc/csg/surface.hpp
 create mode 100644 contrib/Netgen/libsrc/csg/triapprox.cpp
 create mode 100644 contrib/Netgen/libsrc/csg/triapprox.hpp
 create mode 100644 contrib/Netgen/libsrc/general/Makefile
 create mode 100644 contrib/Netgen/libsrc/general/array.cpp
 create mode 100644 contrib/Netgen/libsrc/general/array.hpp
 create mode 100644 contrib/Netgen/libsrc/general/autoptr.hpp
 create mode 100644 contrib/Netgen/libsrc/general/bitarray.cpp
 create mode 100644 contrib/Netgen/libsrc/general/bitarray.hpp
 create mode 100644 contrib/Netgen/libsrc/general/dynamicmem.cpp
 create mode 100644 contrib/Netgen/libsrc/general/dynamicmem.hpp
 create mode 100644 contrib/Netgen/libsrc/general/flags.cpp
 create mode 100644 contrib/Netgen/libsrc/general/flags.hpp
 create mode 100644 contrib/Netgen/libsrc/general/hashtabl.cpp
 create mode 100644 contrib/Netgen/libsrc/general/hashtabl.hpp
 create mode 100644 contrib/Netgen/libsrc/general/moveablemem.cpp
 create mode 100644 contrib/Netgen/libsrc/general/moveablemem.hpp
 create mode 100644 contrib/Netgen/libsrc/general/myadt.hpp
 create mode 100644 contrib/Netgen/libsrc/general/mystring.cpp
 create mode 100644 contrib/Netgen/libsrc/general/mystring.hpp
 create mode 100644 contrib/Netgen/libsrc/general/ngexception.cpp
 create mode 100644 contrib/Netgen/libsrc/general/ngexception.hpp
 create mode 100644 contrib/Netgen/libsrc/general/optmem.cpp
 create mode 100644 contrib/Netgen/libsrc/general/optmem.hpp
 create mode 100644 contrib/Netgen/libsrc/general/parthreads.cpp
 create mode 100644 contrib/Netgen/libsrc/general/parthreads.hpp
 create mode 100644 contrib/Netgen/libsrc/general/seti.cpp
 create mode 100644 contrib/Netgen/libsrc/general/seti.hpp
 create mode 100644 contrib/Netgen/libsrc/general/sort.cpp
 create mode 100644 contrib/Netgen/libsrc/general/sort.hpp
 create mode 100644 contrib/Netgen/libsrc/general/spbita2d.cpp
 create mode 100644 contrib/Netgen/libsrc/general/spbita2d.hpp
 create mode 100644 contrib/Netgen/libsrc/general/stack.hpp
 create mode 100644 contrib/Netgen/libsrc/general/stack.icc
 create mode 100644 contrib/Netgen/libsrc/general/symbolta.cpp
 create mode 100644 contrib/Netgen/libsrc/general/symbolta.hpp
 create mode 100644 contrib/Netgen/libsrc/general/table.cpp
 create mode 100644 contrib/Netgen/libsrc/general/table.hpp
 create mode 100644 contrib/Netgen/libsrc/general/template.hpp
 create mode 100644 contrib/Netgen/libsrc/geom2d/Makefile
 create mode 100644 contrib/Netgen/libsrc/geom2d/genmesh2d.cpp
 create mode 100644 contrib/Netgen/libsrc/geom2d/geom2dmesh.cpp
 create mode 100644 contrib/Netgen/libsrc/geom2d/geom2dmesh.hpp
 create mode 100644 contrib/Netgen/libsrc/geom2d/geometry2d.hpp
 create mode 100644 contrib/Netgen/libsrc/geom2d/spline2d.cpp
 create mode 100644 contrib/Netgen/libsrc/geom2d/spline2d.hpp
 create mode 100644 contrib/Netgen/libsrc/geom2d/splinegeometry2.cpp
 create mode 100644 contrib/Netgen/libsrc/geom2d/splinegeometry2.hpp
 create mode 100644 contrib/Netgen/libsrc/gprim/Makefile
 create mode 100644 contrib/Netgen/libsrc/gprim/adtree.cpp
 create mode 100644 contrib/Netgen/libsrc/gprim/adtree.hpp
 create mode 100644 contrib/Netgen/libsrc/gprim/geom2d.cpp
 create mode 100644 contrib/Netgen/libsrc/gprim/geom2d.hpp
 create mode 100644 contrib/Netgen/libsrc/gprim/geom3d.cpp
 create mode 100644 contrib/Netgen/libsrc/gprim/geom3d.hpp
 create mode 100644 contrib/Netgen/libsrc/gprim/geomfuncs.cpp
 create mode 100644 contrib/Netgen/libsrc/gprim/geomfuncs.hpp
 create mode 100644 contrib/Netgen/libsrc/gprim/geomobjects.hpp
 create mode 100644 contrib/Netgen/libsrc/gprim/geomobjects2.hpp
 create mode 100644 contrib/Netgen/libsrc/gprim/geomops.hpp
 create mode 100644 contrib/Netgen/libsrc/gprim/geomops2.hpp
 create mode 100644 contrib/Netgen/libsrc/gprim/geomtest3d.cpp
 create mode 100644 contrib/Netgen/libsrc/gprim/geomtest3d.hpp
 create mode 100644 contrib/Netgen/libsrc/gprim/gprim.hpp
 create mode 100644 contrib/Netgen/libsrc/gprim/testgeom.cpp
 create mode 100644 contrib/Netgen/libsrc/gprim/transform3d.cpp
 create mode 100644 contrib/Netgen/libsrc/gprim/transform3d.hpp
 create mode 100644 contrib/Netgen/libsrc/include/FlexLexer.h
 create mode 100644 contrib/Netgen/libsrc/include/csg.hpp
 create mode 100644 contrib/Netgen/libsrc/include/geometry2d.hpp
 create mode 100644 contrib/Netgen/libsrc/include/gprim.hpp
 create mode 100644 contrib/Netgen/libsrc/include/incvis.hpp
 create mode 100644 contrib/Netgen/libsrc/include/linalg.hpp
 create mode 100644 contrib/Netgen/libsrc/include/meshing.hpp
 create mode 100644 contrib/Netgen/libsrc/include/myadt.hpp
 create mode 100644 contrib/Netgen/libsrc/include/mydefs.hpp
 create mode 100644 contrib/Netgen/libsrc/include/mystdlib.h
 create mode 100644 contrib/Netgen/libsrc/include/occgeom.hpp
 create mode 100644 contrib/Netgen/libsrc/include/opti.hpp
 create mode 100644 contrib/Netgen/libsrc/include/stepgeom.hpp
 create mode 100644 contrib/Netgen/libsrc/include/stepreader.hpp
 create mode 100644 contrib/Netgen/libsrc/include/stlgeom.hpp
 create mode 100644 contrib/Netgen/libsrc/include/visual.hpp
 create mode 100644 contrib/Netgen/libsrc/interface/Makefile
 create mode 100644 contrib/Netgen/libsrc/interface/importsolution.cpp
 create mode 100644 contrib/Netgen/libsrc/interface/nginterface.cpp
 create mode 100644 contrib/Netgen/libsrc/interface/nginterface.h
 create mode 100644 contrib/Netgen/libsrc/interface/nglib.cpp
 create mode 100644 contrib/Netgen/libsrc/interface/nglib.h
 create mode 100644 contrib/Netgen/libsrc/interface/printdest.cpp
 create mode 100644 contrib/Netgen/libsrc/interface/readuser.cpp
 create mode 100644 contrib/Netgen/libsrc/interface/writeabaqus.cpp
 create mode 100644 contrib/Netgen/libsrc/interface/writediffpack.cpp
 create mode 100644 contrib/Netgen/libsrc/interface/writeelmer.cpp
 create mode 100644 contrib/Netgen/libsrc/interface/writefeap.cpp
 create mode 100644 contrib/Netgen/libsrc/interface/writefluent.cpp
 create mode 100644 contrib/Netgen/libsrc/interface/writegmsh.cpp
 create mode 100644 contrib/Netgen/libsrc/interface/writepermas.cpp
 create mode 100644 contrib/Netgen/libsrc/interface/writepermas2.cpp
 create mode 100644 contrib/Netgen/libsrc/interface/writetecplot.cpp
 create mode 100644 contrib/Netgen/libsrc/interface/writetochnog.cpp
 create mode 100644 contrib/Netgen/libsrc/interface/writeuser.cpp
 create mode 100644 contrib/Netgen/libsrc/interface/writeuser.hpp
 create mode 100644 contrib/Netgen/libsrc/interface/wuchemnitz.cpp
 create mode 100644 contrib/Netgen/libsrc/linalg/Makefile
 create mode 100644 contrib/Netgen/libsrc/linalg/basemat.cpp
 create mode 100644 contrib/Netgen/libsrc/linalg/basemat.hpp
 create mode 100644 contrib/Netgen/libsrc/linalg/densemat.cpp
 create mode 100644 contrib/Netgen/libsrc/linalg/densemat.hpp
 create mode 100644 contrib/Netgen/libsrc/linalg/linalg.hpp
 create mode 100644 contrib/Netgen/libsrc/linalg/polynomial.cpp
 create mode 100644 contrib/Netgen/libsrc/linalg/polynomial.hpp
 create mode 100644 contrib/Netgen/libsrc/linalg/sparsmat.cpp
 create mode 100644 contrib/Netgen/libsrc/linalg/sparsmat.hpp
 create mode 100644 contrib/Netgen/libsrc/linalg/vector.cpp
 create mode 100644 contrib/Netgen/libsrc/linalg/vector.hpp
 create mode 100644 contrib/Netgen/libsrc/makefile.inc
 create mode 100644 contrib/Netgen/libsrc/makefile.mach.FREEBSD
 create mode 100644 contrib/Netgen/libsrc/makefile.mach.INTEL
 create mode 100644 contrib/Netgen/libsrc/makefile.mach.LINUX
 create mode 100644 contrib/Netgen/libsrc/makefile.mach.LINUXGCC33
 create mode 100644 contrib/Netgen/libsrc/makefile.mach.SGI
 create mode 100644 contrib/Netgen/libsrc/makefile.mach.SGIGCC
 create mode 100644 contrib/Netgen/libsrc/makefile.mach.SUN
 create mode 100644 contrib/Netgen/libsrc/meshing/Makefile
 create mode 100644 contrib/Netgen/libsrc/meshing/adfront2.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/adfront2.hpp
 create mode 100644 contrib/Netgen/libsrc/meshing/adfront3.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/adfront3.hpp
 create mode 100644 contrib/Netgen/libsrc/meshing/bisect.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/bisect.hpp
 create mode 100644 contrib/Netgen/libsrc/meshing/boundarylayer.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/boundarylayer.hpp
 create mode 100644 contrib/Netgen/libsrc/meshing/clusters.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/clusters.hpp
 create mode 100644 contrib/Netgen/libsrc/meshing/curvedelems.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/curvedelems.hpp
 create mode 100644 contrib/Netgen/libsrc/meshing/curvedelems2.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/delaunay.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/findip.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/findip.hpp
 create mode 100644 contrib/Netgen/libsrc/meshing/geomsearch.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/geomsearch.hpp
 create mode 100644 contrib/Netgen/libsrc/meshing/global.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/global.hpp
 create mode 100644 contrib/Netgen/libsrc/meshing/hpref_prism.hpp
 create mode 100644 contrib/Netgen/libsrc/meshing/hpref_quad.hpp
 create mode 100644 contrib/Netgen/libsrc/meshing/hpref_tet.hpp
 create mode 100644 contrib/Netgen/libsrc/meshing/hpref_trig.hpp
 create mode 100644 contrib/Netgen/libsrc/meshing/hprefinement.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/hprefinement.hpp
 create mode 100644 contrib/Netgen/libsrc/meshing/improve2.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/improve2.hpp
 create mode 100644 contrib/Netgen/libsrc/meshing/improve2gen.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/improve3.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/improve3.hpp
 create mode 100644 contrib/Netgen/libsrc/meshing/localh.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/localh.hpp
 create mode 100644 contrib/Netgen/libsrc/meshing/meshclass.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/meshclass.hpp
 create mode 100644 contrib/Netgen/libsrc/meshing/meshfunc.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/meshfunc.hpp
 create mode 100644 contrib/Netgen/libsrc/meshing/meshfunc2d.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/meshing.hpp
 create mode 100644 contrib/Netgen/libsrc/meshing/meshing2.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/meshing2.hpp
 create mode 100644 contrib/Netgen/libsrc/meshing/meshing3.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/meshing3.hpp
 create mode 100644 contrib/Netgen/libsrc/meshing/meshtool.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/meshtool.hpp
 create mode 100644 contrib/Netgen/libsrc/meshing/meshtype.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/meshtype.hpp
 create mode 100644 contrib/Netgen/libsrc/meshing/msghandler.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/msghandler.hpp
 create mode 100644 contrib/Netgen/libsrc/meshing/netrule2.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/netrule3.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/parser2.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/parser3.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/prism2rls.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/prism2rls_2.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/pyramid2rls.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/pyramidrls.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/quadrls.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/refine.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/ruler2.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/ruler2.hpp
 create mode 100644 contrib/Netgen/libsrc/meshing/ruler3.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/ruler3.hpp
 create mode 100644 contrib/Netgen/libsrc/meshing/secondorder.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/smoothing2.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/smoothing3.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/specials.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/specials.hpp
 create mode 100644 contrib/Netgen/libsrc/meshing/tetrarls.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/topology.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/topology.hpp
 create mode 100644 contrib/Netgen/libsrc/meshing/triarls.cpp
 create mode 100644 contrib/Netgen/libsrc/meshing/zrefine.cpp
 create mode 100644 contrib/Netgen/libsrc/occ/Makefile
 create mode 100644 contrib/Netgen/libsrc/occ/occgenmesh.cpp
 create mode 100644 contrib/Netgen/libsrc/occ/occgeom.cpp
 create mode 100644 contrib/Netgen/libsrc/occ/occgeom.hpp
 create mode 100644 contrib/Netgen/libsrc/occ/occmeshsurf.cpp
 create mode 100644 contrib/Netgen/libsrc/occ/occmeshsurf.hpp
 create mode 100644 contrib/Netgen/libsrc/opti/Makefile
 create mode 100644 contrib/Netgen/libsrc/opti/bfgs.cpp
 create mode 100644 contrib/Netgen/libsrc/opti/linopt.cpp
 create mode 100644 contrib/Netgen/libsrc/opti/linsearch.cpp
 create mode 100644 contrib/Netgen/libsrc/opti/opti.hpp
 create mode 100644 contrib/Netgen/libsrc/stlgeom/Makefile
 create mode 100644 contrib/Netgen/libsrc/stlgeom/meshstlsurface.cpp
 create mode 100644 contrib/Netgen/libsrc/stlgeom/meshstlsurface.hpp
 create mode 100644 contrib/Netgen/libsrc/stlgeom/stlgeom.cpp
 create mode 100644 contrib/Netgen/libsrc/stlgeom/stlgeom.hpp
 create mode 100644 contrib/Netgen/libsrc/stlgeom/stlgeomchart.cpp
 create mode 100644 contrib/Netgen/libsrc/stlgeom/stlgeommesh.cpp
 create mode 100644 contrib/Netgen/libsrc/stlgeom/stlline.cpp
 create mode 100644 contrib/Netgen/libsrc/stlgeom/stlline.hpp
 create mode 100644 contrib/Netgen/libsrc/stlgeom/stltool.cpp
 create mode 100644 contrib/Netgen/libsrc/stlgeom/stltool.hpp
 create mode 100644 contrib/Netgen/libsrc/stlgeom/stltopology.cpp
 create mode 100644 contrib/Netgen/libsrc/stlgeom/stltopology.hpp
 create mode 100644 contrib/Netgen/libsrc/visualization/Makefile
 create mode 100644 contrib/Netgen/libsrc/visualization/meshdoc.cpp
 create mode 100644 contrib/Netgen/libsrc/visualization/meshdoc.hpp
 create mode 100644 contrib/Netgen/libsrc/visualization/mvdraw.cpp
 create mode 100644 contrib/Netgen/libsrc/visualization/mvdraw.hpp
 create mode 100644 contrib/Netgen/libsrc/visualization/soldata.hpp
 create mode 100644 contrib/Netgen/libsrc/visualization/stlmeshing.cpp
 create mode 100644 contrib/Netgen/libsrc/visualization/vispar.hpp
 create mode 100644 contrib/Netgen/libsrc/visualization/visual.hpp
 create mode 100644 contrib/Netgen/libsrc/visualization/vscsg.cpp
 create mode 100644 contrib/Netgen/libsrc/visualization/vsmesh.cpp
 create mode 100644 contrib/Netgen/libsrc/visualization/vsocc.cpp
 create mode 100644 contrib/Netgen/libsrc/visualization/vssolution.cpp
 create mode 100644 contrib/Netgen/libsrc/visualization/vssolution.hpp
 create mode 100644 contrib/Netgen/nglib_addon.cpp
 create mode 100644 contrib/Netgen/nglib_addon.h
 create mode 100644 contrib/Netgen/nglib_addon.o
 create mode 100644 contrib/Tetgen/LICENSE
 create mode 100644 contrib/Tetgen/Makefile
 create mode 100644 contrib/Tetgen/predicates.cxx
 create mode 100644 contrib/Tetgen/tetgen.cxx
 create mode 100644 contrib/Tetgen/tetgen.h
 create mode 100644 contrib/Triangle/Makefile
 create mode 100644 contrib/Triangle/README
 create mode 100644 contrib/Triangle/triangle.c
 create mode 100644 contrib/Triangle/triangle.h

diff --git a/Common/Makefile b/Common/Makefile
index 612c4ed3d5..7235d9ed38 100644
--- a/Common/Makefile
+++ b/Common/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.76 2005-06-03 17:32:29 geuzaine Exp $
+# $Id: Makefile,v 1.77 2005-09-21 17:29:36 geuzaine Exp $
 #
 # Copyright (C) 1997-2005 C. Geuzaine, J.-F. Remacle
 #
@@ -23,7 +23,8 @@ include ../variables
 
 LIB     = ../lib/libGmshCommon.a
 INCLUDE = -I../Common -I../DataStr -I../Geo -I../Graphics -I../Mesh\
-          -I../Numeric -I../Parser -I../Plugin -I../Fltk -I../MathEval
+          -I../Numeric -I../Parser -I../Plugin -I../Fltk\
+          -I../contrib/MathEval
 CFLAGS  = ${OPTIM} ${FLAGS} ${INCLUDE}
 
 SRC = Context.cpp\
diff --git a/Fltk/Makefile b/Fltk/Makefile
index 05d406690b..99355da315 100644
--- a/Fltk/Makefile
+++ b/Fltk/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.73 2005-08-22 00:35:06 geuzaine Exp $
+# $Id: Makefile,v 1.74 2005-09-21 17:29:36 geuzaine Exp $
 #
 # Copyright (C) 1997-2005 C. Geuzaine, J.-F. Remacle
 #
@@ -23,7 +23,8 @@ include ../variables
 
 LIB     = ../lib/libGmshFltk.a
 INCLUDE = -I../Common -I../DataStr -I../Graphics -I../Geo -I../Mesh\
-          -I../Numeric -I../Parser -I../Fltk -I../Plugin -I../utils/solvers -I../ANN/include
+          -I../Numeric -I../Parser -I../Fltk -I../Plugin -I../utils/solvers\
+          -I../contrib/ANN/include
 CFLAGS  = ${OPTIM} ${FLAGS} ${INCLUDE}
 
 SRC = Main.cpp \
diff --git a/Geo/Makefile b/Geo/Makefile
index 7c08013cf5..106c942560 100644
--- a/Geo/Makefile
+++ b/Geo/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.65 2005-07-15 10:31:06 geuzaine Exp $
+# $Id: Makefile,v 1.66 2005-09-21 17:29:36 geuzaine Exp $
 #
 # Copyright (C) 1997-2005 C. Geuzaine, J.-F. Remacle
 #
@@ -22,8 +22,9 @@
 include ../variables
 
 LIB     = ../lib/libGmshGeo.a
-INCLUDE = -I../Common -I../DataStr -I../Geo -I../Mesh -I../Numeric -I../NR\
-          -I../Parser -I../Fltk 
+INCLUDE = -I../Common -I../DataStr -I../Geo -I../Mesh -I../Numeric\
+          -I../Parser -I../Fltk\
+          -I../contrib/NR
 CFLAGS  = ${OPTIM} ${FLAGS} ${INCLUDE}
 
 SRC = CAD.cpp \
diff --git a/Graphics/Makefile b/Graphics/Makefile
index 57db350a9a..d168498c7c 100644
--- a/Graphics/Makefile
+++ b/Graphics/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.72 2005-08-19 14:07:32 remacle Exp $
+# $Id: Makefile,v 1.73 2005-09-21 17:29:37 geuzaine Exp $
 #
 # Copyright (C) 1997-2005 C. Geuzaine, J.-F. Remacle
 #
@@ -23,7 +23,8 @@ include ../variables
 
 LIB     = ../lib/libGmshGraphics.a
 INCLUDE = -I../Common -I../DataStr -I../Geo -I../Graphics\
-          -I../Fltk -I../Mesh -I../Numeric -I../Parser -I../Plugin -I../ANN/include
+          -I../Fltk -I../Mesh -I../Numeric -I../Parser -I../Plugin\
+          -I../contrib/ANN/include
 CFLAGS  = ${OPTIM} ${FLAGS} ${INCLUDE}
 
 SRC = Draw.cpp \
diff --git a/Makefile b/Makefile
index 0886f105ab..c69733e595 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.395 2005-08-31 22:03:26 geuzaine Exp $
+# $Id: Makefile,v 1.396 2005-09-21 17:29:36 geuzaine Exp $
 #
 # Copyright (C) 1997-2005 C. Geuzaine, J.-F. Remacle
 #
@@ -90,7 +90,7 @@ clean:
 	rm -f ${GMSH_VERSION_FILE}
 
 clean-most:
-	for i in doc lib ${GMSH_DIRS:Netgen=}; do (cd $$i && ${MAKE} clean); done
+	for i in doc lib ${GMSH_DIRS:contrib/Netgen=}; do (cd $$i && ${MAKE} clean); done
 	rm -f ${GMSH_VERSION_FILE}
 
 depend: initialtag
@@ -133,24 +133,26 @@ etags:
 
 source-common:
 	rm -rf gmsh-${GMSH_VERSION}
-	tar zcvf gmsh.tgz `ls TODO README* */README* */COPYING* */VERSION*\
-                           configure *.in *.spec Makefile */Makefile\
-                           */*.[chylr] */*.[ch]pp */*/*/*.[chyli]*\
-                           */*.rc */*.res */*.ico */*.icns`\
+	tar zcvf gmsh.tgz `ls TODO README* */README* */*/README* */COPYING* */VERSION*\
+                           configure *.in *.spec Makefile */Makefile */*/Makefile\
+                           */*.[chylr] */*/*.[chylr] */*.[ch]pp */*/*.[ch]pp\
+                           */*/*/*/*.[chyli]* */*.rc */*.res */*.ico */*.icns`\
                            doc demos tutorial utils
 	mkdir gmsh-${GMSH_VERSION}
 	cd gmsh-${GMSH_VERSION} && tar zxvf ../gmsh.tgz
 	rm -f gmsh.tgz
 
 source: source-common
-	cd gmsh-${GMSH_VERSION} && rm -rf CVS */CVS */*/CVS */*/*/CVS */.globalrc\
-          NR Triangle/triangle.* Tetgen/tetgen.* Tetgen/predicates.*\
+	cd gmsh-${GMSH_VERSION} && rm -rf CVS */CVS */*/CVS */*/*/CVS */*/*/*/CVS\
+          */.globalrc contrib/NR contrib/Triangle/triangle.* contrib/Tetgen/tetgen.*\
+          contrib/Tetgen/predicates.*\
           utils/commercial ${GMSH_VERSION_FILE} 
 	tar zcvf gmsh-${GMSH_VERSION}-source.tgz gmsh-${GMSH_VERSION}
 
 source-commercial: source-common
-	cd gmsh-${GMSH_VERSION} && rm -rf CVS */CVS */*/CVS */*/*/CVS */.globalrc\
-          MathEval Triangle/triangle.* Tetgen/tetgen.* Tetgen/predicates.* Netgen/libsrc\
+	cd gmsh-${GMSH_VERSION} && rm -rf CVS */CVS */*/CVS */*/*/CVS  */*/*/*/CVS\
+          */.globalrc contrib/MathEval contrib/Triangle/triangle.* contrib/Tetgen/tetgen.*\
+          contrib/Tetgen/predicates.* contrib/Netgen/libsrc\
           TODO *.spec doc/gmsh.html doc/FAQ doc/README.cvs\
           utils/commercial ${GMSH_VERSION_FILE}
 	cp -f utils/commercial/README gmsh-${GMSH_VERSION}/README
diff --git a/Mesh/Makefile b/Mesh/Makefile
index 7eb1902c4c..02f16d2a67 100644
--- a/Mesh/Makefile
+++ b/Mesh/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.95 2005-09-07 14:36:45 remacle Exp $
+# $Id: Makefile,v 1.96 2005-09-21 17:29:37 geuzaine Exp $
 #
 # Copyright (C) 1997-2005 C. Geuzaine, J.-F. Remacle
 #
@@ -22,9 +22,12 @@
 include ../variables
 
 LIB     = ../lib/libGmshMesh.a
-INCLUDE = -I../Numeric -I../NR -I../Common -I../DataStr -I../Geo -I../Mesh\
-          -I../Graphics -I../Parser -I../Fltk -I../Triangle -I../Tetgen\
-          -I../Netgen -I../Netgen/libsrc/include  -I../Netgen/libsrc/interface -I../ANN/include -I../Metis
+INCLUDE = -I../Numeric -I../Common -I../DataStr -I../Geo -I../Mesh\
+          -I../Graphics -I../Parser -I../Fltk\
+          -I../contrib/NR -I../contrib/Triangle -I../contrib/Tetgen\
+          -I../contrib/Netgen -I../contrib/Netgen/libsrc/include\
+          -I../contrib/Netgen/libsrc/interface -I../contrib/ANN/include\
+          -I../contrib/Metis
 CFLAGS  = ${OPTIM} ${FLAGS} ${INCLUDE}
 
 SRC = 1D_Mesh.cpp \
diff --git a/Parser/Makefile b/Parser/Makefile
index 875bef0aac..1bc5e3a8be 100644
--- a/Parser/Makefile
+++ b/Parser/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.74 2005-08-19 14:07:35 remacle Exp $
+# $Id: Makefile,v 1.75 2005-09-21 17:29:37 geuzaine Exp $
 #
 # Copyright (C) 1997-2005 C. Geuzaine, J.-F. Remacle
 #
@@ -23,7 +23,8 @@ include ../variables
 
 LIB     = ../lib/libGmshParser.a
 INCLUDE = -I../Common -I../DataStr -I../Geo -I../Graphics\
-          -I../Mesh -I../Numeric -I../Fltk -I../Plugin -I../Parallel -I../ANN/include
+          -I../Mesh -I../Numeric -I../Fltk -I../Plugin -I../Parallel\
+          -I../contrib/ANN/include
 CFLAGS  = ${OPTIM} ${FLAGS} ${INCLUDE}
 
 SRC = Gmsh.tab.cpp\
diff --git a/Plugin/Makefile b/Plugin/Makefile
index 12b3bdfb92..a1102f133a 100644
--- a/Plugin/Makefile
+++ b/Plugin/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.83 2005-06-27 19:34:34 geuzaine Exp $
+# $Id: Makefile,v 1.84 2005-09-21 17:29:37 geuzaine Exp $
 #
 # Copyright (C) 1997-2005 C. Geuzaine, J.-F. Remacle
 #
@@ -23,8 +23,9 @@ include ../variables
 
 LIB     = ../lib/libGmshPlugin.a
 INCLUDE = -I../Common -I../Graphics -I../DataStr -I../Geo -I../Fltk\
-            -I../Mesh -I../Numeric -I../Triangle -I../MathEval
-CFLAGS  = -DMPICH_SKIP_MPICXX ${OPTIM} ${FLAGS} ${INCLUDE}
+          -I../Mesh -I../Numeric -I../Triangle\
+          -I../contrib/MathEval
+CFLAGS  = ${OPTIM} ${FLAGS} ${INCLUDE}
 
 SRC = Plugin.cpp\
         Levelset.cpp\
diff --git a/configure b/configure
index 8656b52314..35500545c5 100755
--- a/configure
+++ b/configure
@@ -3742,24 +3742,24 @@ else
 
 fi
 
-echo "$as_me:$LINENO: checking for ./Triangle/triangle.c" >&5
-echo $ECHO_N "checking for ./Triangle/triangle.c... $ECHO_C" >&6
-if test "${ac_cv_file___Triangle_triangle_c+set}" = set; then
+echo "$as_me:$LINENO: checking for ./contrib/Triangle/triangle.c" >&5
+echo $ECHO_N "checking for ./contrib/Triangle/triangle.c... $ECHO_C" >&6
+if test "${ac_cv_file___contrib_Triangle_triangle_c+set}" = set; then
   echo $ECHO_N "(cached) $ECHO_C" >&6
 else
   test "$cross_compiling" = yes &&
   { { echo "$as_me:$LINENO: error: cannot check for file existence when cross compiling" >&5
 echo "$as_me: error: cannot check for file existence when cross compiling" >&2;}
    { (exit 1); exit 1; }; }
-if test -r "./Triangle/triangle.c"; then
-  ac_cv_file___Triangle_triangle_c=yes
+if test -r "./contrib/Triangle/triangle.c"; then
+  ac_cv_file___contrib_Triangle_triangle_c=yes
 else
-  ac_cv_file___Triangle_triangle_c=no
+  ac_cv_file___contrib_Triangle_triangle_c=no
 fi
 fi
-echo "$as_me:$LINENO: result: $ac_cv_file___Triangle_triangle_c" >&5
-echo "${ECHO_T}$ac_cv_file___Triangle_triangle_c" >&6
-if test $ac_cv_file___Triangle_triangle_c = yes; then
+echo "$as_me:$LINENO: result: $ac_cv_file___contrib_Triangle_triangle_c" >&5
+echo "${ECHO_T}$ac_cv_file___contrib_Triangle_triangle_c" >&6
+if test $ac_cv_file___contrib_Triangle_triangle_c = yes; then
   TRIANGLE="yes"
 else
   TRIANGLE="no"
@@ -3767,15 +3767,15 @@ fi
 
 if test "x${TRIANGLE}" = "xyes"; then
   if test "x$enable_triangle" != "xno"; then
-     GMSH_DIRS="${GMSH_DIRS} Triangle"
+     GMSH_DIRS="${GMSH_DIRS} contrib/Triangle"
      GMSH_LIBS="${GMSH_LIBS} -lGmshTriangle"
      FLAGS="-DHAVE_TRIANGLE ${FLAGS}"
      echo "********************************************************************"
      echo "You are building a version of Gmsh that contains Jonathan"
      echo "Shewchuk's Triangle as an alternative isotropic 2D mesh generator."
      echo "Please note that by doing so, you agree with Triangle's licensing"
-     echo "requirements stated in ./Triangle/README. (Most notably, you may"
-     echo "then only redistribute Gmsh for non-commercial purposes.)"
+     echo "requirements stated in ./contrib/Triangle/README. (Most notably, you"
+     echo "may then only redistribute Gmsh for non-commercial purposes.)"
      echo "To disable Triangle, run configure again with the --disable-triangle"
      echo "option."
      echo "********************************************************************"
@@ -3787,33 +3787,33 @@ else
      echo "isotropic 2D mesh generator, please download Triangle from the"
      echo "author's web site at http://www.cs.cmu.edu/~quake/triangle.html,"
      echo "unpack the archive and copy the two files 'triangle.c' and"
-     echo "'triangle.h' in the ./Triangle subdirectory. Then run ./configure"
-     echo "again."
+     echo "'triangle.h' in the ./contrib/Triangle subdirectory. Then run"
+     echo "./configure again."
      echo "Please note that by doing so, you agree with Triangle's licensing"
-     echo "requirements stated in ./Triangle/README. (Most notably, you may"
-     echo "then only redistribute Gmsh if no compensation is received.)"
+     echo "requirements stated in ./contrib/Triangle/README. (Most notably, you"
+     echo "may then only redistribute Gmsh if no compensation is received.)"
      echo "********************************************************************"
   fi
 fi
 
-echo "$as_me:$LINENO: checking for ./ANN/include/ANN/ANN.h" >&5
-echo $ECHO_N "checking for ./ANN/include/ANN/ANN.h... $ECHO_C" >&6
-if test "${ac_cv_file___ANN_include_ANN_ANN_h+set}" = set; then
+echo "$as_me:$LINENO: checking for ./contrib/ANN/include/ANN/ANN.h" >&5
+echo $ECHO_N "checking for ./contrib/ANN/include/ANN/ANN.h... $ECHO_C" >&6
+if test "${ac_cv_file___contrib_ANN_include_ANN_ANN_h+set}" = set; then
   echo $ECHO_N "(cached) $ECHO_C" >&6
 else
   test "$cross_compiling" = yes &&
   { { echo "$as_me:$LINENO: error: cannot check for file existence when cross compiling" >&5
 echo "$as_me: error: cannot check for file existence when cross compiling" >&2;}
    { (exit 1); exit 1; }; }
-if test -r "./ANN/include/ANN/ANN.h"; then
-  ac_cv_file___ANN_include_ANN_ANN_h=yes
+if test -r "./contrib/ANN/include/ANN/ANN.h"; then
+  ac_cv_file___contrib_ANN_include_ANN_ANN_h=yes
 else
-  ac_cv_file___ANN_include_ANN_ANN_h=no
+  ac_cv_file___contrib_ANN_include_ANN_ANN_h=no
 fi
 fi
-echo "$as_me:$LINENO: result: $ac_cv_file___ANN_include_ANN_ANN_h" >&5
-echo "${ECHO_T}$ac_cv_file___ANN_include_ANN_ANN_h" >&6
-if test $ac_cv_file___ANN_include_ANN_ANN_h = yes; then
+echo "$as_me:$LINENO: result: $ac_cv_file___contrib_ANN_include_ANN_ANN_h" >&5
+echo "${ECHO_T}$ac_cv_file___contrib_ANN_include_ANN_ANN_h" >&6
+if test $ac_cv_file___contrib_ANN_include_ANN_ANN_h = yes; then
   ANN="yes"
 else
   ANN="no"
@@ -3821,14 +3821,14 @@ fi
 
 if test "x${ANN}" = "xyes"; then
   if test "x$enable_ann" != "xno"; then
-     GMSH_DIRS="${GMSH_DIRS} ANN"
+     GMSH_DIRS="${GMSH_DIRS} contrib/ANN"
      GMSH_LIBS="${GMSH_LIBS} -lGmshANN"
      FLAGS="-DHAVE_ANN_ ${FLAGS}"
      echo "********************************************************************"
      echo "You are building a version of Gmsh that contains ANN, the"
      echo "Approximate Nearest Neighbor library."
      echo "Please note that by doing so, you agree with ANN's licensing"
-     echo "requirements stated in ./ANN/Copyright.txt."
+     echo "requirements stated in ./contrib/ANN/Copyright.txt."
      echo "To disable ANN, run configure again with the --disable-ann"
      echo "option."
      echo "********************************************************************"
@@ -3840,29 +3840,29 @@ else
      echo "STL mesher, please download ANN from the"
      echo "author's web site at http://www.cs.umd.edu/~mount/ANN/,"
      echo "unpack the archive and copy both src and include directories in the"
-     echo "./ANN subdirectory. Then run ./configure again."
+     echo "./contrib/ANN subdirectory. Then run ./configure again."
      echo "********************************************************************"
   fi
 fi
 
-echo "$as_me:$LINENO: checking for ./Metis/metis.h" >&5
-echo $ECHO_N "checking for ./Metis/metis.h... $ECHO_C" >&6
-if test "${ac_cv_file___Metis_metis_h+set}" = set; then
+echo "$as_me:$LINENO: checking for ./contrib/Metis/metis.h" >&5
+echo $ECHO_N "checking for ./contrib/Metis/metis.h... $ECHO_C" >&6
+if test "${ac_cv_file___contrib_Metis_metis_h+set}" = set; then
   echo $ECHO_N "(cached) $ECHO_C" >&6
 else
   test "$cross_compiling" = yes &&
   { { echo "$as_me:$LINENO: error: cannot check for file existence when cross compiling" >&5
 echo "$as_me: error: cannot check for file existence when cross compiling" >&2;}
    { (exit 1); exit 1; }; }
-if test -r "./Metis/metis.h"; then
-  ac_cv_file___Metis_metis_h=yes
+if test -r "./contrib/Metis/metis.h"; then
+  ac_cv_file___contrib_Metis_metis_h=yes
 else
-  ac_cv_file___Metis_metis_h=no
+  ac_cv_file___contrib_Metis_metis_h=no
 fi
 fi
-echo "$as_me:$LINENO: result: $ac_cv_file___Metis_metis_h" >&5
-echo "${ECHO_T}$ac_cv_file___Metis_metis_h" >&6
-if test $ac_cv_file___Metis_metis_h = yes; then
+echo "$as_me:$LINENO: result: $ac_cv_file___contrib_Metis_metis_h" >&5
+echo "${ECHO_T}$ac_cv_file___contrib_Metis_metis_h" >&6
+if test $ac_cv_file___contrib_Metis_metis_h = yes; then
   METIS="yes"
 else
   METIS="no"
@@ -3870,14 +3870,14 @@ fi
 
 if test "x${METIS}" = "xyes"; then
   if test "x$enable_metis" != "xno"; then
-     GMSH_DIRS="${GMSH_DIRS} Metis"
+     GMSH_DIRS="${GMSH_DIRS} contrib/Metis"
      GMSH_LIBS="${GMSH_LIBS} -lGmshMetis"
      FLAGS="-DHAVE_METIS ${FLAGS}"
      echo "********************************************************************"
      echo "You are building a version of Gmsh that contains METIS, the"
      echo "Serial Graph Partitioner."
      echo "Please note that by doing so, you agree with METIS's licensing"
-     echo "requirements stated in ./Metis/Doc/manual.ps."
+     echo "requirements stated in ./contrib/Metis/Doc/manual.ps."
      echo "To disable METIS, run configure again with the --disable-metis"
      echo "option."
      echo "********************************************************************"
@@ -3885,32 +3885,33 @@ if test "x${METIS}" = "xyes"; then
 else
   if test "x$enable-metis" != "xno"; then
      echo "********************************************************************"
-     echo "If you want to use METIS for doing mesh partitioning, please download METIS from the"
-     echo "author's web site at http://www-users.cs.umn.edu/~karypis/metis/"
-     echo "unpack the archive and copy the Lib Directory in the"
-     echo "./Metis subdirectory. Then run ./configure again."
+     echo "If you want to use METIS for doing mesh partitioning, please"
+     echo "download METIS from the author's web site at"
+     echo "http://www-users.cs.umn.edu/~karypis/metis/, unpack the archive and"
+     echo "copy the Lib Directory in the ./contrib/Metis subdirectory. Then"
+     echo "run ./configure again."
      echo "********************************************************************"
   fi
 fi
 
-echo "$as_me:$LINENO: checking for ./Netgen/libsrc/meshing/meshclass.cpp" >&5
-echo $ECHO_N "checking for ./Netgen/libsrc/meshing/meshclass.cpp... $ECHO_C" >&6
-if test "${ac_cv_file___Netgen_libsrc_meshing_meshclass_cpp+set}" = set; then
+echo "$as_me:$LINENO: checking for ./contrib/Netgen/libsrc/meshing/meshclass.cpp" >&5
+echo $ECHO_N "checking for ./contrib/Netgen/libsrc/meshing/meshclass.cpp... $ECHO_C" >&6
+if test "${ac_cv_file___contrib_Netgen_libsrc_meshing_meshclass_cpp+set}" = set; then
   echo $ECHO_N "(cached) $ECHO_C" >&6
 else
   test "$cross_compiling" = yes &&
   { { echo "$as_me:$LINENO: error: cannot check for file existence when cross compiling" >&5
 echo "$as_me: error: cannot check for file existence when cross compiling" >&2;}
    { (exit 1); exit 1; }; }
-if test -r "./Netgen/libsrc/meshing/meshclass.cpp"; then
-  ac_cv_file___Netgen_libsrc_meshing_meshclass_cpp=yes
+if test -r "./contrib/Netgen/libsrc/meshing/meshclass.cpp"; then
+  ac_cv_file___contrib_Netgen_libsrc_meshing_meshclass_cpp=yes
 else
-  ac_cv_file___Netgen_libsrc_meshing_meshclass_cpp=no
+  ac_cv_file___contrib_Netgen_libsrc_meshing_meshclass_cpp=no
 fi
 fi
-echo "$as_me:$LINENO: result: $ac_cv_file___Netgen_libsrc_meshing_meshclass_cpp" >&5
-echo "${ECHO_T}$ac_cv_file___Netgen_libsrc_meshing_meshclass_cpp" >&6
-if test $ac_cv_file___Netgen_libsrc_meshing_meshclass_cpp = yes; then
+echo "$as_me:$LINENO: result: $ac_cv_file___contrib_Netgen_libsrc_meshing_meshclass_cpp" >&5
+echo "${ECHO_T}$ac_cv_file___contrib_Netgen_libsrc_meshing_meshclass_cpp" >&6
+if test $ac_cv_file___contrib_Netgen_libsrc_meshing_meshclass_cpp = yes; then
   NETGEN="yes"
 else
   NETGEN="no"
@@ -3918,13 +3919,13 @@ fi
 
 if test "x${NETGEN}" = "xyes"; then
   if test "x$enable_netgen" != "xno"; then
-     GMSH_DIRS="${GMSH_DIRS} Netgen"
+     GMSH_DIRS="${GMSH_DIRS} contrib/Netgen"
      GMSH_LIBS="${GMSH_LIBS} -lGmshNetgen"
      FLAGS="-DHAVE_NETGEN ${FLAGS}"
      echo "********************************************************************"
      echo "You are building a version of Gmsh that contains Joachim Schoberl's"
      echo "Netgen as an alternative 3D mesh generator. Netgen is distributed"
-     echo "under the GNU LGPL: see ./Netgen/COPYING.LIB for more info."
+     echo "under the GNU LGPL: see ./contrib/Netgen/COPYING.LIB for more info."
      echo "To disable Netgen, run configure again with the --disable-netgen"
      echo "option."
      echo "********************************************************************"
@@ -3935,32 +3936,32 @@ else
      echo "If you want to use Joachim Schoberl's Netgen as an alternative"
      echo "3D mesh generator, please download Netgen from the project's"
      echo "web site at http://www.hpfem.jku.at/netgen/, unpack the archive"
-     echo "and move the libsrc subdirectory in the ./Netgen subdirectory. Then"
-     echo "run ./configure again."
+     echo "and move the libsrc subdirectory in the ./contrib/Netgen"
+     echo "subdirectory. Then run ./configure again."
      echo "Please note that by doing so, you agree with Netgen's licensing"
-     echo "requirements stated in ./Netgen/COPYING.LIB."
+     echo "requirements stated in ./contrib/Netgen/COPYING.LIB."
      echo "********************************************************************"
   fi
 fi
 
-echo "$as_me:$LINENO: checking for ./Tetgen/tetgen.h" >&5
-echo $ECHO_N "checking for ./Tetgen/tetgen.h... $ECHO_C" >&6
-if test "${ac_cv_file___Tetgen_tetgen_h+set}" = set; then
+echo "$as_me:$LINENO: checking for ./contrib/Tetgen/tetgen.h" >&5
+echo $ECHO_N "checking for ./contrib/Tetgen/tetgen.h... $ECHO_C" >&6
+if test "${ac_cv_file___contrib_Tetgen_tetgen_h+set}" = set; then
   echo $ECHO_N "(cached) $ECHO_C" >&6
 else
   test "$cross_compiling" = yes &&
   { { echo "$as_me:$LINENO: error: cannot check for file existence when cross compiling" >&5
 echo "$as_me: error: cannot check for file existence when cross compiling" >&2;}
    { (exit 1); exit 1; }; }
-if test -r "./Tetgen/tetgen.h"; then
-  ac_cv_file___Tetgen_tetgen_h=yes
+if test -r "./contrib/Tetgen/tetgen.h"; then
+  ac_cv_file___contrib_Tetgen_tetgen_h=yes
 else
-  ac_cv_file___Tetgen_tetgen_h=no
+  ac_cv_file___contrib_Tetgen_tetgen_h=no
 fi
 fi
-echo "$as_me:$LINENO: result: $ac_cv_file___Tetgen_tetgen_h" >&5
-echo "${ECHO_T}$ac_cv_file___Tetgen_tetgen_h" >&6
-if test $ac_cv_file___Tetgen_tetgen_h = yes; then
+echo "$as_me:$LINENO: result: $ac_cv_file___contrib_Tetgen_tetgen_h" >&5
+echo "${ECHO_T}$ac_cv_file___contrib_Tetgen_tetgen_h" >&6
+if test $ac_cv_file___contrib_Tetgen_tetgen_h = yes; then
   TETGEN="yes"
 else
   TETGEN="no"
@@ -3968,15 +3969,15 @@ fi
 
 if test "x${TETGEN}" = "xyes"; then
   if test "x$enable_tetgen" != "xno"; then
-     GMSH_DIRS="${GMSH_DIRS} Tetgen"
+     GMSH_DIRS="${GMSH_DIRS} contrib/Tetgen"
      GMSH_LIBS="${GMSH_LIBS} -lGmshTetgen"
      FLAGS="-DHAVE_TETGEN ${FLAGS}"
      echo "********************************************************************"
      echo "You are building a version of Gmsh that contains Hang Si's"
      echo "Tetgen as an alternative 3D mesh generator."
      echo "Please note that by doing so, you agree with Tetgen's licensing"
-     echo "requirements stated in ./Tetgen/LICENSE. (Most notably, you may"
-     echo "then only redistribute Gmsh for non-commercial purposes.)"
+     echo "requirements stated in ./contrib/Tetgen/LICENSE. (Most notably, you"
+     echo "may then only redistribute Gmsh for non-commercial purposes.)"
      echo "To disable Tetgen, run configure again with the --disable-tetgen"
      echo "option."
      echo "********************************************************************"
@@ -3988,32 +3989,32 @@ else
      echo "3D mesh generator, please download Tetgen from the project's"
      echo "web site at http://www.tetgen.berlios.de, unpack the archive"
      echo "and move the files predicates.cxx, tetgen.cxx and tetgen.h in"
-     echo "the ./Tetgen subdirectory. Then run ./configure again."
+     echo "the ./contrib/Tetgen subdirectory. Then run ./configure again."
      echo "Please note that by doing so, you agree with Tetgen's licensing"
-     echo "requirements stated in ./Tetgen/LICENSE. (Most notably, you may"
-     echo "then only redistribute Gmsh if no compensation is received.)"
+     echo "requirements stated in ./contrib/Tetgen/LICENSE. (Most notably, you"
+     echo "may then only redistribute Gmsh if no compensation is received.)"
      echo "********************************************************************"
   fi
 fi
 
-echo "$as_me:$LINENO: checking for ./MathEval/matheval.cpp" >&5
-echo $ECHO_N "checking for ./MathEval/matheval.cpp... $ECHO_C" >&6
-if test "${ac_cv_file___MathEval_matheval_cpp+set}" = set; then
+echo "$as_me:$LINENO: checking for ./contrib/MathEval/matheval.cpp" >&5
+echo $ECHO_N "checking for ./contrib/MathEval/matheval.cpp... $ECHO_C" >&6
+if test "${ac_cv_file___contrib_MathEval_matheval_cpp+set}" = set; then
   echo $ECHO_N "(cached) $ECHO_C" >&6
 else
   test "$cross_compiling" = yes &&
   { { echo "$as_me:$LINENO: error: cannot check for file existence when cross compiling" >&5
 echo "$as_me: error: cannot check for file existence when cross compiling" >&2;}
    { (exit 1); exit 1; }; }
-if test -r "./MathEval/matheval.cpp"; then
-  ac_cv_file___MathEval_matheval_cpp=yes
+if test -r "./contrib/MathEval/matheval.cpp"; then
+  ac_cv_file___contrib_MathEval_matheval_cpp=yes
 else
-  ac_cv_file___MathEval_matheval_cpp=no
+  ac_cv_file___contrib_MathEval_matheval_cpp=no
 fi
 fi
-echo "$as_me:$LINENO: result: $ac_cv_file___MathEval_matheval_cpp" >&5
-echo "${ECHO_T}$ac_cv_file___MathEval_matheval_cpp" >&6
-if test $ac_cv_file___MathEval_matheval_cpp = yes; then
+echo "$as_me:$LINENO: result: $ac_cv_file___contrib_MathEval_matheval_cpp" >&5
+echo "${ECHO_T}$ac_cv_file___contrib_MathEval_matheval_cpp" >&6
+if test $ac_cv_file___contrib_MathEval_matheval_cpp = yes; then
   MATHEVAL="yes"
 else
   MATHEVAL="no"
@@ -4021,7 +4022,7 @@ fi
 
 if test "x${MATHEVAL}" = "xyes"; then
   if test "x$enable_matheval" != "xno"; then
-     GMSH_DIRS="${GMSH_DIRS} MathEval"
+     GMSH_DIRS="${GMSH_DIRS} contrib/MathEval"
      GMSH_LIBS="${GMSH_LIBS} -lGmshMathEval"
      FLAGS="-DHAVE_MATH_EVAL ${FLAGS}"
   fi
@@ -4174,24 +4175,24 @@ fi
   fi
 fi
 if test "x${GSL}" != "xyes"; then
-    echo "$as_me:$LINENO: checking for ./NR/dsvdcmp.cpp" >&5
-echo $ECHO_N "checking for ./NR/dsvdcmp.cpp... $ECHO_C" >&6
-if test "${ac_cv_file___NR_dsvdcmp_cpp+set}" = set; then
+    echo "$as_me:$LINENO: checking for ./contrib/NR/dsvdcmp.cpp" >&5
+echo $ECHO_N "checking for ./contrib/NR/dsvdcmp.cpp... $ECHO_C" >&6
+if test "${ac_cv_file___contrib_NR_dsvdcmp_cpp+set}" = set; then
   echo $ECHO_N "(cached) $ECHO_C" >&6
 else
   test "$cross_compiling" = yes &&
   { { echo "$as_me:$LINENO: error: cannot check for file existence when cross compiling" >&5
 echo "$as_me: error: cannot check for file existence when cross compiling" >&2;}
    { (exit 1); exit 1; }; }
-if test -r "./NR/dsvdcmp.cpp"; then
-  ac_cv_file___NR_dsvdcmp_cpp=yes
+if test -r "./contrib/NR/dsvdcmp.cpp"; then
+  ac_cv_file___contrib_NR_dsvdcmp_cpp=yes
 else
-  ac_cv_file___NR_dsvdcmp_cpp=no
+  ac_cv_file___contrib_NR_dsvdcmp_cpp=no
 fi
 fi
-echo "$as_me:$LINENO: result: $ac_cv_file___NR_dsvdcmp_cpp" >&5
-echo "${ECHO_T}$ac_cv_file___NR_dsvdcmp_cpp" >&6
-if test $ac_cv_file___NR_dsvdcmp_cpp = yes; then
+echo "$as_me:$LINENO: result: $ac_cv_file___contrib_NR_dsvdcmp_cpp" >&5
+echo "${ECHO_T}$ac_cv_file___contrib_NR_dsvdcmp_cpp" >&6
+if test $ac_cv_file___contrib_NR_dsvdcmp_cpp = yes; then
   NR="yes"
 else
   NR="no"
@@ -4204,7 +4205,7 @@ fi
     echo "To use the GSL instead, run configure again with the --enable-gsl"
     echo "option."
     echo "********************************************************************"
-    GMSH_DIRS="${GMSH_DIRS} NR"
+    GMSH_DIRS="${GMSH_DIRS} contrib/NR"
     GMSH_LIBS="${GMSH_LIBS} -lGmshNR"
   else
     echo "********************************************************************"
diff --git a/configure.in b/configure.in
index 27f5183387..e9ca9483ea 100644
--- a/configure.in
+++ b/configure.in
@@ -1,4 +1,4 @@
-dnl $Id: configure.in,v 1.79 2005-09-15 16:58:09 geuzaine Exp $
+dnl $Id: configure.in,v 1.80 2005-09-21 17:29:36 geuzaine Exp $
 dnl
 dnl Copyright (C) 1997-2005 C. Geuzaine, J.-F. Remacle
 dnl
@@ -226,18 +226,18 @@ else
 fi
 
 dnl Check if Triangle is installed
-AC_CHECK_FILE(./Triangle/triangle.c, TRIANGLE="yes", TRIANGLE="no")
+AC_CHECK_FILE(./contrib/Triangle/triangle.c, TRIANGLE="yes", TRIANGLE="no")
 if test "x${TRIANGLE}" = "xyes"; then
   if test "x$enable_triangle" != "xno"; then
-     GMSH_DIRS="${GMSH_DIRS} Triangle"
+     GMSH_DIRS="${GMSH_DIRS} contrib/Triangle"
      GMSH_LIBS="${GMSH_LIBS} -lGmshTriangle"
      FLAGS="-DHAVE_TRIANGLE ${FLAGS}"
      echo "********************************************************************"
      echo "You are building a version of Gmsh that contains Jonathan"
      echo "Shewchuk's Triangle as an alternative isotropic 2D mesh generator."
      echo "Please note that by doing so, you agree with Triangle's licensing"
-     echo "requirements stated in ./Triangle/README. (Most notably, you may"
-     echo "then only redistribute Gmsh for non-commercial purposes.)"
+     echo "requirements stated in ./contrib/Triangle/README. (Most notably, you"
+     echo "may then only redistribute Gmsh for non-commercial purposes.)"
      echo "To disable Triangle, run configure again with the --disable-triangle"
      echo "option."
      echo "********************************************************************"
@@ -249,27 +249,27 @@ else
      echo "isotropic 2D mesh generator, please download Triangle from the"
      echo "author's web site at http://www.cs.cmu.edu/~quake/triangle.html,"
      echo "unpack the archive and copy the two files 'triangle.c' and"
-     echo "'triangle.h' in the ./Triangle subdirectory. Then run ./configure"
-     echo "again."
+     echo "'triangle.h' in the ./contrib/Triangle subdirectory. Then run"
+     echo "./configure again."
      echo "Please note that by doing so, you agree with Triangle's licensing"
-     echo "requirements stated in ./Triangle/README. (Most notably, you may"
-     echo "then only redistribute Gmsh if no compensation is received.)"
+     echo "requirements stated in ./contrib/Triangle/README. (Most notably, you"
+     echo "may then only redistribute Gmsh if no compensation is received.)"
      echo "********************************************************************"
   fi
 fi
 
 dnl Check if ANN is installed
-AC_CHECK_FILE(./ANN/include/ANN/ANN.h, ANN="yes", ANN="no")
+AC_CHECK_FILE(./contrib/ANN/include/ANN/ANN.h, ANN="yes", ANN="no")
 if test "x${ANN}" = "xyes"; then
   if test "x$enable_ann" != "xno"; then
-     GMSH_DIRS="${GMSH_DIRS} ANN"
+     GMSH_DIRS="${GMSH_DIRS} contrib/ANN"
      GMSH_LIBS="${GMSH_LIBS} -lGmshANN"
      FLAGS="-DHAVE_ANN_ ${FLAGS}"
      echo "********************************************************************"
      echo "You are building a version of Gmsh that contains ANN, the"
      echo "Approximate Nearest Neighbor library."
      echo "Please note that by doing so, you agree with ANN's licensing"
-     echo "requirements stated in ./ANN/Copyright.txt."
+     echo "requirements stated in ./contrib/ANN/Copyright.txt."
      echo "To disable ANN, run configure again with the --disable-ann"
      echo "option."
      echo "********************************************************************"
@@ -281,23 +281,23 @@ else
      echo "STL mesher, please download ANN from the"
      echo "author's web site at http://www.cs.umd.edu/~mount/ANN/,"
      echo "unpack the archive and copy both src and include directories in the"
-     echo "./ANN subdirectory. Then run ./configure again."
+     echo "./contrib/ANN subdirectory. Then run ./configure again."
      echo "********************************************************************"
   fi
 fi
 
 dnl Check if METIS is installed
-AC_CHECK_FILE(./Metis/metis.h, METIS="yes", METIS="no")
+AC_CHECK_FILE(./contrib/Metis/metis.h, METIS="yes", METIS="no")
 if test "x${METIS}" = "xyes"; then
   if test "x$enable_metis" != "xno"; then
-     GMSH_DIRS="${GMSH_DIRS} Metis"
+     GMSH_DIRS="${GMSH_DIRS} contrib/Metis"
      GMSH_LIBS="${GMSH_LIBS} -lGmshMetis"
      FLAGS="-DHAVE_METIS ${FLAGS}"
      echo "********************************************************************"
      echo "You are building a version of Gmsh that contains METIS, the"
      echo "Serial Graph Partitioner."
      echo "Please note that by doing so, you agree with METIS's licensing"
-     echo "requirements stated in ./Metis/Doc/manual.ps."
+     echo "requirements stated in ./contrib/Metis/Doc/manual.ps."
      echo "To disable METIS, run configure again with the --disable-metis"
      echo "option."
      echo "********************************************************************"
@@ -305,25 +305,26 @@ if test "x${METIS}" = "xyes"; then
 else
   if test "x$enable-metis" != "xno"; then
      echo "********************************************************************"
-     echo "If you want to use METIS for doing mesh partitioning, please download METIS from the"
-     echo "author's web site at http://www-users.cs.umn.edu/~karypis/metis/"
-     echo "unpack the archive and copy the Lib Directory in the"
-     echo "./Metis subdirectory. Then run ./configure again."
+     echo "If you want to use METIS for doing mesh partitioning, please"
+     echo "download METIS from the author's web site at"
+     echo "http://www-users.cs.umn.edu/~karypis/metis/, unpack the archive and"
+     echo "copy the Lib Directory in the ./contrib/Metis subdirectory. Then"
+     echo "run ./configure again."
      echo "********************************************************************"
   fi
 fi
 
 dnl Check if Netgen is installed
-AC_CHECK_FILE(./Netgen/libsrc/meshing/meshclass.cpp, NETGEN="yes", NETGEN="no")
+AC_CHECK_FILE(./contrib/Netgen/libsrc/meshing/meshclass.cpp, NETGEN="yes", NETGEN="no")
 if test "x${NETGEN}" = "xyes"; then
   if test "x$enable_netgen" != "xno"; then
-     GMSH_DIRS="${GMSH_DIRS} Netgen"
+     GMSH_DIRS="${GMSH_DIRS} contrib/Netgen"
      GMSH_LIBS="${GMSH_LIBS} -lGmshNetgen"
      FLAGS="-DHAVE_NETGEN ${FLAGS}"
      echo "********************************************************************"
      echo "You are building a version of Gmsh that contains Joachim Schoberl's"
      echo "Netgen as an alternative 3D mesh generator. Netgen is distributed"
-     echo "under the GNU LGPL: see ./Netgen/COPYING.LIB for more info."
+     echo "under the GNU LGPL: see ./contrib/Netgen/COPYING.LIB for more info."
      echo "To disable Netgen, run configure again with the --disable-netgen"
      echo "option."
      echo "********************************************************************"
@@ -334,27 +335,27 @@ else
      echo "If you want to use Joachim Schoberl's Netgen as an alternative"
      echo "3D mesh generator, please download Netgen from the project's"
      echo "web site at http://www.hpfem.jku.at/netgen/, unpack the archive"
-     echo "and move the libsrc subdirectory in the ./Netgen subdirectory. Then"
-     echo "run ./configure again."
+     echo "and move the libsrc subdirectory in the ./contrib/Netgen"
+     echo "subdirectory. Then run ./configure again."
      echo "Please note that by doing so, you agree with Netgen's licensing"
-     echo "requirements stated in ./Netgen/COPYING.LIB."
+     echo "requirements stated in ./contrib/Netgen/COPYING.LIB."
      echo "********************************************************************"
   fi
 fi
 
 dnl Check if Tetgen is installed
-AC_CHECK_FILE(./Tetgen/tetgen.h, TETGEN="yes", TETGEN="no")
+AC_CHECK_FILE(./contrib/Tetgen/tetgen.h, TETGEN="yes", TETGEN="no")
 if test "x${TETGEN}" = "xyes"; then
   if test "x$enable_tetgen" != "xno"; then
-     GMSH_DIRS="${GMSH_DIRS} Tetgen"
+     GMSH_DIRS="${GMSH_DIRS} contrib/Tetgen"
      GMSH_LIBS="${GMSH_LIBS} -lGmshTetgen"
      FLAGS="-DHAVE_TETGEN ${FLAGS}"
      echo "********************************************************************"
      echo "You are building a version of Gmsh that contains Hang Si's"
      echo "Tetgen as an alternative 3D mesh generator."
      echo "Please note that by doing so, you agree with Tetgen's licensing"
-     echo "requirements stated in ./Tetgen/LICENSE. (Most notably, you may"
-     echo "then only redistribute Gmsh for non-commercial purposes.)"
+     echo "requirements stated in ./contrib/Tetgen/LICENSE. (Most notably, you"
+     echo "may then only redistribute Gmsh for non-commercial purposes.)"
      echo "To disable Tetgen, run configure again with the --disable-tetgen"
      echo "option."
      echo "********************************************************************"
@@ -366,19 +367,19 @@ else
      echo "3D mesh generator, please download Tetgen from the project's"
      echo "web site at http://www.tetgen.berlios.de, unpack the archive"
      echo "and move the files predicates.cxx, tetgen.cxx and tetgen.h in"
-     echo "the ./Tetgen subdirectory. Then run ./configure again."
+     echo "the ./contrib/Tetgen subdirectory. Then run ./configure again."
      echo "Please note that by doing so, you agree with Tetgen's licensing"
-     echo "requirements stated in ./Tetgen/LICENSE. (Most notably, you may"
-     echo "then only redistribute Gmsh if no compensation is received.)"
+     echo "requirements stated in ./contrib/Tetgen/LICENSE. (Most notably, you"
+     echo "may then only redistribute Gmsh if no compensation is received.)"
      echo "********************************************************************"
   fi
 fi
 
 dnl Check for MathEval
-AC_CHECK_FILE(./MathEval/matheval.cpp, MATHEVAL="yes", MATHEVAL="no")
+AC_CHECK_FILE(./contrib/MathEval/matheval.cpp, MATHEVAL="yes", MATHEVAL="no")
 if test "x${MATHEVAL}" = "xyes"; then
   if test "x$enable_matheval" != "xno"; then
-     GMSH_DIRS="${GMSH_DIRS} MathEval"
+     GMSH_DIRS="${GMSH_DIRS} contrib/MathEval"
      GMSH_LIBS="${GMSH_LIBS} -lGmshMathEval"
      FLAGS="-DHAVE_MATH_EVAL ${FLAGS}"
   fi
@@ -403,7 +404,7 @@ if test "x$enable_gsl" != "xno"; then
 fi
 if test "x${GSL}" != "xyes"; then
   dnl Check if non-free numerical recipes routines are in the tree
-  AC_CHECK_FILE(./NR/dsvdcmp.cpp,NR="yes",NR="no")
+  AC_CHECK_FILE(./contrib/NR/dsvdcmp.cpp,NR="yes",NR="no")
   if test "x${NR}" = "xyes"; then
     echo "********************************************************************"
     echo "You are building a non-free version of Gmsh, using code copyright"
@@ -411,7 +412,7 @@ if test "x${GSL}" != "xyes"; then
     echo "To use the GSL instead, run configure again with the --enable-gsl"
     echo "option."
     echo "********************************************************************"
-    GMSH_DIRS="${GMSH_DIRS} NR"
+    GMSH_DIRS="${GMSH_DIRS} contrib/NR"
     GMSH_LIBS="${GMSH_LIBS} -lGmshNR"
   else
     echo "********************************************************************"
diff --git a/contrib/ANN/Copyright.txt b/contrib/ANN/Copyright.txt
new file mode 100644
index 0000000000..9b752403d4
--- /dev/null
+++ b/contrib/ANN/Copyright.txt
@@ -0,0 +1,47 @@
+ANN: Approximate Nearest Neighbors
+Version: 1.1
+Release Date: May 3, 2005
+----------------------------------------------------------------------------
+Copyright (c) 1997-2005 University of Maryland and Sunil Arya and David
+Mount All Rights Reserved.
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser Public License as published by the
+Free Software Foundation; either version 2.1 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
+Lesser Public License for more details.
+
+A copy of the terms and conditions of the license can be found in
+License.txt or online at
+
+    http://www.gnu.org/copyleft/lesser.html
+
+To obtain a copy, write to the Free Software Foundation, Inc.,
+59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+Disclaimer
+----------
+The University of Maryland and the authors make no representations about
+the suitability or fitness of this software for any purpose.  It is
+provided "as is" without express or implied warranty.
+---------------------------------------------------------------------
+
+Authors
+-------
+David Mount
+Dept of Computer Science
+University of Maryland,
+College Park, MD 20742 USA
+mount@cs.umd.edu
+http://www.cs.umd.edu/~mount/
+
+Sunil Arya
+Dept of Computer Science
+Hong University of Science and Technology
+Clearwater Bay, HONG KONG
+arya@cs.ust.hk
+http://www.cs.ust.hk/faculty/arya/
diff --git a/contrib/ANN/License.txt b/contrib/ANN/License.txt
new file mode 100644
index 0000000000..456ea97817
--- /dev/null
+++ b/contrib/ANN/License.txt
@@ -0,0 +1,450 @@
+----------------------------------------------------------------------
+The ANN Library (all versions) is provided under the terms and
+conditions of the GNU Lesser General Public Library, which is stated
+below.  It can also be found at:
+
+   http://www.gnu.org/copyleft/lesser.html
+
+----------------------------------------------------------------------
+
+GNU LESSER GENERAL PUBLIC LICENSE
+
+Version 2.1, February 1999
+
+Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+59 Temple Place, Suite 330, Boston, MA  02111-1307  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.
+
+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. 
diff --git a/contrib/ANN/Makefile b/contrib/ANN/Makefile
new file mode 100644
index 0000000000..15fc66e90e
--- /dev/null
+++ b/contrib/ANN/Makefile
@@ -0,0 +1,114 @@
+# $Id: Makefile,v 1.1 2005-09-21 17:29:37 geuzaine Exp $
+#
+# Copyright (C) 1997-2005 C. Geuzaine, J.-F. Remacle
+#
+# 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.
+# 
+# Please report all bugs and problems to <gmsh@geuz.org>.
+
+include ../../variables
+
+LIB     = ../../lib/libGmshANN.a
+INCLUDE = -I../../Common -I./include/
+CFLAGS  = ${OPTIM} ${FLAGS} ${INCLUDE}
+
+SRC = src/ANN.cpp\
+      src/bd_fix_rad_search.cpp\
+      src/bd_pr_search.cpp\
+      src/bd_search.cpp\
+      src/bd_tree.cpp\
+      src/brute.cpp\
+      src/kd_dump.cpp\
+      src/kd_fix_rad_search.cpp\
+      src/kd_pr_search.cpp\
+      src/kd_search.cpp\
+      src/kd_split.cpp\
+      src/kd_tree.cpp\
+      src/kd_util.cpp\
+      src/perf.cpp
+
+OBJ = ${SRC:.cpp=.o}
+
+.SUFFIXES: .o .cpp
+
+${LIB}: ${OBJ} 
+	${AR} ${LIB} ${OBJ} 
+	${RANLIB} ${LIB}
+
+.cpp.o:
+	${CXX} ${CFLAGS} -c $< -o ${<:.cpp=.o}
+
+clean:
+	rm -f src/*.o
+
+depend:
+	(sed '/^# DO NOT DELETE THIS LINE/q' Makefile && \
+	${CXX} -MM ${CFLAGS} ${SRC} \
+	) >Makefile.new
+	cp Makefile Makefile.bak
+	cp Makefile.new Makefile
+	rm -f Makefile.new
+
+# DO NOT DELETE THIS LINE
+# 1 "/Users/geuzaine/.gmsh/ANN//"
+ANN.o: src/ANN.cpp include/ANN/ANNx.h include/ANN/ANN.h \
+  include/ANN/ANNperf.h
+# 1 "/Users/geuzaine/.gmsh/ANN//"
+bd_fix_rad_search.o: src/bd_fix_rad_search.cpp src/bd_tree.h \
+  include/ANN/ANNx.h include/ANN/ANN.h src/kd_tree.h \
+  src/kd_fix_rad_search.h src/kd_util.h src/pr_queue_k.h \
+  include/ANN/ANNperf.h
+# 1 "/Users/geuzaine/.gmsh/ANN//"
+bd_pr_search.o: src/bd_pr_search.cpp src/bd_tree.h include/ANN/ANNx.h \
+  include/ANN/ANN.h src/kd_tree.h src/kd_pr_search.h src/kd_util.h \
+  src/pr_queue.h include/ANN/ANNperf.h src/pr_queue_k.h
+# 1 "/Users/geuzaine/.gmsh/ANN//"
+bd_search.o: src/bd_search.cpp src/bd_tree.h include/ANN/ANNx.h \
+  include/ANN/ANN.h src/kd_tree.h src/kd_search.h src/kd_util.h \
+  src/pr_queue_k.h include/ANN/ANNperf.h
+# 1 "/Users/geuzaine/.gmsh/ANN//"
+bd_tree.o: src/bd_tree.cpp src/bd_tree.h include/ANN/ANNx.h \
+  include/ANN/ANN.h src/kd_tree.h src/kd_util.h src/kd_split.h \
+  include/ANN/ANNperf.h
+# 1 "/Users/geuzaine/.gmsh/ANN//"
+brute.o: src/brute.cpp include/ANN/ANNx.h include/ANN/ANN.h \
+  src/pr_queue_k.h include/ANN/ANNperf.h
+# 1 "/Users/geuzaine/.gmsh/ANN//"
+kd_dump.o: src/kd_dump.cpp src/kd_tree.h include/ANN/ANNx.h \
+  include/ANN/ANN.h src/bd_tree.h
+# 1 "/Users/geuzaine/.gmsh/ANN//"
+kd_fix_rad_search.o: src/kd_fix_rad_search.cpp src/kd_fix_rad_search.h \
+  src/kd_tree.h include/ANN/ANNx.h include/ANN/ANN.h src/kd_util.h \
+  src/pr_queue_k.h include/ANN/ANNperf.h
+# 1 "/Users/geuzaine/.gmsh/ANN//"
+kd_pr_search.o: src/kd_pr_search.cpp src/kd_pr_search.h src/kd_tree.h \
+  include/ANN/ANNx.h include/ANN/ANN.h src/kd_util.h src/pr_queue.h \
+  include/ANN/ANNperf.h src/pr_queue_k.h
+# 1 "/Users/geuzaine/.gmsh/ANN//"
+kd_search.o: src/kd_search.cpp src/kd_search.h src/kd_tree.h \
+  include/ANN/ANNx.h include/ANN/ANN.h src/kd_util.h src/pr_queue_k.h \
+  include/ANN/ANNperf.h
+# 1 "/Users/geuzaine/.gmsh/ANN//"
+kd_split.o: src/kd_split.cpp src/kd_tree.h include/ANN/ANNx.h \
+  include/ANN/ANN.h src/kd_util.h src/kd_split.h
+# 1 "/Users/geuzaine/.gmsh/ANN//"
+kd_tree.o: src/kd_tree.cpp src/kd_tree.h include/ANN/ANNx.h \
+  include/ANN/ANN.h src/kd_split.h src/kd_util.h include/ANN/ANNperf.h
+# 1 "/Users/geuzaine/.gmsh/ANN//"
+kd_util.o: src/kd_util.cpp src/kd_util.h src/kd_tree.h include/ANN/ANNx.h \
+  include/ANN/ANN.h include/ANN/ANNperf.h
+# 1 "/Users/geuzaine/.gmsh/ANN//"
+perf.o: src/perf.cpp include/ANN/ANN.h include/ANN/ANNperf.h
diff --git a/contrib/ANN/include/ANN/ANN.h b/contrib/ANN/include/ANN/ANN.h
new file mode 100644
index 0000000000..ca8146a35a
--- /dev/null
+++ b/contrib/ANN/include/ANN/ANN.h
@@ -0,0 +1,829 @@
+//----------------------------------------------------------------------
+// File:			ANN.h
+// Programmer:		Sunil Arya and David Mount
+// Last modified:	05/03/05 (Release 1.1)
+// Description:		Basic include file for approximate nearest
+//					neighbor searching.
+//----------------------------------------------------------------------
+// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
+// David Mount.  All Rights Reserved.
+//
+// This software and related documentation is part of the 
+// Approximate Nearest Neighbor Library (ANN).
+// 
+// Permission to use, copy, and distribute this software and its 
+// documentation is hereby granted free of charge, provided that 
+// (1) it is not a component of a commercial product, and 
+// (2) this notice appears in all copies of the software and
+//   related documentation. 
+// 
+// The University of Maryland (U.M.) and the authors make no representations
+// about the suitability or fitness of this software for any purpose.  It is
+// provided "as is" without express or implied warranty.
+//----------------------------------------------------------------------
+// History:
+//	Revision 0.1  03/04/98
+//		Initial release
+//	Revision 1.0  04/01/05
+//		Added copyright and revision information
+//		Added ANNcoordPrec for coordinate precision.
+//		Added methods theDim, nPoints, maxPoints, thePoints to ANNpointSet.
+//		Cleaned up C++ structure for modern compilers
+//	Revision 1.1  05/03/05
+//		Added fixed-radius k-NN searching
+//----------------------------------------------------------------------
+
+//----------------------------------------------------------------------
+// ANN - approximate nearest neighbor searching
+//	ANN is a library for approximate nearest neighbor searching,
+//	based on the use of standard and priority search in kd-trees
+//	and balanced box-decomposition (bbd) trees. Here are some
+//	references to the main algorithmic techniques used here:
+//
+//		kd-trees:
+//			Friedman, Bentley, and Finkel, ``An algorithm for finding
+//				best matches in logarithmic expected time,'' ACM
+//				Transactions on Mathematical Software, 3(3):209-226, 1977.
+//
+//		Priority search in kd-trees:
+//			Arya and Mount, ``Algorithms for fast vector quantization,''
+//				Proc. of DCC '93: Data Compression Conference, eds. J. A.
+//				Storer and M. Cohn, IEEE Press, 1993, 381-390.
+//
+//		Approximate nearest neighbor search and bbd-trees:
+//			Arya, Mount, Netanyahu, Silverman, and Wu, ``An optimal
+//				algorithm for approximate nearest neighbor searching,''
+//				5th Ann. ACM-SIAM Symposium on Discrete Algorithms,
+//				1994, 573-582.
+//----------------------------------------------------------------------
+
+#ifndef ANN_H
+#define ANN_H
+
+#ifdef WIN33
+  //----------------------------------------------------------------------
+  // For Microsoft Visual C++, externally accessible symbols must be
+  // explicitly indicated with DLL_API, which is somewhat like "extern."
+  //
+  // The following ifdef block is the standard way of creating macros
+  // which make exporting from a DLL simpler. All files within this DLL
+  // are compiled with the DLL_EXPORTS preprocessor symbol defined on the
+  // command line. In contrast, projects that use (or import) the DLL
+  // objects do not define the DLL_EXPORTS symbol. This way any other
+  // project whose source files include this file see DLL_API functions as
+  // being imported from a DLL, wheras this DLL sees symbols defined with
+  // this macro as being exported.
+  //----------------------------------------------------------------------
+  #ifdef DLL_EXPORTS
+	 #define DLL_API __declspec(dllexport)
+  #else
+	#define DLL_API __declspec(dllimport)
+  #endif
+  //----------------------------------------------------------------------
+  // DLL_API is ignored for all other systems
+  //----------------------------------------------------------------------
+#else
+  #define DLL_API
+#endif
+
+//----------------------------------------------------------------------
+//  basic includes
+//----------------------------------------------------------------------
+
+#include <cmath>			// math includes
+#include <iostream>			// I/O streams
+
+//----------------------------------------------------------------------
+// Limits
+// There are a number of places where we use the maximum double value as
+// default initializers (and others may be used, depending on the
+// data/distance representation). These can usually be found in limits.h
+// (as LONG_MAX, INT_MAX) or in float.h (as DBL_MAX, FLT_MAX).
+//
+// Not all systems have these files.  If you are using such a system,
+// you should set the preprocessor symbol ANN_NO_LIMITS_H when
+// compiling, and modify the statements below to generate the
+// appropriate value. For practical purposes, this does not need to be
+// the maximum double value. It is sufficient that it be at least as
+// large than the maximum squared distance between between any two
+// points.
+//----------------------------------------------------------------------
+#ifdef ANN_NO_LIMITS_H					// limits.h unavailable
+  #include <cvalues>					// replacement for limits.h
+  const double ANN_DBL_MAX = MAXDOUBLE;	// insert maximum double
+#else
+  #include <climits>
+  #include <cfloat>
+  const double ANN_DBL_MAX = DBL_MAX;
+#endif
+
+#define ANNversion 		"1.0"			// ANN version and information
+#define ANNversionCmt	""
+#define ANNcopyright	"David M. Mount and Sunil Arya"
+#define ANNlatestRev	"Mar 1, 2005"
+
+//----------------------------------------------------------------------
+//	ANNbool
+//	This is a simple boolean type. Although ANSI C++ is supposed
+//	to support the type bool, some compilers do not have it.
+//----------------------------------------------------------------------
+
+enum ANNbool {ANNfalse = 0, ANNtrue = 1}; // ANN boolean type (non ANSI C++)
+
+//----------------------------------------------------------------------
+//	ANNcoord, ANNdist
+//		ANNcoord and ANNdist are the types used for representing
+//		point coordinates and distances.  They can be modified by the
+//		user, with some care.  It is assumed that they are both numeric
+//		types, and that ANNdist is generally of an equal or higher type
+//		from ANNcoord.	A variable of type ANNdist should be large
+//		enough to store the sum of squared components of a variable
+//		of type ANNcoord for the number of dimensions needed in the
+//		application.  For example, the following combinations are
+//		legal:
+//
+//		ANNcoord		ANNdist
+//		---------		-------------------------------
+//		short			short, int, long, float, double
+//		int				int, long, float, double
+//		long			long, float, double
+//		float			float, double
+//		double			double
+//
+//		It is the user's responsibility to make sure that overflow does
+//		not occur in distance calculation.
+//----------------------------------------------------------------------
+
+typedef double	ANNcoord;				// coordinate data type
+typedef double	ANNdist;				// distance data type
+
+//----------------------------------------------------------------------
+//	ANNidx
+//		ANNidx is a point index.  When the data structure is built, the
+//		points are given as an array.  Nearest neighbor results are
+//		returned as an integer index into this array.  To make it
+//		clearer when this is happening, we define the integer type
+//		ANNidx.	 Indexing starts from 0.
+//		
+//		For fixed-radius near neighbor searching, it is possible that
+//		there are not k nearest neighbors within the search radius.  To
+//		indicate this, the algorithm returns ANN_NULL_IDX as its result.
+//		It should be distinguishable from any valid array index.
+//----------------------------------------------------------------------
+
+typedef int		ANNidx;					// point index
+const ANNidx	ANN_NULL_IDX = -1;		// a NULL point index
+
+//----------------------------------------------------------------------
+//	Infinite distance:
+//		The code assumes that there is an "infinite distance" which it
+//		uses to initialize distances before performing nearest neighbor
+//		searches.  It should be as larger or larger than any legitimate
+//		nearest neighbor distance.
+//
+//		On most systems, these should be found in the standard include
+//		file <limits.h> or possibly <float.h>.  If you do not have these
+//		file, some suggested values are listed below, assuming 64-bit
+//		long, 32-bit int and 16-bit short.
+//
+//		ANNdist ANN_DIST_INF	Values (see <limits.h> or <float.h>)
+//		------- ------------	------------------------------------
+//		double	DBL_MAX			1.79769313486231570e+308
+//		float	FLT_MAX			3.40282346638528860e+38
+//		long	LONG_MAX		0x7fffffffffffffff
+//		int		INT_MAX			0x7fffffff
+//		short	SHRT_MAX		0x7fff
+//----------------------------------------------------------------------
+
+const ANNdist	ANN_DIST_INF = ANN_DBL_MAX;
+
+//----------------------------------------------------------------------
+//	Significant digits for tree dumps:
+//		When floating point coordinates are used, the routine that dumps
+//		a tree needs to know roughly how many significant digits there
+//		are in a ANNcoord, so it can output points to full precision.
+//		This is defined to be ANNcoordPrec.  On most systems these
+//		values can be found in the standard include files <limits.h> or
+//		<float.h>.  For integer types, the value is essentially ignored.
+//
+//		ANNcoord ANNcoordPrec	Values (see <limits.h> or <float.h>)
+//		-------- ------------	------------------------------------
+//		double	 DBL_DIG		15
+//		float	 FLT_DIG		6
+//		long	 doesn't matter 19
+//		int		 doesn't matter 10
+//		short	 doesn't matter 5
+//----------------------------------------------------------------------
+
+#ifdef DBL_DIG							// number of sig. bits in ANNcoord
+	const int	 ANNcoordPrec	= DBL_DIG;
+#else
+	const int	 ANNcoordPrec	= 15;	// default precision
+#endif
+
+//----------------------------------------------------------------------
+// Self match?
+//	In some applications, the nearest neighbor of a point is not
+//	allowed to be the point itself. This occurs, for example, when
+//	computing all nearest neighbors in a set.  By setting the
+//	parameter ANN_ALLOW_SELF_MATCH to ANNfalse, the nearest neighbor
+//	is the closest point whose distance from the query point is
+//	strictly positive.
+//----------------------------------------------------------------------
+
+const ANNbool	ANN_ALLOW_SELF_MATCH	= ANNtrue;
+
+//----------------------------------------------------------------------
+//	Norms and metrics:
+//		ANN supports any Minkowski norm for defining distance.  In
+//		particular, for any p >= 1, the L_p Minkowski norm defines the
+//		length of a d-vector (v0, v1, ..., v(d-1)) to be
+//
+//				(|v0|^p + |v1|^p + ... + |v(d-1)|^p)^(1/p),
+//
+//		(where ^ denotes exponentiation, and |.| denotes absolute
+//		value).  The distance between two points is defined to be the
+//		norm of the vector joining them.  Some common distance metrics
+//		include
+//
+//				Euclidean metric		p = 2
+//				Manhattan metric		p = 1
+//				Max metric				p = infinity
+//
+//		In the case of the max metric, the norm is computed by taking
+//		the maxima of the absolute values of the components.  ANN is
+//		highly "coordinate-based" and does not support general distances
+//		functions (e.g. those obeying just the triangle inequality).  It
+//		also does not support distance functions based on
+//		inner-products.
+//
+//		For the purpose of computing nearest neighbors, it is not
+//		necessary to compute the final power (1/p).  Thus the only
+//		component that is used by the program is |v(i)|^p.
+//
+//		ANN parameterizes the distance computation through the following
+//		macros.  (Macros are used rather than procedures for
+//		efficiency.) Recall that the distance between two points is
+//		given by the length of the vector joining them, and the length
+//		or norm of a vector v is given by formula:
+//
+//				|v| = ROOT(POW(v0) # POW(v1) # ... # POW(v(d-1)))
+//
+//		where ROOT, POW are unary functions and # is an associative and
+//		commutative binary operator mapping the following types:
+//
+//			**	POW:	ANNcoord				--> ANNdist
+//			**	#:		ANNdist x ANNdist		--> ANNdist
+//			**	ROOT:	ANNdist (>0)			--> double
+//
+//		For early termination in distance calculation (partial distance
+//		calculation) we assume that POW and # together are monotonically
+//		increasing on sequences of arguments, meaning that for all
+//		v0..vk and y:
+//
+//		POW(v0) #...# POW(vk) <= (POW(v0) #...# POW(vk)) # POW(y).
+//
+//	Incremental Distance Calculation:
+//		The program uses an optimized method of computing distances for
+//		kd-trees and bd-trees, called incremental distance calculation.
+//		It is used when distances are to be updated when only a single
+//		coordinate of a point has been changed.  In order to use this,
+//		we assume that there is an incremental update function DIFF(x,y)
+//		for #, such that if:
+//
+//					s = x0 # ... # xi # ... # xk 
+//
+//		then if s' is equal to s but with xi replaced by y, that is, 
+//		
+//					s' = x0 # ... # y # ... # xk
+//
+//		then the length of s' can be computed by:
+//
+//					|s'| = |s| # DIFF(xi,y).
+//
+//		Thus, if # is + then DIFF(xi,y) is (yi-x).  For the L_infinity
+//		norm we make use of the fact that in the program this function
+//		is only invoked when y > xi, and hence DIFF(xi,y)=y.
+//
+//		Finally, for approximate nearest neighbor queries we assume
+//		that POW and ROOT are related such that
+//
+//					v*ROOT(x) = ROOT(POW(v)*x)
+//
+//		Here are the values for the various Minkowski norms:
+//
+//		L_p:	p even:							p odd:
+//				-------------------------		------------------------
+//				POW(v)			= v^p			POW(v)			= |v|^p
+//				ROOT(x)			= x^(1/p)		ROOT(x)			= x^(1/p)
+//				#				= +				#				= +
+//				DIFF(x,y)		= y - x			DIFF(x,y)		= y - x 
+//
+//		L_inf:
+//				POW(v)			= |v|
+//				ROOT(x)			= x
+//				#				= max
+//				DIFF(x,y)		= y
+//
+//		By default the Euclidean norm is assumed.  To change the norm,
+//		uncomment the appropriate set of macros below.
+//----------------------------------------------------------------------
+
+//----------------------------------------------------------------------
+//	Use the following for the Euclidean norm
+//----------------------------------------------------------------------
+#define ANN_POW(v)			((v)*(v))
+#define ANN_ROOT(x)			sqrt(x)
+#define ANN_SUM(x,y)		((x) + (y))
+#define ANN_DIFF(x,y)		((y) - (x))
+
+//----------------------------------------------------------------------
+//	Use the following for the L_1 (Manhattan) norm
+//----------------------------------------------------------------------
+// #define ANN_POW(v)		fabs(v)
+// #define ANN_ROOT(x)		(x)
+// #define ANN_SUM(x,y)		((x) + (y))
+// #define ANN_DIFF(x,y)	((y) - (x))
+
+//----------------------------------------------------------------------
+//	Use the following for a general L_p norm
+//----------------------------------------------------------------------
+// #define ANN_POW(v)		pow(fabs(v),p)
+// #define ANN_ROOT(x)		pow(fabs(x),1/p)
+// #define ANN_SUM(x,y)		((x) + (y))
+// #define ANN_DIFF(x,y)	((y) - (x))
+
+//----------------------------------------------------------------------
+//	Use the following for the L_infinity (Max) norm
+//----------------------------------------------------------------------
+// #define ANN_POW(v)		fabs(v)
+// #define ANN_ROOT(x)		(x)
+// #define ANN_SUM(x,y)		((x) > (y) ? (x) : (y))
+// #define ANN_DIFF(x,y)	(y)
+
+//----------------------------------------------------------------------
+//	Array types
+//		The following array types are of basic interest.  A point is
+//		just a dimensionless array of coordinates, a point array is a
+//		dimensionless array of points.  A distance array is a
+//		dimensionless array of distances and an index array is a
+//		dimensionless array of point indices.  The latter two are used
+//		when returning the results of k-nearest neighbor queries.
+//----------------------------------------------------------------------
+
+typedef ANNcoord* ANNpoint;			// a point
+typedef ANNpoint* ANNpointArray;	// an array of points 
+typedef ANNdist*  ANNdistArray;		// an array of distances 
+typedef ANNidx*   ANNidxArray;		// an array of point indices
+
+//----------------------------------------------------------------------
+//	Basic point and array utilities:
+//		The following procedures are useful supplements to ANN's nearest
+//		neighbor capabilities.
+//
+//		annDist():
+//			Computes the (squared) distance between a pair of points.
+//			Note that this routine is not used internally by ANN for
+//			computing distance calculations.  For reasons of efficiency
+//			this is done using incremental distance calculation.  Thus,
+//			this routine cannot be modified as a method of changing the
+//			metric.
+//
+//		Because points (somewhat like strings in C) are stored as
+//		pointers.  Consequently, creating and destroying copies of
+//		points may require storage allocation.  These procedures do
+//		this.
+//
+//		annAllocPt() and annDeallocPt():
+//				Allocate a deallocate storage for a single point, and
+//				return a pointer to it.  The argument to AllocPt() is
+//				used to initialize all components.
+//
+//		annAllocPts() and annDeallocPts():
+//				Allocate and deallocate an array of points as well a
+//				place to store their coordinates, and initializes the
+//				points to point to their respective coordinates.  It
+//				allocates point storage in a contiguous block large
+//				enough to store all the points.  It performs no
+//				initialization.
+//
+//		annCopyPt():
+//				Creates a copy of a given point, allocating space for
+//				the new point.  It returns a pointer to the newly
+//				allocated copy.
+//----------------------------------------------------------------------
+   
+DLL_API ANNdist annDist(
+	int				dim,		// dimension of space
+	ANNpoint		p,			// points
+	ANNpoint		q);
+
+DLL_API ANNpoint annAllocPt(
+	int				dim,		// dimension
+	ANNcoord		c = 0);		// coordinate value (all equal)
+
+DLL_API ANNpointArray annAllocPts(
+	int				n,			// number of points
+	int				dim);		// dimension
+
+DLL_API void annDeallocPt(
+	ANNpoint		&p);		// deallocate 1 point
+   
+DLL_API void annDeallocPts(
+	ANNpointArray	&pa);		// point array
+
+DLL_API ANNpoint annCopyPt(
+	int				dim,		// dimension
+	ANNpoint		source);	// point to copy
+
+//----------------------------------------------------------------------
+//Overall structure: ANN supports a number of different data structures
+//for approximate and exact nearest neighbor searching.  These are:
+//
+//		ANNbruteForce	A simple brute-force search structure.
+//		ANNkd_tree		A kd-tree tree search structure.  ANNbd_tree
+//		A bd-tree tree search structure (a kd-tree with shrink
+//		capabilities).
+//
+//		At a minimum, each of these data structures support k-nearest
+//		neighbor queries.  The nearest neighbor query, annkSearch,
+//		returns an integer identifier and the distance to the nearest
+//		neighbor(s) and annRangeSearch returns the nearest points that
+//		lie within a given query ball.
+//
+//		Each structure is built by invoking the appropriate constructor
+//		and passing it (at a minimum) the array of points, the total
+//		number of points and the dimension of the space.  Each structure
+//		is also assumed to support a destructor and member functions
+//		that return basic information about the point set.
+//
+//		Note that the array of points is not copied by the data
+//		structure (for reasons of space efficiency), and it is assumed
+//		to be constant throughout the lifetime of the search structure.
+//
+//		The search algorithm, annkSearch, is given the query point (q),
+//		and the desired number of nearest neighbors to report (k), and
+//		the error bound (eps) (whose default value is 0, implying exact
+//		nearest neighbors).  It returns two arrays which are assumed to
+//		contain at least k elements: one (nn_idx) contains the indices
+//		(within the point array) of the nearest neighbors and the other
+//		(dd) contains the squared distances to these nearest neighbors.
+//
+//		The search algorithm, annkFRSearch, is a fixed-radius kNN
+//		search.  In addition to a query point, it is given a (squared)
+//		radius bound.  (This is done for consistency, because the search
+//		returns distances as squared quantities.) It does two things.
+//		First, it computes the k nearest neighbors within the radius
+//		bound, and second, it returns the total number of points lying
+//		within the radius bound. It is permitted to set k = 0, in which
+//		case it effectively answers a range counting query.  If the
+//		error bound epsilon is positive, then the search is approximate
+//		in the sense that it is free to ignore any point that lies
+//		outside a ball of radius r/(1+epsilon), where r is the given
+//		(unsquared) radius bound.
+//
+//		The generic object from which all the search structures are
+//		dervied is given below.  It is a virtual object, and is useless
+//		by itself.
+//----------------------------------------------------------------------
+
+class DLL_API ANNpointSet {
+public:
+	virtual ~ANNpointSet() {}			// virtual distructor
+
+	virtual void annkSearch(			// approx k near neighbor search
+		ANNpoint		q,				// query point
+		int				k,				// number of near neighbors to return
+		ANNidxArray		nn_idx,			// nearest neighbor array (modified)
+		ANNdistArray	dd,				// dist to near neighbors (modified)
+		double			eps=0.0			// error bound
+		) = 0;							// pure virtual (defined elsewhere)
+
+	virtual int annkFRSearch(			// approx fixed-radius kNN search
+		ANNpoint		q,				// query point
+		ANNdist			sqRad,			// squared radius
+		int				k = 0,			// number of near neighbors to return
+		ANNidxArray		nn_idx = NULL,	// nearest neighbor array (modified)
+		ANNdistArray	dd = NULL,		// dist to near neighbors (modified)
+		double			eps=0.0			// error bound
+		) = 0;							// pure virtual (defined elsewhere)
+
+	virtual int theDim() = 0;			// return dimension of space
+	virtual int nPoints() = 0;			// return number of points
+										// return pointer to points
+	virtual ANNpointArray thePoints() = 0;
+};
+
+//----------------------------------------------------------------------
+//	Brute-force nearest neighbor search:
+//		The brute-force search structure is very simple but inefficient.
+//		It has been provided primarily for the sake of comparison with
+//		and validation of the more complex search structures.
+//
+//		Query processing is the same as described above, but the value
+//		of epsilon is ignored, since all distance calculations are
+//		performed exactly.
+//
+//		WARNING: This data structure is very slow, and should not be
+//		used unless the number of points is very small.
+//
+//		Internal information:
+//		---------------------
+//		This data structure bascially consists of the array of points
+//		(each a pointer to an array of coordinates).  The search is
+//		performed by a simple linear scan of all the points.
+//----------------------------------------------------------------------
+
+class DLL_API ANNbruteForce: public ANNpointSet {
+	int				dim;				// dimension
+	int				n_pts;				// number of points
+	ANNpointArray	pts;				// point array
+public:
+	ANNbruteForce(						// constructor from point array
+		ANNpointArray	pa,				// point array
+		int				n,				// number of points
+		int				dd);			// dimension
+
+	~ANNbruteForce();					// destructor
+
+	void annkSearch(					// approx k near neighbor search
+		ANNpoint		q,				// query point
+		int				k,				// number of near neighbors to return
+		ANNidxArray		nn_idx,			// nearest neighbor array (modified)
+		ANNdistArray	dd,				// dist to near neighbors (modified)
+		double			eps=0.0);		// error bound
+
+	int annkFRSearch(					// approx fixed-radius kNN search
+		ANNpoint		q,				// query point
+		ANNdist			sqRad,			// squared radius
+		int				k = 0,			// number of near neighbors to return
+		ANNidxArray		nn_idx = NULL,	// nearest neighbor array (modified)
+		ANNdistArray	dd = NULL,		// dist to near neighbors (modified)
+		double			eps=0.0);		// error bound
+
+	int theDim()						// return dimension of space
+		{ return dim; }
+
+	int nPoints()						// return number of points
+		{ return n_pts; }
+
+	ANNpointArray thePoints()			// return pointer to points
+		{  return pts;  }
+};
+
+//----------------------------------------------------------------------
+// kd- and bd-tree splitting and shrinking rules
+//		kd-trees supports a collection of different splitting rules.
+//		In addition to the standard kd-tree splitting rule proposed
+//		by Friedman, Bentley, and Finkel, we have introduced a
+//		number of other splitting rules, which seem to perform
+//		as well or better (for the distributions we have tested).
+//
+//		The splitting methods given below allow the user to tailor
+//		the data structure to the particular data set.  They are
+//		are described in greater details in the kd_split.cc source
+//		file.  The method ANN_KD_SUGGEST is the method chosen (rather
+//		subjectively) by the implementors as the one giving the
+//		fastest performance, and is the default splitting method.
+//
+//		As with splitting rules, there are a number of different
+//		shrinking rules.  The shrinking rule ANN_BD_NONE does no
+//		shrinking (and hence produces a kd-tree tree).  The rule
+//		ANN_BD_SUGGEST uses the implementors favorite rule.
+//----------------------------------------------------------------------
+
+enum ANNsplitRule {
+		ANN_KD_STD				= 0,	// the optimized kd-splitting rule
+		ANN_KD_MIDPT			= 1,	// midpoint split
+		ANN_KD_FAIR				= 2,	// fair split
+		ANN_KD_SL_MIDPT			= 3,	// sliding midpoint splitting method
+		ANN_KD_SL_FAIR			= 4,	// sliding fair split method
+		ANN_KD_SUGGEST			= 5};	// the authors' suggestion for best
+const int ANN_N_SPLIT_RULES		= 6;	// number of split rules
+
+enum ANNshrinkRule {
+		ANN_BD_NONE				= 0,	// no shrinking at all (just kd-tree)
+		ANN_BD_SIMPLE			= 1,	// simple splitting
+		ANN_BD_CENTROID			= 2,	// centroid splitting
+		ANN_BD_SUGGEST			= 3};	// the authors' suggested choice
+const int ANN_N_SHRINK_RULES	= 4;	// number of shrink rules
+
+//----------------------------------------------------------------------
+//	kd-tree:
+//		The main search data structure supported by ANN is a kd-tree.
+//		The main constructor is given a set of points and a choice of
+//		splitting method to use in building the tree.
+//
+//		Construction:
+//		-------------
+//		The constructor is given the point array, number of points,
+//		dimension, bucket size (default = 1), and the splitting rule
+//		(default = ANN_KD_SUGGEST).  The point array is not copied, and
+//		is assumed to be kept constant throughout the lifetime of the
+//		search structure.  There is also a "load" constructor that
+//		builds a tree from a file description that was created by the
+//		Dump operation.
+//
+//		Search:
+//		-------
+//		There are two search methods:
+//
+//			Standard search (annkSearch()):
+//				Searches nodes in tree-traversal order, always visiting
+//				the closer child first.
+//			Priority search (annkPriSearch()):
+//				Searches nodes in order of increasing distance of the
+//				associated cell from the query point.  For many
+//				distributions the standard search seems to work just
+//				fine, but priority search is safer for worst-case
+//				performance.
+//
+//		Printing:
+//		---------
+//		There are two methods provided for printing the tree.  Print()
+//		is used to produce a "human-readable" display of the tree, with
+//		indenation, which is handy for debugging.  Dump() produces a
+//		format that is suitable reading by another program.  There is a
+//		"load" constructor, which constructs a tree which is assumed to
+//		have been saved by the Dump() procedure.
+//		
+//		Performance and Structure Statistics:
+//		-------------------------------------
+//		The procedure getStats() collects statistics information on the
+//		tree (its size, height, etc.)  See ANNperf.h for information on
+//		the stats structure it returns.
+//
+//		Internal information:
+//		---------------------
+//		The data structure consists of three major chunks of storage.
+//		The first (implicit) storage are the points themselves (pts),
+//		which have been provided by the users as an argument to the
+//		constructor, or are allocated dynamically if the tree is built
+//		using the load constructor).  These should not be changed during
+//		the lifetime of the search structure.  It is the user's
+//		responsibility to delete these after the tree is destroyed.
+//
+//		The second is the tree itself (which is dynamically allocated in
+//		the constructor) and is given as a pointer to its root node
+//		(root).  These nodes are automatically deallocated when the tree
+//		is deleted.  See the file src/kd_tree.h for further information
+//		on the structure of the tree nodes.
+//
+//		Each leaf of the tree does not contain a pointer directly to a
+//		point, but rather contains a pointer to a "bucket", which is an
+//		array consisting of point indices.  The third major chunk of
+//		storage is an array (pidx), which is a large array in which all
+//		these bucket subarrays reside.  (The reason for storing them
+//		separately is the buckets are typically small, but of varying
+//		sizes.  This was done to avoid fragmentation.)  This array is
+//		also deallocated when the tree is deleted.
+//
+//		In addition to this, the tree consists of a number of other
+//		pieces of information which are used in searching and for
+//		subsequent tree operations.  These consist of the following:
+//
+//		dim						Dimension of space
+//		n_pts					Number of points currently in the tree
+//		n_max					Maximum number of points that are allowed
+//								in the tree
+//		bkt_size				Maximum bucket size (no. of points per leaf)
+//		bnd_box_lo				Bounding box low point
+//		bnd_box_hi				Bounding box high point
+//		splitRule				Splitting method used
+//
+//----------------------------------------------------------------------
+
+//----------------------------------------------------------------------
+// Some types and objects used by kd-tree functions
+// See src/kd_tree.h and src/kd_tree.cpp for definitions
+//----------------------------------------------------------------------
+class ANNkdStats;				// stats on kd-tree
+class ANNkd_node;				// generic node in a kd-tree
+typedef ANNkd_node*	ANNkd_ptr;	// pointer to a kd-tree node
+
+class DLL_API ANNkd_tree: public ANNpointSet {
+protected:
+	int				dim;				// dimension of space
+	int				n_pts;				// number of points in tree
+	int				bkt_size;			// bucket size
+	ANNpointArray	pts;				// the points
+	ANNidxArray		pidx;				// point indices (to pts array)
+	ANNkd_ptr		root;				// root of kd-tree
+	ANNpoint		bnd_box_lo;			// bounding box low point
+	ANNpoint		bnd_box_hi;			// bounding box high point
+
+	void SkeletonTree(					// construct skeleton tree
+		int				n,				// number of points
+		int				dd,				// dimension
+		int				bs,				// bucket size
+		ANNpointArray pa = NULL,		// point array (optional)
+		ANNidxArray pi = NULL);			// point indices (optional)
+
+public:
+	ANNkd_tree(							// build skeleton tree
+		int				n = 0,			// number of points
+		int				dd = 0,			// dimension
+		int				bs = 1);		// bucket size
+
+	ANNkd_tree(							// build from point array
+		ANNpointArray	pa,				// point array
+		int				n,				// number of points
+		int				dd,				// dimension
+		int				bs = 1,			// bucket size
+		ANNsplitRule	split = ANN_KD_SUGGEST);	// splitting method
+
+	ANNkd_tree(							// build from dump file
+		std::istream&	in);			// input stream for dump file
+
+	~ANNkd_tree();						// tree destructor
+
+	void annkSearch(					// approx k near neighbor search
+		ANNpoint		q,				// query point
+		int				k,				// number of near neighbors to return
+		ANNidxArray		nn_idx,			// nearest neighbor array (modified)
+		ANNdistArray	dd,				// dist to near neighbors (modified)
+		double			eps=0.0);		// error bound
+
+	void annkPriSearch( 				// priority k near neighbor search
+		ANNpoint		q,				// query point
+		int				k,				// number of near neighbors to return
+		ANNidxArray		nn_idx,			// nearest neighbor array (modified)
+		ANNdistArray	dd,				// dist to near neighbors (modified)
+		double			eps=0.0);		// error bound
+
+	int annkFRSearch(					// approx fixed-radius kNN search
+		ANNpoint		q,				// the query point
+		ANNdist			sqRad,			// squared radius of query ball
+		int				k,				// number of neighbors to return
+		ANNidxArray		nn_idx = NULL,	// nearest neighbor array (modified)
+		ANNdistArray	dd = NULL,		// dist to near neighbors (modified)
+		double			eps=0.0);		// error bound
+
+	int theDim()						// return dimension of space
+		{ return dim; }
+
+	int nPoints()						// return number of points
+		{ return n_pts; }
+
+	ANNpointArray thePoints()			// return pointer to points
+		{  return pts;  }
+
+	virtual void Print(					// print the tree (for debugging)
+		ANNbool			with_pts,		// print points as well?
+		std::ostream&	out);			// output stream
+
+	virtual void Dump(					// dump entire tree
+		ANNbool			with_pts,		// print points as well?
+		std::ostream&	out);			// output stream
+								
+	virtual void getStats(				// compute tree statistics
+		ANNkdStats&		st);			// the statistics (modified)
+};								
+
+//----------------------------------------------------------------------
+//	Box decomposition tree (bd-tree)
+//		The bd-tree is inherited from a kd-tree.  The main difference
+//		in the bd-tree and the kd-tree is a new type of internal node
+//		called a shrinking node (in the kd-tree there is only one type
+//		of internal node, a splitting node).  The shrinking node
+//		makes it possible to generate balanced trees in which the
+//		cells have bounded aspect ratio, by allowing the decomposition
+//		to zoom in on regions of dense point concentration.  Although
+//		this is a nice idea in theory, few point distributions are so
+//		densely clustered that this is really needed.
+//----------------------------------------------------------------------
+
+class DLL_API ANNbd_tree: public ANNkd_tree {
+public:
+	ANNbd_tree(							// build skeleton tree
+		int				n,				// number of points
+		int				dd,				// dimension
+		int				bs = 1)			// bucket size
+		: ANNkd_tree(n, dd, bs) {}		// build base kd-tree
+
+	ANNbd_tree(							// build from point array
+		ANNpointArray	pa,				// point array
+		int				n,				// number of points
+		int				dd,				// dimension
+		int				bs = 1,			// bucket size
+		ANNsplitRule	split  = ANN_KD_SUGGEST,	// splitting rule
+		ANNshrinkRule	shrink = ANN_BD_SUGGEST);	// shrinking rule
+
+	ANNbd_tree(							// build from dump file
+		std::istream&	in);			// input stream for dump file
+};
+
+//----------------------------------------------------------------------
+//	Other functions
+//	annMaxPtsVisit		Sets a limit on the maximum number of points
+//						to visit in the search.
+//  annClose			Can be called when all use of ANN is finished.
+//						It clears up a minor memory leak.
+//----------------------------------------------------------------------
+
+DLL_API void annMaxPtsVisit(	// max. pts to visit in search
+	int				maxPts);	// the limit
+
+DLL_API void annClose();		// called to end use of ANN
+
+#endif
diff --git a/contrib/ANN/include/ANN/ANNperf.h b/contrib/ANN/include/ANN/ANNperf.h
new file mode 100644
index 0000000000..b18c81658d
--- /dev/null
+++ b/contrib/ANN/include/ANN/ANNperf.h
@@ -0,0 +1,226 @@
+//----------------------------------------------------------------------
+//	File:			ANNperf.h
+//	Programmer:		Sunil Arya and David Mount
+//	Last modified:	03/04/98 (Release 0.1)
+//	Description:	Include file for ANN performance stats
+//
+//	Some of the code for statistics gathering has been adapted
+//	from the SmplStat.h package in the g++ library.
+//----------------------------------------------------------------------
+// Copyright (c) 1997-1998 University of Maryland and Sunil Arya and David
+// Mount.  All Rights Reserved.
+// 
+// This software and related documentation is part of the 
+// Approximate Nearest Neighbor Library (ANN).
+// 
+// Permission to use, copy, and distribute this software and its 
+// documentation is hereby granted free of charge, provided that 
+// (1) it is not a component of a commercial product, and 
+// (2) this notice appears in all copies of the software and
+//     related documentation. 
+// 
+// The University of Maryland (U.M.) and the authors make no representations
+// about the suitability or fitness of this software for any purpose.  It is
+// provided "as is" without express or implied warranty.
+//----------------------------------------------------------------------
+//      History:
+//      Revision 0.1  03/04/98
+//          Initial release
+//      Revision 1.0  04/01/05
+//          Added ANN_ prefix to avoid name conflicts.
+//----------------------------------------------------------------------
+
+#ifndef ANNperf_H
+#define ANNperf_H
+
+//----------------------------------------------------------------------
+//	basic includes
+//----------------------------------------------------------------------
+
+#include <ANN/ANN.h>					// basic ANN includes
+
+//----------------------------------------------------------------------
+// kd-tree stats object
+//	This object is used for collecting information about a kd-tree
+//	or bd-tree.
+//----------------------------------------------------------------------
+
+class ANNkdStats {			// stats on kd-tree
+public:
+	int		dim;			// dimension of space
+	int		n_pts;			// no. of points
+	int		bkt_size;		// bucket size
+	int		n_lf;			// no. of leaves (including trivial)
+	int		n_tl;			// no. of trivial leaves (no points)
+	int		n_spl;			// no. of splitting nodes
+	int		n_shr;			// no. of shrinking nodes (for bd-trees)
+	int		depth;			// depth of tree
+	float	sum_ar;			// sum of leaf aspect ratios
+	float	avg_ar;			// average leaf aspect ratio
+ //
+							// reset stats
+	void reset(int d=0, int n=0, int bs=0)
+	{
+		dim = d; n_pts = n; bkt_size = bs;
+		n_lf = n_tl = n_spl = n_shr = depth = 0;
+		sum_ar = avg_ar = 0.0;
+	}
+
+	ANNkdStats()			// basic constructor
+	{ reset(); }
+
+	void merge(const ANNkdStats &st);	// merge stats from child 
+};
+
+//----------------------------------------------------------------------
+//  ANNsampStat
+//	A sample stat collects numeric (double) samples and returns some
+//	simple statistics.  Its main functions are:
+//
+//		reset()		Reset to no samples.
+//		+= x		Include sample x.
+//		samples()	Return number of samples.
+//		mean()		Return mean of samples.
+//		stdDev()	Return standard deviation
+//		min()		Return minimum of samples.
+//		max()		Return maximum of samples.
+//----------------------------------------------------------------------
+class DLL_API ANNsampStat {
+	int				n;				// number of samples
+	double			sum;			// sum
+	double			sum2;			// sum of squares
+	double			minVal, maxVal;	// min and max
+public :
+	void reset()				// reset everything
+	{  
+		n = 0;
+		sum = sum2 = 0;
+		minVal = ANN_DBL_MAX;
+		maxVal = -ANN_DBL_MAX; 
+	}
+
+	ANNsampStat() { reset(); }		// constructor
+
+	void operator+=(double x)		// add sample
+	{
+		n++;  sum += x;  sum2 += x*x;
+		if (x < minVal) minVal = x;
+		if (x > maxVal) maxVal = x;
+	}
+
+	int samples() { return n; }		// number of samples
+
+	double mean() { return sum/n; } // mean
+
+									// standard deviation
+	double stdDev() { return sqrt((sum2 - (sum*sum)/n)/(n-1));}
+
+	double min() { return minVal; } // minimum
+	double max() { return maxVal; } // maximum
+};
+
+//----------------------------------------------------------------------
+//		Operation count updates
+//----------------------------------------------------------------------
+
+#ifdef ANN_PERF
+  #define ANN_FLOP(n)	{ann_Nfloat_ops += (n);}
+  #define ANN_LEAF(n)	{ann_Nvisit_lfs += (n);}
+  #define ANN_SPL(n)	{ann_Nvisit_spl += (n);}
+  #define ANN_SHR(n)	{ann_Nvisit_shr += (n);}
+  #define ANN_PTS(n)	{ann_Nvisit_pts += (n);}
+  #define ANN_COORD(n)	{ann_Ncoord_hts += (n);}
+#else
+  #define ANN_FLOP(n)
+  #define ANN_LEAF(n)
+  #define ANN_SPL(n)
+  #define ANN_SHR(n)
+  #define ANN_PTS(n)
+  #define ANN_COORD(n)
+#endif
+
+//----------------------------------------------------------------------
+//	Performance statistics
+//	The following data and routines are used for computing performance
+//	statistics for nearest neighbor searching.  Because these routines
+//	can slow the code down, they can be activated and deactiviated by
+//	defining the ANN_PERF variable, by compiling with the option:
+//	-DANN_PERF
+//----------------------------------------------------------------------
+
+//----------------------------------------------------------------------
+//	Global counters for performance measurement
+//
+//	visit_lfs	The number of leaf nodes visited in the
+//				tree.
+//
+//	visit_spl	The number of splitting nodes visited in the
+//				tree.
+//
+//	visit_shr	The number of shrinking nodes visited in the
+//				tree.
+//
+//	visit_pts	The number of points visited in all the
+//				leaf nodes visited. Equivalently, this
+//				is the number of points for which distance
+//				calculations are performed.
+//
+//	coord_hts	The number of times a coordinate of a 
+//				data point is accessed. This is generally
+//				less than visit_pts*d if partial distance
+//				calculation is used.  This count is low
+//				in the sense that if a coordinate is hit
+//				many times in the same routine we may
+//				count it only once.
+//
+//	float_ops	The number of floating point operations.
+//				This includes all operations in the heap
+//				as well as distance calculations to boxes.
+//
+//	average_err	The average error of each query (the
+//				error of the reported point to the true
+//				nearest neighbor).  For k nearest neighbors
+//				the error is computed k times.
+//
+//	rank_err	The rank error of each query (the difference
+//				in the rank of the reported point and its
+//				true rank).
+//
+//	data_pts	The number of data points.  This is not
+//				a counter, but used in stats computation.
+//----------------------------------------------------------------------
+
+extern int			ann_Ndata_pts;	// number of data points
+extern int			ann_Nvisit_lfs;	// number of leaf nodes visited
+extern int			ann_Nvisit_spl;	// number of splitting nodes visited
+extern int			ann_Nvisit_shr;	// number of shrinking nodes visited
+extern int			ann_Nvisit_pts;	// visited points for one query
+extern int			ann_Ncoord_hts;	// coordinate hits for one query
+extern int			ann_Nfloat_ops;	// floating ops for one query
+extern ANNsampStat	ann_visit_lfs;	// stats on leaf nodes visits
+extern ANNsampStat	ann_visit_spl;	// stats on splitting nodes visits
+extern ANNsampStat	ann_visit_shr;	// stats on shrinking nodes visits
+extern ANNsampStat	ann_visit_nds;	// stats on total nodes visits
+extern ANNsampStat	ann_visit_pts;	// stats on points visited
+extern ANNsampStat	ann_coord_hts;	// stats on coordinate hits
+extern ANNsampStat	ann_float_ops;	// stats on floating ops
+//----------------------------------------------------------------------
+//  The following need to be part of the public interface, because
+//  they are accessed outside the DLL in ann_test.cpp.
+//----------------------------------------------------------------------
+DLL_API extern ANNsampStat ann_average_err;	// average error
+DLL_API extern ANNsampStat ann_rank_err;	// rank error
+
+//----------------------------------------------------------------------
+//	Declaration of externally accessible routines for statistics
+//----------------------------------------------------------------------
+
+DLL_API void annResetStats(int data_size);	// reset stats for a set of queries
+
+DLL_API void annResetCounts();				// reset counts for one queries
+
+DLL_API void annUpdateStats();				// update stats with current counts
+
+DLL_API void annPrintStats(ANNbool validate); // print statistics for a run
+
+#endif
diff --git a/contrib/ANN/include/ANN/ANNx.h b/contrib/ANN/include/ANN/ANNx.h
new file mode 100644
index 0000000000..38b07b76fa
--- /dev/null
+++ b/contrib/ANN/include/ANN/ANNx.h
@@ -0,0 +1,170 @@
+//----------------------------------------------------------------------
+//	File:			ANNx.h
+//	Programmer: 	Sunil Arya and David Mount
+//	Last modified:	03/04/98 (Release 0.1)
+//	Description:	Internal include file for ANN
+//
+//	These declarations are of use in manipulating some of
+//	the internal data objects appearing in ANN, but are not
+//	needed for applications just using the nearest neighbor
+//	search.
+//
+//	Typical users of ANN should not need to access this file.
+//----------------------------------------------------------------------
+// Copyright (c) 1997-1998 University of Maryland and Sunil Arya and David
+// Mount.  All Rights Reserved.
+// 
+// This software and related documentation is part of the 
+// Approximate Nearest Neighbor Library (ANN).
+// 
+// Permission to use, copy, and distribute this software and its 
+// documentation is hereby granted free of charge, provided that 
+// (1) it is not a component of a commercial product, and 
+// (2) this notice appears in all copies of the software and
+//   related documentation. 
+// 
+// The University of Maryland (U.M.) and the authors make no representations
+// about the suitability or fitness of this software for any purpose.  It is
+// provided "as is" without express or implied warranty.
+//----------------------------------------------------------------------
+//	History:
+//	Revision 0.1  03/04/98
+//	    Initial release
+//	Revision 1.0  04/01/05
+//	    Changed LO, HI, IN, OUT to ANN_LO, ANN_HI, etc.
+//----------------------------------------------------------------------
+
+#ifndef ANNx_H
+#define ANNx_H
+
+#include <iomanip>				// I/O manipulators
+#include <ANN/ANN.h>			// ANN includes
+
+//----------------------------------------------------------------------
+//	Global constants and types
+//----------------------------------------------------------------------
+enum	{ANN_LO=0, ANN_HI=1};	// splitting indices
+enum	{ANN_IN=0, ANN_OUT=1};	// shrinking indices
+								// what to do in case of error
+enum ANNerr {ANNwarn = 0, ANNabort = 1};
+
+//----------------------------------------------------------------------
+//	Maximum number of points to visit
+//	We have an option for terminating the search early if the
+//	number of points visited exceeds some threshold.  If the
+//	threshold is 0 (its default)  this means there is no limit
+//	and the algorithm applies its normal termination condition.
+//----------------------------------------------------------------------
+
+extern int		ANNmaxPtsVisited;	// maximum number of pts visited
+extern int		ANNptsVisited;		// number of pts visited in search
+
+//----------------------------------------------------------------------
+//	Global function declarations
+//----------------------------------------------------------------------
+
+void annError(					// ANN error routine
+	char			*msg,		// error message
+	ANNerr			level);		// level of error
+
+void annPrintPt(				// print a point
+	ANNpoint		pt,			// the point
+	int				dim,		// the dimension
+	std::ostream	&out);		// output stream
+
+//----------------------------------------------------------------------
+//	Orthogonal (axis aligned) rectangle
+//	Orthogonal rectangles are represented by two points, one
+//	for the lower left corner (min coordinates) and the other
+//	for the upper right corner (max coordinates).
+//
+//	The constructor initializes from either a pair of coordinates,
+//	pair of points, or another rectangle.  Note that all constructors
+//	allocate new point storage. The destructor deallocates this
+//	storage.
+//
+//	BEWARE: Orthogonal rectangles should be passed ONLY BY REFERENCE.
+//	(C++'s default copy constructor will not allocate new point
+//	storage, then on return the destructor free's storage, and then
+//	you get into big trouble in the calling procedure.)
+//----------------------------------------------------------------------
+
+class ANNorthRect {
+public:
+	ANNpoint		lo;			// rectangle lower bounds
+	ANNpoint		hi;			// rectangle upper bounds
+//
+	ANNorthRect(				// basic constructor
+	int				dd,			// dimension of space
+	ANNcoord		l=0,		// default is empty
+	ANNcoord		h=0)
+	{  lo = annAllocPt(dd, l);  hi = annAllocPt(dd, h); }
+
+	ANNorthRect(				// (almost a) copy constructor
+	int				dd,			// dimension
+	const			ANNorthRect &r) // rectangle to copy
+	{  lo = annCopyPt(dd, r.lo);  hi = annCopyPt(dd, r.hi);  }
+
+	ANNorthRect(				// construct from points
+	int				dd,			// dimension
+	ANNpoint		l,			// low point
+	ANNpoint		h)			// hight point
+	{  lo = annCopyPt(dd, l);  hi = annCopyPt(dd, h);  }
+
+	~ANNorthRect()				// destructor
+    {  annDeallocPt(lo);  annDeallocPt(hi);  }
+
+	ANNbool inside(int dim, ANNpoint p);// is point p inside rectangle?
+};
+
+void annAssignRect(				// assign one rect to another
+	int				dim,		// dimension (both must be same)
+	ANNorthRect		&dest,		// destination (modified)
+	const ANNorthRect &source);	// source
+
+//----------------------------------------------------------------------
+//	Orthogonal (axis aligned) halfspace
+//	An orthogonal halfspace is represented by an integer cutting
+//	dimension cd, coordinate cutting value, cv, and side, sd, which is
+//	either +1 or -1. Our convention is that point q lies in the (closed)
+//	halfspace if (q[cd] - cv)*sd >= 0.
+//----------------------------------------------------------------------
+
+class ANNorthHalfSpace {
+public:
+	int				cd;			// cutting dimension
+	ANNcoord		cv;			// cutting value
+	int				sd;			// which side
+//
+	ANNorthHalfSpace()			// default constructor
+	{  cd = 0; cv = 0;  sd = 0;  }
+
+	ANNorthHalfSpace(			// basic constructor
+	int				cdd,		// dimension of space
+	ANNcoord		cvv,		// cutting value
+	int				sdd)		// side
+	{  cd = cdd;  cv = cvv;  sd = sdd;  }
+
+	ANNbool in(ANNpoint q) const	// is q inside halfspace?
+	{  return  (ANNbool) ((q[cd] - cv)*sd >= 0);  }
+
+	ANNbool out(ANNpoint q) const	// is q outside halfspace?
+	{  return  (ANNbool) ((q[cd] - cv)*sd < 0);  }
+
+	ANNdist dist(ANNpoint q) const	// (squared) distance from q
+	{  return  (ANNdist) ANN_POW(q[cd] - cv);  }
+
+	void setLowerBound(int d, ANNpoint p)// set to lower bound at p[i]
+	{  cd = d;  cv = p[d];  sd = +1;  }
+
+	void setUpperBound(int d, ANNpoint p)// set to upper bound at p[i]
+	{  cd = d;  cv = p[d];  sd = -1;  }
+
+	void project(ANNpoint &q)		// project q (modified) onto halfspace
+	{  if (out(q)) q[cd] = cv;  }
+};
+
+								// array of halfspaces
+typedef ANNorthHalfSpace *ANNorthHSArray;
+
+#endif
diff --git a/contrib/ANN/src/ANN.cpp b/contrib/ANN/src/ANN.cpp
new file mode 100644
index 0000000000..82f90f54af
--- /dev/null
+++ b/contrib/ANN/src/ANN.cpp
@@ -0,0 +1,198 @@
+//----------------------------------------------------------------------
+// File:			ANN.cpp
+// Programmer:		Sunil Arya and David Mount
+// Description:		Methods for ANN.h and ANNx.h
+// Last modified:	01/04/05 (Version 1.0)
+//----------------------------------------------------------------------
+// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
+// David Mount.  All Rights Reserved.
+// 
+// This software and related documentation is part of the Approximate
+// Nearest Neighbor Library (ANN).  This software is provided under
+// the provisions of the Lesser GNU Public License (LGPL).  See the
+// file ../ReadMe.txt for further information.
+// 
+// The University of Maryland (U.M.) and the authors make no
+// representations about the suitability or fitness of this software for
+// any purpose.  It is provided "as is" without express or implied
+// warranty.
+//----------------------------------------------------------------------
+// History:
+//	Revision 0.1  03/04/98
+//		Initial release
+//	Revision 1.0  04/01/05
+//		Added performance counting to annDist()
+//----------------------------------------------------------------------
+
+#include <ANN/ANNx.h>					// all ANN includes
+#include <ANN/ANNperf.h>				// ANN performance 
+
+using namespace std;					// make std:: accessible
+
+//----------------------------------------------------------------------
+//	Point methods
+//----------------------------------------------------------------------
+
+//----------------------------------------------------------------------
+//	Distance utility.
+//		(Note: In the nearest neighbor search, most distances are
+//		computed using partial distance calculations, not this
+//		procedure.)
+//----------------------------------------------------------------------
+
+ANNdist annDist(						// interpoint squared distance
+	int					dim,
+	ANNpoint			p,
+	ANNpoint			q)
+{
+	register int d;
+	register ANNcoord diff;
+	register ANNcoord dist;
+
+	dist = 0;
+	for (d = 0; d < dim; d++) {
+		diff = p[d] - q[d];
+		dist = ANN_SUM(dist, ANN_POW(diff));
+	}
+	ANN_FLOP(3*dim)					// performance counts
+	ANN_PTS(1)
+	ANN_COORD(dim)
+	return dist;
+}
+
+//----------------------------------------------------------------------
+//	annPrintPoint() prints a point to a given output stream.
+//----------------------------------------------------------------------
+
+void annPrintPt(						// print a point
+	ANNpoint			pt,				// the point
+	int					dim,			// the dimension
+	std::ostream		&out)			// output stream
+{
+	for (int j = 0; j < dim; j++) {
+		out << pt[j];
+		if (j < dim-1) out << " ";
+	}
+}
+
+//----------------------------------------------------------------------
+//	Point allocation/deallocation:
+//
+//		Because points (somewhat like strings in C) are stored
+//		as pointers.  Consequently, creating and destroying
+//		copies of points may require storage allocation.  These
+//		procedures do this.
+//
+//		annAllocPt() and annDeallocPt() allocate a deallocate
+//		storage for a single point, and return a pointer to it.
+//
+//		annAllocPts() allocates an array of points as well a place
+//		to store their coordinates, and initializes the points to
+//		point to their respective coordinates.  It allocates point
+//		storage in a contiguous block large enough to store all the
+//		points.  It performs no initialization.
+//
+//		annDeallocPts() should only be used on point arrays allocated
+//		by annAllocPts since it assumes that points are allocated in
+//		a block.
+//
+//		annCopyPt() copies a point taking care to allocate storage
+//		for the new point.
+//
+//		annAssignRect() assigns the coordinates of one rectangle to
+//		another.  The two rectangles must have the same dimension
+//		(and it is not possible to test this here).
+//----------------------------------------------------------------------
+
+ANNpoint annAllocPt(int dim, ANNcoord c)		// allocate 1 point
+{
+	ANNpoint p = new ANNcoord[dim];
+	for (int i = 0; i < dim; i++) p[i] = c;
+	return p;
+}
+   
+ANNpointArray annAllocPts(int n, int dim)		// allocate n pts in dim
+{
+	ANNpointArray pa = new ANNpoint[n];			// allocate points
+	ANNpoint	  p  = new ANNcoord[n*dim];		// allocate space for coords
+	for (int i = 0; i < n; i++) {
+		pa[i] = &(p[i*dim]);
+	}
+	return pa;
+}
+
+void annDeallocPt(ANNpoint &p)					// deallocate 1 point
+{
+	delete [] p;
+	p = NULL;
+}
+   
+void annDeallocPts(ANNpointArray &pa)			// deallocate points
+{
+	delete [] pa[0];							// dealloc coordinate storage
+	delete [] pa;								// dealloc points
+	pa = NULL;
+}
+   
+ANNpoint annCopyPt(int dim, ANNpoint source)	// copy point
+{
+	ANNpoint p = new ANNcoord[dim];
+	for (int i = 0; i < dim; i++) p[i] = source[i];
+	return p;
+}
+   
+												// assign one rect to another
+void annAssignRect(int dim, ANNorthRect &dest, const ANNorthRect &source)
+{
+	for (int i = 0; i < dim; i++) {
+		dest.lo[i] = source.lo[i];
+		dest.hi[i] = source.hi[i];
+	}
+}
+
+												// is point inside rectangle?
+ANNbool ANNorthRect::inside(int dim, ANNpoint p)
+{
+	for (int i = 0; i < dim; i++) {
+		if (p[i] < lo[i] || p[i] > hi[i]) return ANNfalse;
+	}
+	return ANNtrue;
+}
+
+//----------------------------------------------------------------------
+//	Error handler
+//----------------------------------------------------------------------
+
+void annError(char *msg, ANNerr level)
+{
+	if (level == ANNabort) {
+		cerr << "ANN: ERROR------->" << msg << "<-------------ERROR\n";
+		exit(1);
+	}
+	else {
+		cerr << "ANN: WARNING----->" << msg << "<-------------WARNING\n";
+	}
+}
+
+//----------------------------------------------------------------------
+//	Limit on number of points visited
+//		We have an option for terminating the search early if the
+//		number of points visited exceeds some threshold.  If the
+//		threshold is 0 (its default)  this means there is no limit
+//		and the algorithm applies its normal termination condition.
+//		This is for applications where there are real time constraints
+//		on the running time of the algorithm.
+//----------------------------------------------------------------------
+
+int	ANNmaxPtsVisited = 0;	// maximum number of pts visited
+int	ANNptsVisited;			// number of pts visited in search
+
+//----------------------------------------------------------------------
+//	Global function declarations
+//----------------------------------------------------------------------
+
+void annMaxPtsVisit(			// set limit on max. pts to visit in search
+	int					maxPts)			// the limit
+{
+	ANNmaxPtsVisited = maxPts;
+}
diff --git a/contrib/ANN/src/bd_fix_rad_search.cpp b/contrib/ANN/src/bd_fix_rad_search.cpp
new file mode 100644
index 0000000000..dea3f6bdf1
--- /dev/null
+++ b/contrib/ANN/src/bd_fix_rad_search.cpp
@@ -0,0 +1,61 @@
+//----------------------------------------------------------------------
+// File:			bd_fix_rad_search.cpp
+// Programmer:		David Mount
+// Description:		Standard bd-tree search
+// Last modified:	05/03/05 (Version 1.1)
+//----------------------------------------------------------------------
+// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
+// David Mount.  All Rights Reserved.
+// 
+// This software and related documentation is part of the Approximate
+// Nearest Neighbor Library (ANN).  This software is provided under
+// the provisions of the Lesser GNU Public License (LGPL).  See the
+// file ../ReadMe.txt for further information.
+// 
+// The University of Maryland (U.M.) and the authors make no
+// representations about the suitability or fitness of this software for
+// any purpose.  It is provided "as is" without express or implied
+// warranty.
+//----------------------------------------------------------------------
+// History:
+//	Revision 1.1  05/03/05
+//		Initial release
+//----------------------------------------------------------------------
+
+#include "bd_tree.h"					// bd-tree declarations
+#include "kd_fix_rad_search.h"			// kd-tree FR search declarations
+
+//----------------------------------------------------------------------
+//	Approximate searching for bd-trees.
+//		See the file kd_FR_search.cpp for general information on the
+//		approximate nearest neighbor search algorithm.  Here we
+//		include the extensions for shrinking nodes.
+//----------------------------------------------------------------------
+
+//----------------------------------------------------------------------
+//	bd_shrink::ann_FR_search - search a shrinking node
+//----------------------------------------------------------------------
+
+void ANNbd_shrink::ann_FR_search(ANNdist box_dist)
+{
+												// check dist calc term cond.
+	if (ANNmaxPtsVisited != 0 && ANNptsVisited > ANNmaxPtsVisited) return;
+
+	ANNdist inner_dist = 0;						// distance to inner box
+	for (int i = 0; i < n_bnds; i++) {			// is query point in the box?
+		if (bnds[i].out(ANNkdFRQ)) {			// outside this bounding side?
+												// add to inner distance
+			inner_dist = (ANNdist) ANN_SUM(inner_dist, bnds[i].dist(ANNkdFRQ));
+		}
+	}
+	if (inner_dist <= box_dist) {				// if inner box is closer
+		child[ANN_IN]->ann_FR_search(inner_dist);// search inner child first
+		child[ANN_OUT]->ann_FR_search(box_dist);// ...then outer child
+	}
+	else {										// if outer box is closer
+		child[ANN_OUT]->ann_FR_search(box_dist);// search outer child first
+		child[ANN_IN]->ann_FR_search(inner_dist);// ...then outer child
+	}
+	ANN_FLOP(3*n_bnds)							// increment floating ops
+	ANN_SHR(1)									// one more shrinking node
+}
diff --git a/contrib/ANN/src/bd_pr_search.cpp b/contrib/ANN/src/bd_pr_search.cpp
new file mode 100644
index 0000000000..d16d632945
--- /dev/null
+++ b/contrib/ANN/src/bd_pr_search.cpp
@@ -0,0 +1,62 @@
+//----------------------------------------------------------------------
+// File:			bd_pr_search.cpp
+// Programmer:		David Mount
+// Description:		Priority search for bd-trees
+// Last modified:	01/04/05 (Version 1.0)
+//----------------------------------------------------------------------
+// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
+// David Mount.  All Rights Reserved.
+// 
+// This software and related documentation is part of the Approximate
+// Nearest Neighbor Library (ANN).  This software is provided under
+// the provisions of the Lesser GNU Public License (LGPL).  See the
+// file ../ReadMe.txt for further information.
+// 
+// The University of Maryland (U.M.) and the authors make no
+// representations about the suitability or fitness of this software for
+// any purpose.  It is provided "as is" without express or implied
+// warranty.
+//----------------------------------------------------------------------
+//History:
+//	Revision 0.1  03/04/98
+//		Initial release
+//----------------------------------------------------------------------
+
+#include "bd_tree.h"					// bd-tree declarations
+#include "kd_pr_search.h"				// kd priority search declarations
+
+//----------------------------------------------------------------------
+//	Approximate priority searching for bd-trees.
+//		See the file kd_pr_search.cc for general information on the
+//		approximate nearest neighbor priority search algorithm.  Here
+//		we include the extensions for shrinking nodes.
+//----------------------------------------------------------------------
+
+//----------------------------------------------------------------------
+//	bd_shrink::ann_search - search a shrinking node
+//----------------------------------------------------------------------
+
+void ANNbd_shrink::ann_pri_search(ANNdist box_dist)
+{
+	ANNdist inner_dist = 0;						// distance to inner box
+	for (int i = 0; i < n_bnds; i++) {			// is query point in the box?
+		if (bnds[i].out(ANNprQ)) {				// outside this bounding side?
+												// add to inner distance
+			inner_dist = (ANNdist) ANN_SUM(inner_dist, bnds[i].dist(ANNprQ));
+		}
+	}
+	if (inner_dist <= box_dist) {				// if inner box is closer
+		if (child[ANN_OUT] != KD_TRIVIAL)		// enqueue outer if not trivial
+			ANNprBoxPQ->insert(box_dist,child[ANN_OUT]);
+												// continue with inner child
+		child[ANN_IN]->ann_pri_search(inner_dist);
+	}
+	else {										// if outer box is closer
+		if (child[ANN_IN] != KD_TRIVIAL)		// enqueue inner if not trivial
+			ANNprBoxPQ->insert(inner_dist,child[ANN_IN]);
+												// continue with outer child
+		child[ANN_OUT]->ann_pri_search(box_dist);
+	}
+	ANN_FLOP(3*n_bnds)							// increment floating ops
+	ANN_SHR(1)									// one more shrinking node
+}
diff --git a/contrib/ANN/src/bd_search.cpp b/contrib/ANN/src/bd_search.cpp
new file mode 100644
index 0000000000..f057018a28
--- /dev/null
+++ b/contrib/ANN/src/bd_search.cpp
@@ -0,0 +1,61 @@
+//----------------------------------------------------------------------
+// File:			bd_search.cpp
+// Programmer:		David Mount
+// Description:		Standard bd-tree search
+// Last modified:	01/04/05 (Version 1.0)
+//----------------------------------------------------------------------
+// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
+// David Mount.  All Rights Reserved.
+// 
+// This software and related documentation is part of the Approximate
+// Nearest Neighbor Library (ANN).  This software is provided under
+// the provisions of the Lesser GNU Public License (LGPL).  See the
+// file ../ReadMe.txt for further information.
+// 
+// The University of Maryland (U.M.) and the authors make no
+// representations about the suitability or fitness of this software for
+// any purpose.  It is provided "as is" without express or implied
+// warranty.
+//----------------------------------------------------------------------
+// History:
+//	Revision 0.1  03/04/98
+//		Initial release
+//----------------------------------------------------------------------
+
+#include "bd_tree.h"					// bd-tree declarations
+#include "kd_search.h"					// kd-tree search declarations
+
+//----------------------------------------------------------------------
+//	Approximate searching for bd-trees.
+//		See the file kd_search.cpp for general information on the
+//		approximate nearest neighbor search algorithm.  Here we
+//		include the extensions for shrinking nodes.
+//----------------------------------------------------------------------
+
+//----------------------------------------------------------------------
+//	bd_shrink::ann_search - search a shrinking node
+//----------------------------------------------------------------------
+
+void ANNbd_shrink::ann_search(ANNdist box_dist)
+{
+												// check dist calc term cond.
+	if (ANNmaxPtsVisited != 0 && ANNptsVisited > ANNmaxPtsVisited) return;
+
+	ANNdist inner_dist = 0;						// distance to inner box
+	for (int i = 0; i < n_bnds; i++) {			// is query point in the box?
+		if (bnds[i].out(ANNkdQ)) {				// outside this bounding side?
+												// add to inner distance
+			inner_dist = (ANNdist) ANN_SUM(inner_dist, bnds[i].dist(ANNkdQ));
+		}
+	}
+	if (inner_dist <= box_dist) {				// if inner box is closer
+		child[ANN_IN]->ann_search(inner_dist);	// search inner child first
+		child[ANN_OUT]->ann_search(box_dist);	// ...then outer child
+	}
+	else {										// if outer box is closer
+		child[ANN_OUT]->ann_search(box_dist);	// search outer child first
+		child[ANN_IN]->ann_search(inner_dist);	// ...then outer child
+	}
+	ANN_FLOP(3*n_bnds)							// increment floating ops
+	ANN_SHR(1)									// one more shrinking node
+}
diff --git a/contrib/ANN/src/bd_tree.cpp b/contrib/ANN/src/bd_tree.cpp
new file mode 100644
index 0000000000..0977dea96f
--- /dev/null
+++ b/contrib/ANN/src/bd_tree.cpp
@@ -0,0 +1,417 @@
+//----------------------------------------------------------------------
+// File:			bd_tree.cpp
+// Programmer:		David Mount
+// Description:		Basic methods for bd-trees.
+// Last modified:	01/04/05 (Version 1.0)
+//----------------------------------------------------------------------
+// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
+// David Mount.  All Rights Reserved.
+// 
+// This software and related documentation is part of the Approximate
+// Nearest Neighbor Library (ANN).  This software is provided under
+// the provisions of the Lesser GNU Public License (LGPL).  See the
+// file ../ReadMe.txt for further information.
+// 
+// The University of Maryland (U.M.) and the authors make no
+// representations about the suitability or fitness of this software for
+// any purpose.  It is provided "as is" without express or implied
+// warranty.
+//----------------------------------------------------------------------
+// History:
+//	Revision 0.1  03/04/98
+//		Initial release
+//	Revision l.0  04/01/05
+//		Fixed centroid shrink threshold condition to depend on the
+//			dimension.
+//		Moved dump routine to kd_dump.cpp.
+//----------------------------------------------------------------------
+
+#include "bd_tree.h"					// bd-tree declarations
+#include "kd_util.h"					// kd-tree utilities
+#include "kd_split.h"					// kd-tree splitting rules
+
+#include <ANN/ANNperf.h>				// performance evaluation
+
+//----------------------------------------------------------------------
+//	Printing a bd-tree 
+//		These routines print a bd-tree.   See the analogous procedure
+//		in kd_tree.cpp for more information.
+//----------------------------------------------------------------------
+
+void ANNbd_shrink::print(				// print shrinking node
+		int level,						// depth of node in tree
+		ostream &out)					// output stream
+{
+	child[ANN_OUT]->print(level+1, out);		// print out-child
+
+	out << "    ";
+	for (int i = 0; i < level; i++)				// print indentation
+		out << "..";
+	out << "Shrink";
+	for (int j = 0; j < n_bnds; j++) {			// print sides, 2 per line
+		if (j % 2 == 0) {
+			out << "\n";						// newline and indentation
+			for (int i = 0; i < level+2; i++) out << "  ";
+		}
+		out << "  ([" << bnds[j].cd << "]"
+			 << (bnds[j].sd > 0 ? ">=" : "< ")
+			 << bnds[j].cv << ")";
+	}
+	out << "\n";
+
+	child[ANN_IN]->print(level+1, out);			// print in-child
+}
+
+//----------------------------------------------------------------------
+//	kd_tree statistics utility (for performance evaluation)
+//		This routine computes various statistics information for
+//		shrinking nodes.  See file kd_tree.cpp for more information.
+//----------------------------------------------------------------------
+
+void ANNbd_shrink::getStats(					// get subtree statistics
+	int					dim,					// dimension of space
+	ANNkdStats			&st,					// stats (modified)
+	ANNorthRect			&bnd_box)				// bounding box
+{
+	ANNkdStats ch_stats;						// stats for children
+	ANNorthRect inner_box(dim);					// inner box of shrink
+
+	annBnds2Box(bnd_box,						// enclosing box
+				dim,							// dimension
+				n_bnds,							// number of bounds
+				bnds,							// bounds array
+				inner_box);						// inner box (modified)
+												// get stats for inner child
+	ch_stats.reset();							// reset
+	child[ANN_IN]->getStats(dim, ch_stats, inner_box);
+	st.merge(ch_stats);							// merge them
+												// get stats for outer child
+	ch_stats.reset();							// reset
+	child[ANN_OUT]->getStats(dim, ch_stats, bnd_box);
+	st.merge(ch_stats);							// merge them
+
+	st.depth++;									// increment depth
+	st.n_shr++;									// increment number of shrinks
+}
+
+//----------------------------------------------------------------------
+// bd-tree constructor
+//		This is the main constructor for bd-trees given a set of points.
+//		It first builds a skeleton kd-tree as a basis, then computes the
+//		bounding box of the data points, and then invokes rbd_tree() to
+//		actually build the tree, passing it the appropriate splitting
+//		and shrinking information.
+//----------------------------------------------------------------------
+
+ANNkd_ptr rbd_tree(						// recursive construction of bd-tree
+	ANNpointArray		pa,				// point array
+	ANNidxArray			pidx,			// point indices to store in subtree
+	int					n,				// number of points
+	int					dim,			// dimension of space
+	int					bsp,			// bucket space
+	ANNorthRect			&bnd_box,		// bounding box for current node
+	ANNkd_splitter		splitter,		// splitting routine
+	ANNshrinkRule		shrink);		// shrinking rule
+
+ANNbd_tree::ANNbd_tree(					// construct from point array
+	ANNpointArray		pa,				// point array (with at least n pts)
+	int					n,				// number of points
+	int					dd,				// dimension
+	int					bs,				// bucket size
+	ANNsplitRule		split,			// splitting rule
+	ANNshrinkRule		shrink)			// shrinking rule
+	: ANNkd_tree(n, dd, bs)				// build skeleton base tree
+{
+	pts = pa;							// where the points are
+	if (n == 0) return;					// no points--no sweat
+
+	ANNorthRect bnd_box(dd);			// bounding box for points
+										// construct bounding rectangle
+	annEnclRect(pa, pidx, n, dd, bnd_box);
+										// copy to tree structure
+	bnd_box_lo = annCopyPt(dd, bnd_box.lo);
+	bnd_box_hi = annCopyPt(dd, bnd_box.hi);
+
+	switch (split) {					// build by rule
+	case ANN_KD_STD:					// standard kd-splitting rule
+		root = rbd_tree(pa, pidx, n, dd, bs, bnd_box, kd_split, shrink);
+		break;
+	case ANN_KD_MIDPT:					// midpoint split
+		root = rbd_tree(pa, pidx, n, dd, bs, bnd_box, midpt_split, shrink);
+		break;
+	case ANN_KD_SUGGEST:				// best (in our opinion)
+	case ANN_KD_SL_MIDPT:				// sliding midpoint split
+		root = rbd_tree(pa, pidx, n, dd, bs, bnd_box, sl_midpt_split, shrink);
+		break;
+	case ANN_KD_FAIR:					// fair split
+		root = rbd_tree(pa, pidx, n, dd, bs, bnd_box, fair_split, shrink);
+		break;
+	case ANN_KD_SL_FAIR:				// sliding fair split
+		root = rbd_tree(pa, pidx, n, dd, bs,
+						bnd_box, sl_fair_split, shrink);
+		break;
+	default:
+		annError("Illegal splitting method", ANNabort);
+	}
+}
+
+//----------------------------------------------------------------------
+//	Shrinking rules
+//----------------------------------------------------------------------
+
+enum ANNdecomp {SPLIT, SHRINK};			// decomposition methods
+
+//----------------------------------------------------------------------
+//	trySimpleShrink - Attempt a simple shrink
+//
+//		We compute the tight bounding box of the points, and compute
+//		the 2*dim ``gaps'' between the sides of the tight box and the
+//		bounding box.  If any of the gaps is large enough relative to
+//		the longest side of the tight bounding box, then we shrink
+//		all sides whose gaps are large enough.  (The reason for
+//		comparing against the tight bounding box, is that after
+//		shrinking the longest box size will decrease, and if we use
+//		the standard bounding box, we may decide to shrink twice in
+//		a row.  Since the tight box is fixed, we cannot shrink twice
+//		consecutively.)
+//----------------------------------------------------------------------
+const float BD_GAP_THRESH = 0.5;		// gap threshold (must be < 1)
+const int   BD_CT_THRESH  = 2;			// min number of shrink sides
+
+ANNdecomp trySimpleShrink(				// try a simple shrink
+	ANNpointArray		pa,				// point array
+	ANNidxArray			pidx,			// point indices to store in subtree
+	int					n,				// number of points
+	int					dim,			// dimension of space
+	const ANNorthRect	&bnd_box,		// current bounding box
+	ANNorthRect			&inner_box)		// inner box if shrinking (returned)
+{
+	int i;
+												// compute tight bounding box
+	annEnclRect(pa, pidx, n, dim, inner_box);
+
+	ANNcoord max_length = 0;					// find longest box side
+	for (i = 0; i < dim; i++) {
+		ANNcoord length = inner_box.hi[i] - inner_box.lo[i];
+		if (length > max_length) {
+			max_length = length;
+		}
+	}
+
+	int shrink_ct = 0;							// number of sides we shrunk
+	for (i = 0; i < dim; i++) {					// select which sides to shrink
+												// gap between boxes
+		ANNcoord gap_hi = bnd_box.hi[i] - inner_box.hi[i];
+												// big enough gap to shrink?
+		if (gap_hi < max_length*BD_GAP_THRESH)
+			inner_box.hi[i] = bnd_box.hi[i];	// no - expand
+		else shrink_ct++;						// yes - shrink this side
+
+												// repeat for high side
+		ANNcoord gap_lo = inner_box.lo[i] - bnd_box.lo[i];
+		if (gap_lo < max_length*BD_GAP_THRESH)
+			inner_box.lo[i] = bnd_box.lo[i];	// no - expand
+		else shrink_ct++;						// yes - shrink this side
+	}
+
+	if (shrink_ct >= BD_CT_THRESH)				// did we shrink enough sides?
+		 return SHRINK;
+	else return SPLIT;
+}
+
+//----------------------------------------------------------------------
+//	tryCentroidShrink - Attempt a centroid shrink
+//
+//	We repeatedly apply the splitting rule, always to the larger subset
+//	of points, until the number of points decreases by the constant
+//	fraction BD_FRACTION.  If this takes more than dim*BD_MAX_SPLIT_FAC
+//	splits for this to happen, then we shrink to the final inner box
+//	Otherwise we split.
+//----------------------------------------------------------------------
+
+const float	BD_MAX_SPLIT_FAC = 0.5;		// maximum number of splits allowed
+const float	BD_FRACTION = 0.5;			// ...to reduce points by this fraction
+										// ...This must be < 1.
+
+ANNdecomp tryCentroidShrink(			// try a centroid shrink
+	ANNpointArray		pa,				// point array
+	ANNidxArray			pidx,			// point indices to store in subtree
+	int					n,				// number of points
+	int					dim,			// dimension of space
+	const ANNorthRect	&bnd_box,		// current bounding box
+	ANNkd_splitter		splitter,		// splitting procedure
+	ANNorthRect			&inner_box)		// inner box if shrinking (returned)
+{
+	int n_sub = n;						// number of points in subset
+	int n_goal = (int) (n*BD_FRACTION); // number of point in goal
+	int n_splits = 0;					// number of splits needed
+										// initialize inner box to bounding box
+	annAssignRect(dim, inner_box, bnd_box);
+
+	while (n_sub > n_goal) {			// keep splitting until goal reached
+		int cd;							// cut dim from splitter (ignored)
+		ANNcoord cv;					// cut value from splitter (ignored)
+		int n_lo;						// number of points on low side
+										// invoke splitting procedure
+		(*splitter)(pa, pidx, inner_box, n_sub, dim, cd, cv, n_lo);
+		n_splits++;						// increment split count
+
+		if (n_lo >= n_sub/2) {			// most points on low side
+			inner_box.hi[cd] = cv;		// collapse high side
+			n_sub = n_lo;				// recurse on lower points
+		}
+		else {							// most points on high side
+			inner_box.lo[cd] = cv;		// collapse low side
+			pidx += n_lo;				// recurse on higher points
+			n_sub -= n_lo;
+		}
+	}
+    if (n_splits > dim*BD_MAX_SPLIT_FAC)// took too many splits
+		return SHRINK;					// shrink to final subset
+	else
+		return SPLIT;
+}
+
+//----------------------------------------------------------------------
+//	selectDecomp - select which decomposition to use
+//----------------------------------------------------------------------
+
+ANNdecomp selectDecomp(			// select decomposition method
+	ANNpointArray		pa,				// point array
+	ANNidxArray			pidx,			// point indices to store in subtree
+	int					n,				// number of points
+	int					dim,			// dimension of space
+	const ANNorthRect	&bnd_box,		// current bounding box
+	ANNkd_splitter		splitter,		// splitting procedure
+	ANNshrinkRule		shrink,			// shrinking rule
+	ANNorthRect			&inner_box)		// inner box if shrinking (returned)
+{
+	ANNdecomp decomp = SPLIT;			// decomposition
+
+	switch (shrink) {					// check shrinking rule
+	case ANN_BD_NONE:					// no shrinking allowed
+		decomp = SPLIT;
+		break;
+	case ANN_BD_SUGGEST:				// author's suggestion
+	case ANN_BD_SIMPLE:					// simple shrink
+		decomp = trySimpleShrink(
+				pa, pidx,				// points and indices
+				n, dim,					// number of points and dimension
+				bnd_box,				// current bounding box
+				inner_box);				// inner box if shrinking (returned)
+		break;
+	case ANN_BD_CENTROID:				// centroid shrink
+		decomp = tryCentroidShrink(
+				pa, pidx,				// points and indices
+				n, dim,					// number of points and dimension
+				bnd_box,				// current bounding box
+				splitter,				// splitting procedure
+				inner_box);				// inner box if shrinking (returned)
+		break;
+	default:
+		annError("Illegal shrinking rule", ANNabort);
+	}
+	return decomp;
+}
+
+//----------------------------------------------------------------------
+//	rbd_tree - recursive procedure to build a bd-tree
+//
+//		This is analogous to rkd_tree, but for bd-trees.  See the
+//		procedure rkd_tree() in kd_split.cpp for more information.
+//
+//		If the number of points falls below the bucket size, then a
+//		leaf node is created for the points.  Otherwise we invoke the
+//		procedure selectDecomp() which determines whether we are to
+//		split or shrink.  If splitting is chosen, then we essentially
+//		do exactly as rkd_tree() would, and invoke the specified
+//		splitting procedure to the points.  Otherwise, the selection
+//		procedure returns a bounding box, from which we extract the
+//		appropriate shrinking bounds, and create a shrinking node.
+//		Finally the points are subdivided, and the procedure is
+//		invoked recursively on the two subsets to form the children.
+//----------------------------------------------------------------------
+
+ANNkd_ptr rbd_tree(				// recursive construction of bd-tree
+	ANNpointArray		pa,				// point array
+	ANNidxArray			pidx,			// point indices to store in subtree
+	int					n,				// number of points
+	int					dim,			// dimension of space
+	int					bsp,			// bucket space
+	ANNorthRect			&bnd_box,		// bounding box for current node
+	ANNkd_splitter		splitter,		// splitting routine
+	ANNshrinkRule		shrink)			// shrinking rule
+{
+	ANNdecomp decomp;					// decomposition method
+
+	ANNorthRect inner_box(dim);			// inner box (if shrinking)
+
+	if (n <= bsp) {						// n small, make a leaf node
+		if (n == 0)						// empty leaf node
+			return KD_TRIVIAL;			// return (canonical) empty leaf
+		else							// construct the node and return
+			return new ANNkd_leaf(n, pidx); 
+	}
+	
+	decomp = selectDecomp(				// select decomposition method
+				pa, pidx,				// points and indices
+				n, dim,					// number of points and dimension
+				bnd_box,				// current bounding box
+				splitter, shrink,		// splitting/shrinking methods
+				inner_box);				// inner box if shrinking (returned)
+	
+	if (decomp == SPLIT) {				// split selected
+		int cd;							// cutting dimension
+		ANNcoord cv;					// cutting value
+		int n_lo;						// number on low side of cut
+										// invoke splitting procedure
+		(*splitter)(pa, pidx, bnd_box, n, dim, cd, cv, n_lo);
+
+		ANNcoord lv = bnd_box.lo[cd];	// save bounds for cutting dimension
+		ANNcoord hv = bnd_box.hi[cd];
+
+		bnd_box.hi[cd] = cv;			// modify bounds for left subtree
+		ANNkd_ptr lo = rbd_tree(		// build left subtree
+				pa, pidx, n_lo,			// ...from pidx[0..n_lo-1]
+				dim, bsp, bnd_box, splitter, shrink);
+		bnd_box.hi[cd] = hv;			// restore bounds
+
+		bnd_box.lo[cd] = cv;			// modify bounds for right subtree
+		ANNkd_ptr hi = rbd_tree(		// build right subtree
+				pa, pidx + n_lo, n-n_lo,// ...from pidx[n_lo..n-1]
+				dim, bsp, bnd_box, splitter, shrink);
+		bnd_box.lo[cd] = lv;			// restore bounds
+										// create the splitting node
+		return new ANNkd_split(cd, cv, lv, hv, lo, hi);
+	}
+	else {								// shrink selected
+		int n_in;						// number of points in box
+		int n_bnds;						// number of bounding sides
+
+		annBoxSplit(					// split points around inner box
+				pa,						// points to split
+				pidx,					// point indices
+				n,						// number of points
+				dim,					// dimension
+				inner_box,				// inner box
+				n_in);					// number of points inside (returned)
+
+		ANNkd_ptr in = rbd_tree(		// build inner subtree pidx[0..n_in-1]
+				pa, pidx, n_in, dim, bsp, inner_box, splitter, shrink);
+		ANNkd_ptr out = rbd_tree(		// build outer subtree pidx[n_in..n]
+				pa, pidx+n_in, n - n_in, dim, bsp, bnd_box, splitter, shrink);
+
+		ANNorthHSArray bnds = NULL;		// bounds (alloc in Box2Bnds and
+										// ...freed in bd_shrink destroyer)
+
+		annBox2Bnds(					// convert inner box to bounds
+				inner_box,				// inner box
+				bnd_box,				// enclosing box
+				dim,					// dimension
+				n_bnds,					// number of bounds (returned)
+				bnds);					// bounds array (modified)
+
+										// return shrinking node
+		return new ANNbd_shrink(n_bnds, bnds, in, out);
+	}
+} 
diff --git a/contrib/ANN/src/bd_tree.h b/contrib/ANN/src/bd_tree.h
new file mode 100644
index 0000000000..408889a07d
--- /dev/null
+++ b/contrib/ANN/src/bd_tree.h
@@ -0,0 +1,100 @@
+//----------------------------------------------------------------------
+// File:			bd_tree.h
+// Programmer:		David Mount
+// Description:		Declarations for standard bd-tree routines
+// Last modified:	01/04/05 (Version 1.0)
+//----------------------------------------------------------------------
+// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
+// David Mount.  All Rights Reserved.
+// 
+// This software and related documentation is part of the Approximate
+// Nearest Neighbor Library (ANN).  This software is provided under
+// the provisions of the Lesser GNU Public License (LGPL).  See the
+// file ../ReadMe.txt for further information.
+// 
+// The University of Maryland (U.M.) and the authors make no
+// representations about the suitability or fitness of this software for
+// any purpose.  It is provided "as is" without express or implied
+// warranty.
+//----------------------------------------------------------------------
+// History:
+//	Revision 0.1  03/04/98
+//		Initial release
+//	Revision 1.0  04/01/05
+//		Changed IN, OUT to ANN_IN, ANN_OUT
+//----------------------------------------------------------------------
+
+#ifndef ANN_bd_tree_H
+#define ANN_bd_tree_H
+
+#include <ANN/ANNx.h>					// all ANN includes
+#include "kd_tree.h"					// kd-tree includes
+
+//----------------------------------------------------------------------
+//	bd-tree shrinking node.
+//		The main addition in the bd-tree is the shrinking node, which
+//		is declared here.
+//
+//		Shrinking nodes are defined by list of orthogonal halfspaces.
+//		These halfspaces define a (possibly unbounded) orthogonal
+//		rectangle.  There are two children, in and out.  Points that
+//		lie within this rectangle are stored in the in-child, and the
+//		other points are stored in the out-child.
+//
+//		We use a list of orthogonal halfspaces rather than an
+//		orthogonal rectangle object because typically the number of
+//		sides of the shrinking box will be much smaller than the
+//		worst case bound of 2*dim.
+//
+//		BEWARE: Note that constructor just copies the pointer to the
+//		bounding array, but the destructor deallocates it.  This is
+//		rather poor practice, but happens to be convenient.  The list
+//		is allocated in the bd-tree building procedure rbd_tree() just
+//		prior to construction, and is used for no other purposes.
+//
+//		WARNING: In the near neighbor searching code it is assumed that
+//		the list of bounding halfspaces is irredundant, meaning that there
+//		are no two distinct halfspaces in the list with the same outward
+//		pointing normals.
+//----------------------------------------------------------------------
+
+class ANNbd_shrink : public ANNkd_node	// splitting node of a kd-tree
+{
+	int					n_bnds;			// number of bounding halfspaces
+	ANNorthHSArray		bnds;			// list of bounding halfspaces
+	ANNkd_ptr			child[2];		// in and out children
+public:
+	ANNbd_shrink(						// constructor
+		int				nb,				// number of bounding halfspaces
+		ANNorthHSArray	bds,			// list of bounding halfspaces
+		ANNkd_ptr ic=NULL, ANNkd_ptr oc=NULL)	// children
+		{
+			n_bnds			= nb;				// cutting dimension
+			bnds			= bds;				// assign bounds
+			child[ANN_IN]	= ic;				// set children
+			child[ANN_OUT]	= oc;
+		}
+
+	~ANNbd_shrink()						// destructor
+		{
+			if (child[ANN_IN]!= NULL && child[ANN_IN]!=  KD_TRIVIAL) 
+				delete child[ANN_IN];
+			if (child[ANN_OUT]!= NULL&& child[ANN_OUT]!= KD_TRIVIAL) 
+				delete child[ANN_OUT];
+			if (bnds != NULL)
+				delete [] bnds;			// delete bounds
+		}
+
+	virtual void getStats(						// get tree statistics
+				int dim,						// dimension of space
+				ANNkdStats &st,					// statistics
+				ANNorthRect &bnd_box);			// bounding box
+	virtual void print(int level, ostream &out);// print node
+	virtual void dump(ostream &out);			// dump node
+
+	virtual void ann_search(ANNdist);			// standard search
+	virtual void ann_pri_search(ANNdist);		// priority search
+	virtual void ann_FR_search(ANNdist); 		// fixed-radius search
+};
+
+#endif
diff --git a/contrib/ANN/src/brute.cpp b/contrib/ANN/src/brute.cpp
new file mode 100644
index 0000000000..d7cba2c7f6
--- /dev/null
+++ b/contrib/ANN/src/brute.cpp
@@ -0,0 +1,109 @@
+//----------------------------------------------------------------------
+// File:			brute.cpp
+// Programmer:		Sunil Arya and David Mount
+// Description:		Brute-force nearest neighbors
+// Last modified:	05/03/05 (Version 1.1)
+//----------------------------------------------------------------------
+// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
+// David Mount.  All Rights Reserved.
+// 
+// This software and related documentation is part of the Approximate
+// Nearest Neighbor Library (ANN).  This software is provided under
+// the provisions of the Lesser GNU Public License (LGPL).  See the
+// file ../ReadMe.txt for further information.
+// 
+// The University of Maryland (U.M.) and the authors make no
+// representations about the suitability or fitness of this software for
+// any purpose.  It is provided "as is" without express or implied
+// warranty.
+//----------------------------------------------------------------------
+// History:
+//	Revision 0.1  03/04/98
+//		Initial release
+//	Revision 1.1  05/03/05
+//		Added fixed-radius kNN search
+//----------------------------------------------------------------------
+
+#include <ANN/ANNx.h>					// all ANN includes
+#include "pr_queue_k.h"					// k element priority queue
+
+//----------------------------------------------------------------------
+//		Brute-force search simply stores a pointer to the list of
+//		data points and searches linearly for the nearest neighbor.
+//		The k nearest neighbors are stored in a k-element priority
+//		queue (which is implemented in a pretty dumb way as well).
+//
+//		If ANN_ALLOW_SELF_MATCH is ANNfalse then data points at distance
+//		zero are not considered.
+//
+//		Note that the error bound eps is passed in, but it is ignored.
+//		These routines compute exact nearest neighbors (which is needed
+//		for validation purposes in ann_test.cpp).
+//----------------------------------------------------------------------
+
+ANNbruteForce::ANNbruteForce(			// constructor from point array
+	ANNpointArray		pa,				// point array
+	int					n,				// number of points
+	int					dd)				// dimension
+{
+	dim = dd;  n_pts = n;  pts = pa;
+}
+
+ANNbruteForce::~ANNbruteForce() { }		// destructor (empty)
+
+void ANNbruteForce::annkSearch(			// approx k near neighbor search
+	ANNpoint			q,				// query point
+	int					k,				// number of near neighbors to return
+	ANNidxArray			nn_idx,			// nearest neighbor indices (returned)
+	ANNdistArray		dd,				// dist to near neighbors (returned)
+	double				eps)			// error bound (ignored)
+{
+	ANNmin_k mk(k);						// construct a k-limited priority queue
+	int i;
+
+	if (k > n_pts) {					// too many near neighbors?
+		annError("Requesting more near neighbors than data points", ANNabort);
+	}
+										// run every point through queue
+	for (i = 0; i < n_pts; i++) {
+										// compute distance to point
+		ANNdist sqDist = annDist(dim, pts[i], q);
+		if (ANN_ALLOW_SELF_MATCH || sqDist != 0)
+			mk.insert(sqDist, i);
+	}
+	for (i = 0; i < k; i++) {			// extract the k closest points
+		dd[i] = mk.ith_smallest_key(i);
+		nn_idx[i] = mk.ith_smallest_info(i);
+	}
+}
+
+int ANNbruteForce::annkFRSearch(		// approx fixed-radius kNN search
+	ANNpoint			q,				// query point
+	ANNdist				sqRad,			// squared radius
+	int					k,				// number of near neighbors to return
+	ANNidxArray			nn_idx,			// nearest neighbor array (returned)
+	ANNdistArray		dd,				// dist to near neighbors (returned)
+	double				eps)			// error bound
+{
+	ANNmin_k mk(k);						// construct a k-limited priority queue
+	int i;
+	int pts_in_range = 0;				// number of points in query range
+										// run every point through queue
+	for (i = 0; i < n_pts; i++) {
+										// compute distance to point
+		ANNdist sqDist = annDist(dim, pts[i], q);
+		if (sqDist <= sqRad &&			// within radius bound
+			(ANN_ALLOW_SELF_MATCH || sqDist != 0)) { // ...and no self match
+			mk.insert(sqDist, i);
+			pts_in_range++;
+		}
+	}
+	for (i = 0; i < k; i++) {			// extract the k closest points
+		if (dd != NULL)
+			dd[i] = mk.ith_smallest_key(i);
+		if (nn_idx != NULL)
+			nn_idx[i] = mk.ith_smallest_info(i);
+	}
+
+	return pts_in_range;
+}
diff --git a/contrib/ANN/src/kd_dump.cpp b/contrib/ANN/src/kd_dump.cpp
new file mode 100644
index 0000000000..e7015efe85
--- /dev/null
+++ b/contrib/ANN/src/kd_dump.cpp
@@ -0,0 +1,444 @@
+//----------------------------------------------------------------------
+// File:			kd_dump.cc
+// Programmer:		David Mount
+// Description:		Dump and Load for kd- and bd-trees
+// Last modified:	01/04/05 (Version 1.0)
+//----------------------------------------------------------------------
+// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
+// David Mount.  All Rights Reserved.
+// 
+// This software and related documentation is part of the Approximate
+// Nearest Neighbor Library (ANN).  This software is provided under
+// the provisions of the Lesser GNU Public License (LGPL).  See the
+// file ../ReadMe.txt for further information.
+// 
+// The University of Maryland (U.M.) and the authors make no
+// representations about the suitability or fitness of this software for
+// any purpose.  It is provided "as is" without express or implied
+// warranty.
+//----------------------------------------------------------------------
+// History:
+//	Revision 0.1  03/04/98
+//		Initial release
+//	Revision 1.0  04/01/05
+//		Moved dump out of kd_tree.cc into this file.
+//		Added kd-tree load constructor.
+//----------------------------------------------------------------------
+// This file contains routines for dumping kd-trees and bd-trees and
+// reloading them. (It is an abuse of policy to include both kd- and
+// bd-tree routines in the same file, sorry.  There should be no problem
+// in deleting the bd- versions of the routines if they are not
+// desired.)
+//----------------------------------------------------------------------
+
+#include "kd_tree.h"					// kd-tree declarations
+#include "bd_tree.h"					// bd-tree declarations
+
+using namespace std;					// make std:: available
+
+//----------------------------------------------------------------------
+//		Constants
+//----------------------------------------------------------------------
+
+const int		STRING_LEN		= 500;	// maximum string length
+const double	EPSILON			= 1E-5; // small number for float comparison
+
+enum ANNtreeType {KD_TREE, BD_TREE};	// tree types (used in loading)
+
+//----------------------------------------------------------------------
+//		Procedure declarations
+//----------------------------------------------------------------------
+
+static ANNkd_ptr annReadDump(			// read dump file
+	istream				&in,					// input stream
+	ANNtreeType			tree_type,				// type of tree expected
+	ANNpointArray		&the_pts,				// new points (if applic)
+	ANNidxArray			&the_pidx,				// point indices (returned)
+	int					&the_dim,				// dimension (returned)
+	int					&the_n_pts,				// number of points (returned)
+	int					&the_bkt_size,			// bucket size (returned)
+	ANNpoint			&the_bnd_box_lo,		// low bounding point
+	ANNpoint			&the_bnd_box_hi);		// high bounding point
+
+static ANNkd_ptr annReadTree(			// read tree-part of dump file
+	istream				&in,					// input stream
+	ANNtreeType			tree_type,				// type of tree expected
+	ANNidxArray			the_pidx,				// point indices (modified)
+	int					&next_idx);				// next index (modified)
+
+//----------------------------------------------------------------------
+//	ANN kd- and bd-tree Dump Format
+//		The dump file begins with a header containing the version of
+//		ANN, an optional section containing the points, followed by
+//		a description of the tree.	The tree is printed in preorder.
+//
+//		Format:
+//		#ANN <version number> <comments> [END_OF_LINE]
+//		points <dim> <n_pts>			(point coordinates: this is optional)
+//		0 <xxx> <xxx> ... <xxx>			(point indices and coordinates)
+//		1 <xxx> <xxx> ... <xxx>
+//		  ...
+//		tree <dim> <n_pts> <bkt_size>
+//		<xxx> <xxx> ... <xxx>			(lower end of bounding box)
+//		<xxx> <xxx> ... <xxx>			(upper end of bounding box)
+//				If the tree is null, then a single line "null" is
+//				output.	 Otherwise the nodes of the tree are printed
+//				one per line in preorder.  Leaves and splitting nodes 
+//				have the following formats:
+//		Leaf node:
+//				leaf <n_pts> <bkt[0]> <bkt[1]> ... <bkt[n-1]>
+//		Splitting nodes:
+//				split <cut_dim> <cut_val> <lo_bound> <hi_bound>
+//
+//		For bd-trees:
+//
+//		Shrinking nodes:
+//				shrink <n_bnds>
+//						<cut_dim> <cut_val> <side>
+//						<cut_dim> <cut_val> <side>
+//						... (repeated n_bnds times)
+//----------------------------------------------------------------------
+
+void ANNkd_tree::Dump(					// dump entire tree
+		ANNbool with_pts,				// print points as well?
+		ostream &out)					// output stream
+{
+	out << "#ANN " << ANNversion << "\n";
+	out.precision(ANNcoordPrec);		// use full precision in dumping
+	if (with_pts) {						// print point coordinates
+		out << "points " << dim << " " << n_pts << "\n";
+		for (int i = 0; i < n_pts; i++) {
+			out << i << " ";
+			annPrintPt(pts[i], dim, out);
+			out << "\n";
+		}
+	}
+	out << "tree "						// print tree elements
+		<< dim << " "
+		<< n_pts << " "
+		<< bkt_size << "\n";
+
+	annPrintPt(bnd_box_lo, dim, out);	// print lower bound
+	out << "\n";
+	annPrintPt(bnd_box_hi, dim, out);	// print upper bound
+	out << "\n";
+
+	if (root == NULL)					// empty tree?
+		out << "null\n";
+	else {
+		root->dump(out);				// invoke printing at root
+	}
+	out.precision(0);					// restore default precision
+}
+
+void ANNkd_split::dump(					// dump a splitting node
+		ostream &out)					// output stream
+{
+	out << "split " << cut_dim << " " << cut_val << " ";
+	out << cd_bnds[ANN_LO] << " " << cd_bnds[ANN_HI] << "\n";
+
+	child[ANN_LO]->dump(out);			// print low child
+	child[ANN_HI]->dump(out);			// print high child
+}
+
+void ANNkd_leaf::dump(					// dump a leaf node
+		ostream &out)					// output stream
+{
+	if (this == KD_TRIVIAL) {			// canonical trivial leaf node
+		out << "leaf 0\n";				// leaf no points
+	}
+	else{
+		out << "leaf " << n_pts;
+		for (int j = 0; j < n_pts; j++) {
+			out << " " << bkt[j];
+		}
+		out << "\n";
+	}
+}
+
+void ANNbd_shrink::dump(				// dump a shrinking node
+		ostream &out)					// output stream
+{
+	out << "shrink " << n_bnds << "\n";
+	for (int j = 0; j < n_bnds; j++) {
+		out << bnds[j].cd << " " << bnds[j].cv << " " << bnds[j].sd << "\n";
+	}
+	child[ANN_IN]->dump(out);			// print in-child
+	child[ANN_OUT]->dump(out);			// print out-child
+}
+
+//----------------------------------------------------------------------
+// Load kd-tree from dump file
+//		This rebuilds a kd-tree which was dumped to a file.	 The dump
+//		file contains all the basic tree information according to a
+//		preorder traversal.	 We assume that the dump file also contains
+//		point data.	 (This is to guarantee the consistency of the tree.)
+//		If not, then an error is generated.
+//
+//		Indirectly, this procedure allocates space for points, point
+//		indices, all nodes in the tree, and the bounding box for the
+//		tree.  When the tree is destroyed, all but the points are
+//		deallocated.
+//
+//		This routine calls annReadDump to do all the work.
+//----------------------------------------------------------------------
+
+ANNkd_tree::ANNkd_tree(					// build from dump file
+	istream				&in)					// input stream for dump file
+{
+	int the_dim;								// local dimension
+	int the_n_pts;								// local number of points
+	int the_bkt_size;							// local number of points
+	ANNpoint the_bnd_box_lo;					// low bounding point
+	ANNpoint the_bnd_box_hi;					// high bounding point
+	ANNpointArray the_pts;						// point storage
+	ANNidxArray the_pidx;						// point index storage
+	ANNkd_ptr the_root;							// root of the tree
+
+	the_root = annReadDump(						// read the dump file
+		in,										// input stream
+		KD_TREE,								// expecting a kd-tree
+		the_pts,								// point array (returned)
+		the_pidx,								// point indices (returned)
+		the_dim, the_n_pts, the_bkt_size,		// basic tree info (returned)
+		the_bnd_box_lo, the_bnd_box_hi);		// bounding box info (returned)
+
+												// create a skeletal tree
+	SkeletonTree(the_n_pts, the_dim, the_bkt_size, the_pts, the_pidx);
+
+	bnd_box_lo = the_bnd_box_lo;
+	bnd_box_hi = the_bnd_box_hi;
+
+	root = the_root;							// set the root
+}
+
+ANNbd_tree::ANNbd_tree(					// build bd-tree from dump file
+	istream				&in) : ANNkd_tree()		// input stream for dump file
+{
+	int the_dim;								// local dimension
+	int the_n_pts;								// local number of points
+	int the_bkt_size;							// local number of points
+	ANNpoint the_bnd_box_lo;					// low bounding point
+	ANNpoint the_bnd_box_hi;					// high bounding point
+	ANNpointArray the_pts;						// point storage
+	ANNidxArray the_pidx;						// point index storage
+	ANNkd_ptr the_root;							// root of the tree
+
+	the_root = annReadDump(						// read the dump file
+		in,										// input stream
+		BD_TREE,								// expecting a bd-tree
+		the_pts,								// point array (returned)
+		the_pidx,								// point indices (returned)
+		the_dim, the_n_pts, the_bkt_size,		// basic tree info (returned)
+		the_bnd_box_lo, the_bnd_box_hi);		// bounding box info (returned)
+
+												// create a skeletal tree
+	SkeletonTree(the_n_pts, the_dim, the_bkt_size, the_pts, the_pidx);
+	bnd_box_lo = the_bnd_box_lo;
+	bnd_box_hi = the_bnd_box_hi;
+
+	root = the_root;							// set the root
+}
+
+//----------------------------------------------------------------------
+//	annReadDump - read a dump file
+//
+//		This procedure reads a dump file, constructs a kd-tree
+//		and returns all the essential information needed to actually
+//		construct the tree.	 Because this procedure is used for
+//		constructing both kd-trees and bd-trees, the second argument
+//		is used to indicate which type of tree we are expecting.
+//----------------------------------------------------------------------
+
+static ANNkd_ptr annReadDump(
+	istream				&in,					// input stream
+	ANNtreeType			tree_type,				// type of tree expected
+	ANNpointArray		&the_pts,				// new points (returned)
+	ANNidxArray			&the_pidx,				// point indices (returned)
+	int					&the_dim,				// dimension (returned)
+	int					&the_n_pts,				// number of points (returned)
+	int					&the_bkt_size,			// bucket size (returned)
+	ANNpoint			&the_bnd_box_lo,		// low bounding point (ret'd)
+	ANNpoint			&the_bnd_box_hi)		// high bounding point (ret'd)
+{
+	int j;
+	char str[STRING_LEN];						// storage for string
+	char version[STRING_LEN];					// ANN version number
+	ANNkd_ptr the_root = NULL;
+
+	//------------------------------------------------------------------
+	//	Input file header
+	//------------------------------------------------------------------
+	in >> str;									// input header
+	if (strcmp(str, "#ANN") != 0) {				// incorrect header
+		annError("Incorrect header for dump file", ANNabort);
+	}
+	in.getline(version, STRING_LEN);			// get version (ignore)
+
+	//------------------------------------------------------------------
+	//	Input the points
+	//			An array the_pts is allocated and points are read from
+	//			the dump file.
+	//------------------------------------------------------------------
+	in >> str;									// get major heading
+	if (strcmp(str, "points") == 0) {			// points section
+		in >> the_dim;							// input dimension
+		in >> the_n_pts;						// number of points
+												// allocate point storage
+		the_pts = annAllocPts(the_n_pts, the_dim);
+		for (int i = 0; i < the_n_pts; i++) {	// input point coordinates
+			ANNidx idx;							// point index
+			in >> idx;							// input point index
+			if (idx < 0 || idx >= the_n_pts) {
+				annError("Point index is out of range", ANNabort);
+			}
+			for (j = 0; j < the_dim; j++) {
+				in >> the_pts[idx][j];			// read point coordinates
+			}
+		}
+		in >> str;								// get next major heading
+	}
+	else {										// no points were input
+		annError("Points must be supplied in the dump file", ANNabort);
+	}
+
+	//------------------------------------------------------------------
+	//	Input the tree
+	//			After the basic header information, we invoke annReadTree
+	//			to do all the heavy work.  We create our own array of
+	//			point indices (so we can pass them to annReadTree())
+	//			but we do not deallocate them.	They will be deallocated
+	//			when the tree is destroyed.
+	//------------------------------------------------------------------
+	if (strcmp(str, "tree") == 0) {				// tree section
+		in >> the_dim;							// read dimension
+		in >> the_n_pts;						// number of points
+		in >> the_bkt_size;						// bucket size
+		the_bnd_box_lo = annAllocPt(the_dim);	// allocate bounding box pts
+		the_bnd_box_hi = annAllocPt(the_dim);
+
+		for (j = 0; j < the_dim; j++) {			// read bounding box low
+			in >> the_bnd_box_lo[j];
+		}
+		for (j = 0; j < the_dim; j++) {			// read bounding box low
+			in >> the_bnd_box_hi[j];
+		}
+		the_pidx = new ANNidx[the_n_pts];		// allocate point index array
+		int next_idx = 0;						// number of indices filled
+												// read the tree and indices
+		the_root = annReadTree(in, tree_type, the_pidx, next_idx);
+		if (next_idx != the_n_pts) {			// didn't see all the points?
+			annError("Didn't see as many points as expected", ANNwarn);
+		}
+	}
+	else {
+		annError("Illegal dump format.	Expecting section heading", ANNabort);
+	}
+	return the_root;
+}
+
+//----------------------------------------------------------------------
+// annReadTree - input tree and return pointer
+//
+//		annReadTree reads in a node of the tree, makes any recursive
+//		calls as needed to input the children of this node (if internal).
+//		It returns a pointer to the node that was created.	An array
+//		of point indices is given along with a pointer to the next
+//		available location in the array.  As leaves are read, their
+//		point indices are stored here, and the point buckets point
+//		to the first entry in the array.
+//
+//		Recall that these are the formats.	The tree is given in
+//		preorder.
+//
+//		Leaf node:
+//				leaf <n_pts> <bkt[0]> <bkt[1]> ... <bkt[n-1]>
+//		Splitting nodes:
+//				split <cut_dim> <cut_val> <lo_bound> <hi_bound>
+//
+//		For bd-trees:
+//
+//		Shrinking nodes:
+//				shrink <n_bnds>
+//						<cut_dim> <cut_val> <side>
+//						<cut_dim> <cut_val> <side>
+//						... (repeated n_bnds times)
+//----------------------------------------------------------------------
+
+static ANNkd_ptr annReadTree(
+	istream				&in,					// input stream
+	ANNtreeType			tree_type,				// type of tree expected
+	ANNidxArray			the_pidx,				// point indices (modified)
+	int					&next_idx)				// next index (modified)
+{
+	char tag[STRING_LEN];						// tag (leaf, split, shrink)
+	int n_pts;									// number of points in leaf
+	int cd;										// cut dimension
+	ANNcoord cv;								// cut value
+	ANNcoord lb;								// low bound
+	ANNcoord hb;								// high bound
+	int n_bnds;									// number of bounding sides
+	int sd;										// which side
+
+	in >> tag;									// input node tag
+
+	if (strcmp(tag, "null") == 0) {				// null tree
+		return NULL;
+	}
+	//------------------------------------------------------------------
+	//	Read a leaf
+	//------------------------------------------------------------------
+	if (strcmp(tag, "leaf") == 0) {				// leaf node
+
+		in >> n_pts;							// input number of points
+		int old_idx = next_idx;					// save next_idx
+		if (n_pts == 0) {						// trivial leaf
+			return KD_TRIVIAL;
+		}
+		else {
+			for (int i = 0; i < n_pts; i++) {	// input point indices
+				in >> the_pidx[next_idx++];		// store in array of indices
+			}
+		}
+		return new ANNkd_leaf(n_pts, &the_pidx[old_idx]);
+	}
+	//------------------------------------------------------------------
+	//	Read a splitting node
+	//------------------------------------------------------------------
+	else if (strcmp(tag, "split") == 0) {		// splitting node
+
+		in >> cd >> cv >> lb >> hb;
+
+												// read low and high subtrees
+		ANNkd_ptr lc = annReadTree(in, tree_type, the_pidx, next_idx);
+		ANNkd_ptr hc = annReadTree(in, tree_type, the_pidx, next_idx);
+												// create new node and return
+		return new ANNkd_split(cd, cv, lb, hb, lc, hc);
+	}
+	//------------------------------------------------------------------
+	//	Read a shrinking node (bd-tree only)
+	//------------------------------------------------------------------
+	else if (strcmp(tag, "shrink") == 0) {		// shrinking node
+		if (tree_type != BD_TREE) {
+			annError("Shrinking node not allowed in kd-tree", ANNabort);
+		}
+
+		in >> n_bnds;							// number of bounding sides
+												// allocate bounds array
+		ANNorthHSArray bds = new ANNorthHalfSpace[n_bnds];
+		for (int i = 0; i < n_bnds; i++) {
+			in >> cd >> cv >> sd;				// input bounding halfspace
+												// copy to array
+			bds[i] = ANNorthHalfSpace(cd, cv, sd);
+		}
+												// read inner and outer subtrees
+		ANNkd_ptr ic = annReadTree(in, tree_type, the_pidx, next_idx);
+		ANNkd_ptr oc = annReadTree(in, tree_type, the_pidx, next_idx);
+												// create new node and return
+		return new ANNbd_shrink(n_bnds, bds, ic, oc);
+	}
+	else {
+		annError("Illegal node type in dump file", ANNabort);
+		exit(0);								// to keep the compiler happy
+	}
+}
diff --git a/contrib/ANN/src/kd_fix_rad_search.cpp b/contrib/ANN/src/kd_fix_rad_search.cpp
new file mode 100644
index 0000000000..87eb757d7b
--- /dev/null
+++ b/contrib/ANN/src/kd_fix_rad_search.cpp
@@ -0,0 +1,183 @@
+//----------------------------------------------------------------------
+// File:			kd_fix_rad_search.cpp
+// Programmer:		Sunil Arya and David Mount
+// Description:		Standard kd-tree fixed-radius kNN search
+// Last modified:	05/03/05 (Version 1.1)
+//----------------------------------------------------------------------
+// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
+// David Mount.  All Rights Reserved.
+// 
+// This software and related documentation is part of the Approximate
+// Nearest Neighbor Library (ANN).  This software is provided under
+// the provisions of the Lesser GNU Public License (LGPL).  See the
+// file ../ReadMe.txt for further information.
+// 
+// The University of Maryland (U.M.) and the authors make no
+// representations about the suitability or fitness of this software for
+// any purpose.  It is provided "as is" without express or implied
+// warranty.
+//----------------------------------------------------------------------
+// History:
+//	Revision 1.1  05/03/05
+//		Initial release
+//----------------------------------------------------------------------
+
+#include "kd_fix_rad_search.h"			// kd fixed-radius search decls
+
+//----------------------------------------------------------------------
+//	Approximate fixed-radius k nearest neighbor search
+//		The squared radius is provided, and this procedure finds the
+//		k nearest neighbors within the radius, and returns the total
+//		number of points lying within the radius.
+//
+//		The method used for searching the kd-tree is a variation of the
+//		nearest neighbor search used in kd_search.cpp, except that the
+//		radius of the search ball is known.  We refer the reader to that
+//		file for the explanation of the recursive search procedure.
+//----------------------------------------------------------------------
+
+//----------------------------------------------------------------------
+//		To keep argument lists short, a number of global variables
+//		are maintained which are common to all the recursive calls.
+//		These are given below.
+//----------------------------------------------------------------------
+
+int				ANNkdFRDim;				// dimension of space
+ANNpoint		ANNkdFRQ;				// query point
+ANNdist			ANNkdFRSqRad;			// squared radius search bound
+double			ANNkdFRMaxErr;			// max tolerable squared error
+ANNpointArray	ANNkdFRPts;				// the points
+ANNmin_k*		ANNkdFRPointMK;			// set of k closest points
+int				ANNkdFRPtsVisited;		// total points visited
+int				ANNkdFRPtsInRange;		// number of points in the range
+
+//----------------------------------------------------------------------
+//	annkFRSearch - fixed radius search for k nearest neighbors
+//----------------------------------------------------------------------
+
+int ANNkd_tree::annkFRSearch(
+	ANNpoint			q,				// the query point
+	ANNdist				sqRad,			// squared radius search bound
+	int					k,				// number of near neighbors to return
+	ANNidxArray			nn_idx,			// nearest neighbor indices (returned)
+	ANNdistArray		dd,				// the approximate nearest neighbor
+	double				eps)			// the error bound
+{
+	ANNkdFRDim = dim;					// copy arguments to static equivs
+	ANNkdFRQ = q;
+	ANNkdFRSqRad = sqRad;
+	ANNkdFRPts = pts;
+	ANNkdFRPtsVisited = 0;				// initialize count of points visited
+	ANNkdFRPtsInRange = 0;				// ...and points in the range
+
+	ANNkdFRMaxErr = ANN_POW(1.0 + eps);
+	ANN_FLOP(2)							// increment floating op count
+
+	ANNkdFRPointMK = new ANNmin_k(k);	// create set for closest k points
+										// search starting at the root
+	root->ann_FR_search(annBoxDistance(q, bnd_box_lo, bnd_box_hi, dim));
+
+	for (int i = 0; i < k; i++) {		// extract the k-th closest points
+		if (dd != NULL)
+			dd[i] = ANNkdFRPointMK->ith_smallest_key(i);
+		if (nn_idx != NULL)
+			nn_idx[i] = ANNkdFRPointMK->ith_smallest_info(i);
+	}
+
+	delete ANNkdFRPointMK;				// deallocate closest point set
+	return ANNkdFRPtsInRange;			// return final point count
+}
+
+//----------------------------------------------------------------------
+//	kd_split::ann_FR_search - search a splitting node
+//		Note: This routine is similar in structure to the standard kNN
+//		search.  It visits the subtree that is closer to the query point
+//		first.  For fixed-radius search, there is no benefit in visiting
+//		one subtree before the other, but we maintain the same basic
+//		code structure for the sake of uniformity.
+//----------------------------------------------------------------------
+
+void ANNkd_split::ann_FR_search(ANNdist box_dist)
+{
+										// check dist calc term condition
+	if (ANNmaxPtsVisited != 0 && ANNkdFRPtsVisited > ANNmaxPtsVisited) return;
+
+										// distance to cutting plane
+	ANNcoord cut_diff = ANNkdFRQ[cut_dim] - cut_val;
+
+	if (cut_diff < 0) {					// left of cutting plane
+		child[ANN_LO]->ann_FR_search(box_dist);// visit closer child first
+
+		ANNcoord box_diff = cd_bnds[ANN_LO] - ANNkdFRQ[cut_dim];
+		if (box_diff < 0)				// within bounds - ignore
+			box_diff = 0;
+										// distance to further box
+		box_dist = (ANNdist) ANN_SUM(box_dist,
+				ANN_DIFF(ANN_POW(box_diff), ANN_POW(cut_diff)));
+
+										// visit further child if in range
+		if (box_dist * ANNkdFRMaxErr <= ANNkdFRSqRad)
+			child[ANN_HI]->ann_FR_search(box_dist);
+
+	}
+	else {								// right of cutting plane
+		child[ANN_HI]->ann_FR_search(box_dist);// visit closer child first
+
+		ANNcoord box_diff = ANNkdFRQ[cut_dim] - cd_bnds[ANN_HI];
+		if (box_diff < 0)				// within bounds - ignore
+			box_diff = 0;
+										// distance to further box
+		box_dist = (ANNdist) ANN_SUM(box_dist,
+				ANN_DIFF(ANN_POW(box_diff), ANN_POW(cut_diff)));
+
+										// visit further child if close enough
+		if (box_dist * ANNkdFRMaxErr <= ANNkdFRSqRad)
+			child[ANN_LO]->ann_FR_search(box_dist);
+
+	}
+	ANN_FLOP(13)						// increment floating ops
+	ANN_SPL(1)							// one more splitting node visited
+}
+
+//----------------------------------------------------------------------
+//	kd_leaf::ann_FR_search - search points in a leaf node
+//		Note: The unreadability of this code is the result of
+//		some fine tuning to replace indexing by pointer operations.
+//----------------------------------------------------------------------
+
+void ANNkd_leaf::ann_FR_search(ANNdist box_dist)
+{
+	register ANNdist dist;				// distance to data point
+	register ANNcoord* pp;				// data coordinate pointer
+	register ANNcoord* qq;				// query coordinate pointer
+	register ANNcoord t;
+	register int d;
+
+	for (int i = 0; i < n_pts; i++) {	// check points in bucket
+
+		pp = ANNkdFRPts[bkt[i]];		// first coord of next data point
+		qq = ANNkdFRQ;					// first coord of query point
+		dist = 0;
+
+		for(d = 0; d < ANNkdFRDim; d++) {
+			ANN_COORD(1)				// one more coordinate hit
+			ANN_FLOP(5)					// increment floating ops
+
+			t = *(qq++) - *(pp++);		// compute length and adv coordinate
+										// exceeds dist to k-th smallest?
+			if( (dist = ANN_SUM(dist, ANN_POW(t))) > ANNkdFRSqRad) {
+				break;
+			}
+		}
+
+		if (d >= ANNkdFRDim &&					// among the k best?
+		   (ANN_ALLOW_SELF_MATCH || dist!=0)) { // and no self-match problem
+												// add it to the list
+			ANNkdFRPointMK->insert(dist, bkt[i]);
+			ANNkdFRPtsInRange++;				// increment point count
+		}
+	}
+	ANN_LEAF(1)							// one more leaf node visited
+	ANN_PTS(n_pts)						// increment points visited
+	ANNkdFRPtsVisited += n_pts;			// increment number of points visited
+}
diff --git a/contrib/ANN/src/kd_fix_rad_search.h b/contrib/ANN/src/kd_fix_rad_search.h
new file mode 100644
index 0000000000..7bae230db2
--- /dev/null
+++ b/contrib/ANN/src/kd_fix_rad_search.h
@@ -0,0 +1,44 @@
+//----------------------------------------------------------------------
+// File:			kd_fix_rad_search.h
+// Programmer:		Sunil Arya and David Mount
+// Description:		Standard kd-tree fixed-radius kNN search
+// Last modified:	??/??/?? (Version 1.1)
+//----------------------------------------------------------------------
+// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
+// David Mount.  All Rights Reserved.
+// 
+// This software and related documentation is part of the Approximate
+// Nearest Neighbor Library (ANN).  This software is provided under
+// the provisions of the Lesser GNU Public License (LGPL).  See the
+// file ../ReadMe.txt for further information.
+// 
+// The University of Maryland (U.M.) and the authors make no
+// representations about the suitability or fitness of this software for
+// any purpose.  It is provided "as is" without express or implied
+// warranty.
+//----------------------------------------------------------------------
+// History:
+//	Revision 1.1  ??/??/??
+//		Initial release
+//----------------------------------------------------------------------
+
+#ifndef ANN_kd_fix_rad_search_H
+#define ANN_kd_fix_rad_search_H
+
+#include "kd_tree.h"					// kd-tree declarations
+#include "kd_util.h"					// kd-tree utilities
+#include "pr_queue_k.h"					// k-element priority queue
+
+#include <ANN/ANNperf.h>				// performance evaluation
+
+//----------------------------------------------------------------------
+//	Global variables
+//		These are active for the life of each call to
+//		annRangeSearch().  They are set to save the number of
+//		variables that need to be passed among the various search
+//		procedures.
+//----------------------------------------------------------------------
+
+extern ANNpoint			ANNkdFRQ;			// query point (static copy)
+
+#endif
diff --git a/contrib/ANN/src/kd_pr_search.cpp b/contrib/ANN/src/kd_pr_search.cpp
new file mode 100644
index 0000000000..edb0479f29
--- /dev/null
+++ b/contrib/ANN/src/kd_pr_search.cpp
@@ -0,0 +1,219 @@
+//----------------------------------------------------------------------
+// File:			kd_pr_search.cpp
+// Programmer:		Sunil Arya and David Mount
+// Description:		Priority search for kd-trees
+// Last modified:	01/04/05 (Version 1.0)
+//----------------------------------------------------------------------
+// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
+// David Mount.  All Rights Reserved.
+// 
+// This software and related documentation is part of the Approximate
+// Nearest Neighbor Library (ANN).  This software is provided under
+// the provisions of the Lesser GNU Public License (LGPL).  See the
+// file ../ReadMe.txt for further information.
+// 
+// The University of Maryland (U.M.) and the authors make no
+// representations about the suitability or fitness of this software for
+// any purpose.  It is provided "as is" without express or implied
+// warranty.
+//----------------------------------------------------------------------
+// History:
+//	Revision 0.1  03/04/98
+//		Initial release
+//----------------------------------------------------------------------
+
+#include "kd_pr_search.h"				// kd priority search declarations
+
+//----------------------------------------------------------------------
+//	Approximate nearest neighbor searching by priority search.
+//		The kd-tree is searched for an approximate nearest neighbor.
+//		The point is returned through one of the arguments, and the
+//		distance returned is the SQUARED distance to this point.
+//
+//		The method used for searching the kd-tree is called priority
+//		search.  (It is described in Arya and Mount, ``Algorithms for
+//		fast vector quantization,'' Proc. of DCC '93: Data Compression
+//		Conference}, eds. J. A. Storer and M. Cohn, IEEE Press, 1993,
+//		381--390.)
+//
+//		The cell of the kd-tree containing the query point is located,
+//		and cells are visited in increasing order of distance from the
+//		query point.  This is done by placing each subtree which has
+//		NOT been visited in a priority queue, according to the closest
+//		distance of the corresponding enclosing rectangle from the
+//		query point.  The search stops when the distance to the nearest
+//		remaining rectangle exceeds the distance to the nearest point
+//		seen by a factor of more than 1/(1+eps). (Implying that any
+//		point found subsequently in the search cannot be closer by more
+//		than this factor.)
+//
+//		The main entry point is annkPriSearch() which sets things up and
+//		then call the recursive routine ann_pri_search().  This is a
+//		recursive routine which performs the processing for one node in
+//		the kd-tree.  There are two versions of this virtual procedure,
+//		one for splitting nodes and one for leaves. When a splitting node
+//		is visited, we determine which child to continue the search on
+//		(the closer one), and insert the other child into the priority
+//		queue.  When a leaf is visited, we compute the distances to the
+//		points in the buckets, and update information on the closest
+//		points.
+//
+//		Some trickery is used to incrementally update the distance from
+//		a kd-tree rectangle to the query point.  This comes about from
+//		the fact that which each successive split, only one component
+//		(along the dimension that is split) of the squared distance to
+//		the child rectangle is different from the squared distance to
+//		the parent rectangle.
+//----------------------------------------------------------------------
+
+//----------------------------------------------------------------------
+//		To keep argument lists short, a number of global variables
+//		are maintained which are common to all the recursive calls.
+//		These are given below.
+//----------------------------------------------------------------------
+
+double			ANNprEps;				// the error bound
+int				ANNprDim;				// dimension of space
+ANNpoint		ANNprQ;					// query point
+double			ANNprMaxErr;			// max tolerable squared error
+ANNpointArray	ANNprPts;				// the points
+ANNpr_queue		*ANNprBoxPQ;			// priority queue for boxes
+ANNmin_k		*ANNprPointMK;			// set of k closest points
+
+//----------------------------------------------------------------------
+//	annkPriSearch - priority search for k nearest neighbors
+//----------------------------------------------------------------------
+
+void ANNkd_tree::annkPriSearch(
+	ANNpoint			q,				// query point
+	int					k,				// number of near neighbors to return
+	ANNidxArray			nn_idx,			// nearest neighbor indices (returned)
+	ANNdistArray		dd,				// dist to near neighbors (returned)
+	double				eps)			// error bound (ignored)
+{
+										// max tolerable squared error
+	ANNprMaxErr = ANN_POW(1.0 + eps);
+	ANN_FLOP(2)							// increment floating ops
+
+	ANNprDim = dim;						// copy arguments to static equivs
+	ANNprQ = q;
+	ANNprPts = pts;
+	ANNptsVisited = 0;					// initialize count of points visited
+
+	ANNprPointMK = new ANNmin_k(k);		// create set for closest k points
+
+										// distance to root box
+	ANNdist box_dist = annBoxDistance(q,
+				bnd_box_lo, bnd_box_hi, dim);
+
+	ANNprBoxPQ = new ANNpr_queue(n_pts);// create priority queue for boxes
+	ANNprBoxPQ->insert(box_dist, root); // insert root in priority queue
+
+	while (ANNprBoxPQ->non_empty() &&
+		(!(ANNmaxPtsVisited != 0 && ANNptsVisited > ANNmaxPtsVisited))) {
+		ANNkd_ptr np;					// next box from prior queue
+
+										// extract closest box from queue
+		ANNprBoxPQ->extr_min(box_dist, (void *&) np);
+
+		ANN_FLOP(2)						// increment floating ops
+		if (box_dist*ANNprMaxErr >= ANNprPointMK->max_key())
+			break;
+
+		np->ann_pri_search(box_dist);	// search this subtree.
+	}
+
+	for (int i = 0; i < k; i++) {		// extract the k-th closest points
+		dd[i] = ANNprPointMK->ith_smallest_key(i);
+		nn_idx[i] = ANNprPointMK->ith_smallest_info(i);
+	}
+
+	delete ANNprPointMK;				// deallocate closest point set
+	delete ANNprBoxPQ;					// deallocate priority queue
+}
+
+//----------------------------------------------------------------------
+//	kd_split::ann_pri_search - search a splitting node
+//----------------------------------------------------------------------
+
+void ANNkd_split::ann_pri_search(ANNdist box_dist)
+{
+	ANNdist new_dist;					// distance to child visited later
+										// distance to cutting plane
+	ANNcoord cut_diff = ANNprQ[cut_dim] - cut_val;
+
+	if (cut_diff < 0) {					// left of cutting plane
+		ANNcoord box_diff = cd_bnds[ANN_LO] - ANNprQ[cut_dim];
+		if (box_diff < 0)				// within bounds - ignore
+			box_diff = 0;
+										// distance to further box
+		new_dist = (ANNdist) ANN_SUM(box_dist,
+				ANN_DIFF(ANN_POW(box_diff), ANN_POW(cut_diff)));
+
+		if (child[ANN_HI] != KD_TRIVIAL)// enqueue if not trivial
+			ANNprBoxPQ->insert(new_dist, child[ANN_HI]);
+										// continue with closer child
+		child[ANN_LO]->ann_pri_search(box_dist);
+	}
+	else {								// right of cutting plane
+		ANNcoord box_diff = ANNprQ[cut_dim] - cd_bnds[ANN_HI];
+		if (box_diff < 0)				// within bounds - ignore
+			box_diff = 0;
+										// distance to further box
+		new_dist = (ANNdist) ANN_SUM(box_dist,
+				ANN_DIFF(ANN_POW(box_diff), ANN_POW(cut_diff)));
+
+		if (child[ANN_LO] != KD_TRIVIAL)// enqueue if not trivial
+			ANNprBoxPQ->insert(new_dist, child[ANN_LO]);
+										// continue with closer child
+		child[ANN_HI]->ann_pri_search(box_dist);
+	}
+	ANN_SPL(1)							// one more splitting node visited
+	ANN_FLOP(8)							// increment floating ops
+}
+
+//----------------------------------------------------------------------
+//	kd_leaf::ann_pri_search - search points in a leaf node
+//
+//		This is virtually identical to the ann_search for standard search.
+//----------------------------------------------------------------------
+
+void ANNkd_leaf::ann_pri_search(ANNdist box_dist)
+{
+	register ANNdist dist;				// distance to data point
+	register ANNcoord* pp;				// data coordinate pointer
+	register ANNcoord* qq;				// query coordinate pointer
+	register ANNdist min_dist;			// distance to k-th closest point
+	register ANNcoord t;
+	register int d;
+
+	min_dist = ANNprPointMK->max_key(); // k-th smallest distance so far
+
+	for (int i = 0; i < n_pts; i++) {	// check points in bucket
+
+		pp = ANNprPts[bkt[i]];			// first coord of next data point
+		qq = ANNprQ;					// first coord of query point
+		dist = 0;
+
+		for(d = 0; d < ANNprDim; d++) {
+			ANN_COORD(1)				// one more coordinate hit
+			ANN_FLOP(4)					// increment floating ops
+
+			t = *(qq++) - *(pp++);		// compute length and adv coordinate
+										// exceeds dist to k-th smallest?
+			if( (dist = ANN_SUM(dist, ANN_POW(t))) > min_dist) {
+				break;
+			}
+		}
+
+		if (d >= ANNprDim &&					// among the k best?
+		   (ANN_ALLOW_SELF_MATCH || dist!=0)) { // and no self-match problem
+												// add it to the list
+			ANNprPointMK->insert(dist, bkt[i]);
+			min_dist = ANNprPointMK->max_key();
+		}
+	}
+	ANN_LEAF(1)							// one more leaf node visited
+	ANN_PTS(n_pts)						// increment points visited
+	ANNptsVisited += n_pts;				// increment number of points visited
+}
diff --git a/contrib/ANN/src/kd_pr_search.h b/contrib/ANN/src/kd_pr_search.h
new file mode 100644
index 0000000000..39e048418d
--- /dev/null
+++ b/contrib/ANN/src/kd_pr_search.h
@@ -0,0 +1,49 @@
+//----------------------------------------------------------------------
+// File:			kd_pr_search.h
+// Programmer:		Sunil Arya and David Mount
+// Description:		Priority kd-tree search
+// Last modified:	01/04/05 (Version 1.0)
+//----------------------------------------------------------------------
+// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
+// David Mount.  All Rights Reserved.
+// 
+// This software and related documentation is part of the Approximate
+// Nearest Neighbor Library (ANN).  This software is provided under
+// the provisions of the Lesser GNU Public License (LGPL).  See the
+// file ../ReadMe.txt for further information.
+// 
+// The University of Maryland (U.M.) and the authors make no
+// representations about the suitability or fitness of this software for
+// any purpose.  It is provided "as is" without express or implied
+// warranty.
+//----------------------------------------------------------------------
+// History:
+//	Revision 0.1  03/04/98
+//		Initial release
+//----------------------------------------------------------------------
+
+#ifndef ANN_kd_pr_search_H
+#define ANN_kd_pr_search_H
+
+#include "kd_tree.h"					// kd-tree declarations
+#include "kd_util.h"					// kd-tree utilities
+#include "pr_queue.h"					// priority queue declarations
+#include "pr_queue_k.h"					// k-element priority queue
+
+#include <ANN/ANNperf.h>				// performance evaluation
+
+//----------------------------------------------------------------------
+//	Global variables
+//		Active for the life of each call to Appx_Near_Neigh() or
+//		Appx_k_Near_Neigh().
+//----------------------------------------------------------------------
+
+extern double			ANNprEps;		// the error bound
+extern int				ANNprDim;		// dimension of space
+extern ANNpoint			ANNprQ;			// query point
+extern double			ANNprMaxErr;	// max tolerable squared error
+extern ANNpointArray	ANNprPts;		// the points
+extern ANNpr_queue		*ANNprBoxPQ;	// priority queue for boxes
+extern ANNmin_k			*ANNprPointMK;	// set of k closest points
+
+#endif
diff --git a/contrib/ANN/src/kd_search.cpp b/contrib/ANN/src/kd_search.cpp
new file mode 100644
index 0000000000..5004ef798c
--- /dev/null
+++ b/contrib/ANN/src/kd_search.cpp
@@ -0,0 +1,210 @@
+//----------------------------------------------------------------------
+// File:			kd_search.cpp
+// Programmer:		Sunil Arya and David Mount
+// Description:		Standard kd-tree search
+// Last modified:	01/04/05 (Version 1.0)
+//----------------------------------------------------------------------
+// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
+// David Mount.  All Rights Reserved.
+// 
+// This software and related documentation is part of the Approximate
+// Nearest Neighbor Library (ANN).  This software is provided under
+// the provisions of the Lesser GNU Public License (LGPL).  See the
+// file ../ReadMe.txt for further information.
+// 
+// The University of Maryland (U.M.) and the authors make no
+// representations about the suitability or fitness of this software for
+// any purpose.  It is provided "as is" without express or implied
+// warranty.
+//----------------------------------------------------------------------
+// History:
+//	Revision 0.1  03/04/98
+//		Initial release
+//	Revision 1.0  04/01/05
+//		Changed names LO, HI to ANN_LO, ANN_HI
+//----------------------------------------------------------------------
+
+#include "kd_search.h"					// kd-search declarations
+
+//----------------------------------------------------------------------
+//	Approximate nearest neighbor searching by kd-tree search
+//		The kd-tree is searched for an approximate nearest neighbor.
+//		The point is returned through one of the arguments, and the
+//		distance returned is the squared distance to this point.
+//
+//		The method used for searching the kd-tree is an approximate
+//		adaptation of the search algorithm described by Friedman,
+//		Bentley, and Finkel, ``An algorithm for finding best matches
+//		in logarithmic expected time,'' ACM Transactions on Mathematical
+//		Software, 3(3):209-226, 1977).
+//
+//		The algorithm operates recursively.  When first encountering a
+//		node of the kd-tree we first visit the child which is closest to
+//		the query point.  On return, we decide whether we want to visit
+//		the other child.  If the box containing the other child exceeds
+//		1/(1+eps) times the current best distance, then we skip it (since
+//		any point found in this child cannot be closer to the query point
+//		by more than this factor.)  Otherwise, we visit it recursively.
+//		The distance between a box and the query point is computed exactly
+//		(not approximated as is often done in kd-tree), using incremental
+//		distance updates, as described by Arya and Mount in ``Algorithms
+//		for fast vector quantization,'' Proc.  of DCC '93: Data Compression
+//		Conference, eds. J. A. Storer and M. Cohn, IEEE Press, 1993,
+//		381-390.
+//
+//		The main entry points is annkSearch() which sets things up and
+//		then call the recursive routine ann_search().  This is a recursive
+//		routine which performs the processing for one node in the kd-tree.
+//		There are two versions of this virtual procedure, one for splitting
+//		nodes and one for leaves.  When a splitting node is visited, we
+//		determine which child to visit first (the closer one), and visit
+//		the other child on return.  When a leaf is visited, we compute
+//		the distances to the points in the buckets, and update information
+//		on the closest points.
+//
+//		Some trickery is used to incrementally update the distance from
+//		a kd-tree rectangle to the query point.  This comes about from
+//		the fact that which each successive split, only one component
+//		(along the dimension that is split) of the squared distance to
+//		the child rectangle is different from the squared distance to
+//		the parent rectangle.
+//----------------------------------------------------------------------
+
+//----------------------------------------------------------------------
+//		To keep argument lists short, a number of global variables
+//		are maintained which are common to all the recursive calls.
+//		These are given below.
+//----------------------------------------------------------------------
+
+int				ANNkdDim;				// dimension of space
+ANNpoint		ANNkdQ;					// query point
+double			ANNkdMaxErr;			// max tolerable squared error
+ANNpointArray	ANNkdPts;				// the points
+ANNmin_k		*ANNkdPointMK;			// set of k closest points
+
+//----------------------------------------------------------------------
+//	annkSearch - search for the k nearest neighbors
+//----------------------------------------------------------------------
+
+void ANNkd_tree::annkSearch(
+	ANNpoint			q,				// the query point
+	int					k,				// number of near neighbors to return
+	ANNidxArray			nn_idx,			// nearest neighbor indices (returned)
+	ANNdistArray		dd,				// the approximate nearest neighbor
+	double				eps)			// the error bound
+{
+
+	ANNkdDim = dim;						// copy arguments to static equivs
+	ANNkdQ = q;
+	ANNkdPts = pts;
+	ANNptsVisited = 0;					// initialize count of points visited
+
+	if (k > n_pts) {					// too many near neighbors?
+		annError("Requesting more near neighbors than data points", ANNabort);
+	}
+
+	ANNkdMaxErr = ANN_POW(1.0 + eps);
+	ANN_FLOP(2)							// increment floating op count
+
+	ANNkdPointMK = new ANNmin_k(k);		// create set for closest k points
+										// search starting at the root
+	root->ann_search(annBoxDistance(q, bnd_box_lo, bnd_box_hi, dim));
+
+	for (int i = 0; i < k; i++) {		// extract the k-th closest points
+		dd[i] = ANNkdPointMK->ith_smallest_key(i);
+		nn_idx[i] = ANNkdPointMK->ith_smallest_info(i);
+	}
+	delete ANNkdPointMK;				// deallocate closest point set
+}
+
+//----------------------------------------------------------------------
+//	kd_split::ann_search - search a splitting node
+//----------------------------------------------------------------------
+
+void ANNkd_split::ann_search(ANNdist box_dist)
+{
+										// check dist calc term condition
+	if (ANNmaxPtsVisited != 0 && ANNptsVisited > ANNmaxPtsVisited) return;
+
+										// distance to cutting plane
+	ANNcoord cut_diff = ANNkdQ[cut_dim] - cut_val;
+
+	if (cut_diff < 0) {					// left of cutting plane
+		child[ANN_LO]->ann_search(box_dist);// visit closer child first
+
+		ANNcoord box_diff = cd_bnds[ANN_LO] - ANNkdQ[cut_dim];
+		if (box_diff < 0)				// within bounds - ignore
+			box_diff = 0;
+										// distance to further box
+		box_dist = (ANNdist) ANN_SUM(box_dist,
+				ANN_DIFF(ANN_POW(box_diff), ANN_POW(cut_diff)));
+
+										// visit further child if close enough
+		if (box_dist * ANNkdMaxErr < ANNkdPointMK->max_key())
+			child[ANN_HI]->ann_search(box_dist);
+
+	}
+	else {								// right of cutting plane
+		child[ANN_HI]->ann_search(box_dist);// visit closer child first
+
+		ANNcoord box_diff = ANNkdQ[cut_dim] - cd_bnds[ANN_HI];
+		if (box_diff < 0)				// within bounds - ignore
+			box_diff = 0;
+										// distance to further box
+		box_dist = (ANNdist) ANN_SUM(box_dist,
+				ANN_DIFF(ANN_POW(box_diff), ANN_POW(cut_diff)));
+
+										// visit further child if close enough
+		if (box_dist * ANNkdMaxErr < ANNkdPointMK->max_key())
+			child[ANN_LO]->ann_search(box_dist);
+
+	}
+	ANN_FLOP(10)						// increment floating ops
+	ANN_SPL(1)							// one more splitting node visited
+}
+
+//----------------------------------------------------------------------
+//	kd_leaf::ann_search - search points in a leaf node
+//		Note: The unreadability of this code is the result of
+//		some fine tuning to replace indexing by pointer operations.
+//----------------------------------------------------------------------
+
+void ANNkd_leaf::ann_search(ANNdist box_dist)
+{
+	register ANNdist dist;				// distance to data point
+	register ANNcoord* pp;				// data coordinate pointer
+	register ANNcoord* qq;				// query coordinate pointer
+	register ANNdist min_dist;			// distance to k-th closest point
+	register ANNcoord t;
+	register int d;
+
+	min_dist = ANNkdPointMK->max_key(); // k-th smallest distance so far
+
+	for (int i = 0; i < n_pts; i++) {	// check points in bucket
+
+		pp = ANNkdPts[bkt[i]];			// first coord of next data point
+		qq = ANNkdQ;					// first coord of query point
+		dist = 0;
+
+		for(d = 0; d < ANNkdDim; d++) {
+			ANN_COORD(1)				// one more coordinate hit
+			ANN_FLOP(4)					// increment floating ops
+
+			t = *(qq++) - *(pp++);		// compute length and adv coordinate
+										// exceeds dist to k-th smallest?
+			if( (dist = ANN_SUM(dist, ANN_POW(t))) > min_dist) {
+				break;
+			}
+		}
+
+		if (d >= ANNkdDim &&					// among the k best?
+		   (ANN_ALLOW_SELF_MATCH || dist!=0)) { // and no self-match problem
+												// add it to the list
+			ANNkdPointMK->insert(dist, bkt[i]);
+			min_dist = ANNkdPointMK->max_key();
+		}
+	}
+	ANN_LEAF(1)							// one more leaf node visited
+	ANN_PTS(n_pts)						// increment points visited
+	ANNptsVisited += n_pts;				// increment number of points visited
+}
diff --git a/contrib/ANN/src/kd_search.h b/contrib/ANN/src/kd_search.h
new file mode 100644
index 0000000000..1adcdd4008
--- /dev/null
+++ b/contrib/ANN/src/kd_search.h
@@ -0,0 +1,48 @@
+//----------------------------------------------------------------------
+// File:			kd_search.h
+// Programmer:		Sunil Arya and David Mount
+// Description:		Standard kd-tree search
+// Last modified:	01/04/05 (Version 1.0)
+//----------------------------------------------------------------------
+// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
+// David Mount.  All Rights Reserved.
+// 
+// This software and related documentation is part of the Approximate
+// Nearest Neighbor Library (ANN).  This software is provided under
+// the provisions of the Lesser GNU Public License (LGPL).  See the
+// file ../ReadMe.txt for further information.
+// 
+// The University of Maryland (U.M.) and the authors make no
+// representations about the suitability or fitness of this software for
+// any purpose.  It is provided "as is" without express or implied
+// warranty.
+//----------------------------------------------------------------------
+// History:
+//	Revision 0.1  03/04/98
+//		Initial release
+//----------------------------------------------------------------------
+
+#ifndef ANN_kd_search_H
+#define ANN_kd_search_H
+
+#include "kd_tree.h"					// kd-tree declarations
+#include "kd_util.h"					// kd-tree utilities
+#include "pr_queue_k.h"					// k-element priority queue
+
+#include <ANN/ANNperf.h>				// performance evaluation
+
+//----------------------------------------------------------------------
+//	More global variables
+//		These are active for the life of each call to annkSearch(). They
+//		are set to save the number of variables that need to be passed
+//		among the various search procedures.
+//----------------------------------------------------------------------
+
+extern int				ANNkdDim;		// dimension of space (static copy)
+extern ANNpoint			ANNkdQ;			// query point (static copy)
+extern double			ANNkdMaxErr;	// max tolerable squared error
+extern ANNpointArray	ANNkdPts;		// the points (static copy)
+extern ANNmin_k			*ANNkdPointMK;	// set of k closest points
+extern int				ANNptsVisited;	// number of points visited
+
+#endif
diff --git a/contrib/ANN/src/kd_split.cpp b/contrib/ANN/src/kd_split.cpp
new file mode 100644
index 0000000000..8d5b3d8735
--- /dev/null
+++ b/contrib/ANN/src/kd_split.cpp
@@ -0,0 +1,428 @@
+//----------------------------------------------------------------------
+// File:			kd_split.cpp
+// Programmer:		Sunil Arya and David Mount
+// Description:		Methods for splitting kd-trees
+// Last modified:	01/04/05 (Version 1.0)
+//----------------------------------------------------------------------
+// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
+// David Mount.  All Rights Reserved.
+// 
+// This software and related documentation is part of the Approximate
+// Nearest Neighbor Library (ANN).  This software is provided under
+// the provisions of the Lesser GNU Public License (LGPL).  See the
+// file ../ReadMe.txt for further information.
+// 
+// The University of Maryland (U.M.) and the authors make no
+// representations about the suitability or fitness of this software for
+// any purpose.  It is provided "as is" without express or implied
+// warranty.
+//----------------------------------------------------------------------
+// History:
+//	Revision 0.1  03/04/98
+//		Initial release
+//	Revision 1.0  04/01/05
+//----------------------------------------------------------------------
+
+#include "kd_tree.h"					// kd-tree definitions
+#include "kd_util.h"					// kd-tree utilities
+#include "kd_split.h"					// splitting functions
+
+//----------------------------------------------------------------------
+//	Constants
+//----------------------------------------------------------------------
+
+const double ERR = 0.001;				// a small value
+const double FS_ASPECT_RATIO = 3.0;		// maximum allowed aspect ratio
+										// in fair split. Must be >= 2.
+
+//----------------------------------------------------------------------
+//	kd_split - Bentley's standard splitting routine for kd-trees
+//		Find the dimension of the greatest spread, and split
+//		just before the median point along this dimension.
+//----------------------------------------------------------------------
+
+void kd_split(
+	ANNpointArray		pa,				// point array (permuted on return)
+	ANNidxArray			pidx,			// point indices
+	const ANNorthRect	&bnds,			// bounding rectangle for cell
+	int					n,				// number of points
+	int					dim,			// dimension of space
+	int					&cut_dim,		// cutting dimension (returned)
+	ANNcoord			&cut_val,		// cutting value (returned)
+	int					&n_lo)			// num of points on low side (returned)
+{
+										// find dimension of maximum spread
+	cut_dim = annMaxSpread(pa, pidx, n, dim);
+	n_lo = n/2;							// median rank
+										// split about median
+	annMedianSplit(pa, pidx, n, cut_dim, cut_val, n_lo);
+}
+
+//----------------------------------------------------------------------
+//	midpt_split - midpoint splitting rule for box-decomposition trees
+//
+//		This is the simplest splitting rule that guarantees boxes
+//		of bounded aspect ratio.  It simply cuts the box with the
+//		longest side through its midpoint.  If there are ties, it
+//		selects the dimension with the maximum point spread.
+//
+//		WARNING: This routine (while simple) doesn't seem to work
+//		well in practice in high dimensions, because it tends to
+//		generate a large number of trivial and/or unbalanced splits.
+//		Either kd_split(), sl_midpt_split(), or fair_split() are
+//		recommended, instead.
+//----------------------------------------------------------------------
+
+void midpt_split(
+	ANNpointArray		pa,				// point array
+	ANNidxArray			pidx,			// point indices (permuted on return)
+	const ANNorthRect	&bnds,			// bounding rectangle for cell
+	int					n,				// number of points
+	int					dim,			// dimension of space
+	int					&cut_dim,		// cutting dimension (returned)
+	ANNcoord			&cut_val,		// cutting value (returned)
+	int					&n_lo)			// num of points on low side (returned)
+{
+	int d;
+
+	ANNcoord max_length = bnds.hi[0] - bnds.lo[0];
+	for (d = 1; d < dim; d++) {			// find length of longest box side
+		ANNcoord length = bnds.hi[d] - bnds.lo[d];
+		if (length > max_length) {
+			max_length = length;
+		}
+	}
+	ANNcoord max_spread = -1;			// find long side with most spread
+	for (d = 0; d < dim; d++) {
+										// is it among longest?
+		if (double(bnds.hi[d] - bnds.lo[d]) >= (1-ERR)*max_length) {
+										// compute its spread
+			ANNcoord spr = annSpread(pa, pidx, n, d);
+			if (spr > max_spread) {		// is it max so far?
+				max_spread = spr;
+				cut_dim = d;
+			}
+		}
+	}
+										// split along cut_dim at midpoint
+	cut_val = (bnds.lo[cut_dim] + bnds.hi[cut_dim]) / 2;
+										// permute points accordingly
+	int br1, br2;
+	annPlaneSplit(pa, pidx, n, cut_dim, cut_val, br1, br2);
+	//------------------------------------------------------------------
+	//	On return:		pa[0..br1-1] < cut_val
+	//					pa[br1..br2-1] == cut_val
+	//					pa[br2..n-1] > cut_val
+	//
+	//	We can set n_lo to any value in the range [br1..br2].
+	//	We choose split so that points are most evenly divided.
+	//------------------------------------------------------------------
+	if (br1 > n/2) n_lo = br1;
+	else if (br2 < n/2) n_lo = br2;
+	else n_lo = n/2;
+}
+
+//----------------------------------------------------------------------
+//	sl_midpt_split - sliding midpoint splitting rule
+//
+//		This is a modification of midpt_split, which has the nonsensical
+//		name "sliding midpoint".  The idea is that we try to use the
+//		midpoint rule, by bisecting the longest side.  If there are
+//		ties, the dimension with the maximum spread is selected.  If,
+//		however, the midpoint split produces a trivial split (no points
+//		on one side of the splitting plane) then we slide the splitting
+//		(maintaining its orientation) until it produces a nontrivial
+//		split. For example, if the splitting plane is along the x-axis,
+//		and all the data points have x-coordinate less than the x-bisector,
+//		then the split is taken along the maximum x-coordinate of the
+//		data points.
+//
+//		Intuitively, this rule cannot generate trivial splits, and
+//		hence avoids midpt_split's tendency to produce trees with
+//		a very large number of nodes.
+//
+//----------------------------------------------------------------------
+
+void sl_midpt_split(
+	ANNpointArray		pa,				// point array
+	ANNidxArray			pidx,			// point indices (permuted on return)
+	const ANNorthRect	&bnds,			// bounding rectangle for cell
+	int					n,				// number of points
+	int					dim,			// dimension of space
+	int					&cut_dim,		// cutting dimension (returned)
+	ANNcoord			&cut_val,		// cutting value (returned)
+	int					&n_lo)			// num of points on low side (returned)
+{
+	int d;
+
+	ANNcoord max_length = bnds.hi[0] - bnds.lo[0];
+	for (d = 1; d < dim; d++) {			// find length of longest box side
+		ANNcoord length = bnds.hi[d] - bnds.lo[d];
+		if (length > max_length) {
+			max_length = length;
+		}
+	}
+	ANNcoord max_spread = -1;			// find long side with most spread
+	for (d = 0; d < dim; d++) {
+										// is it among longest?
+		if ((bnds.hi[d] - bnds.lo[d]) >= (1-ERR)*max_length) {
+										// compute its spread
+			ANNcoord spr = annSpread(pa, pidx, n, d);
+			if (spr > max_spread) {		// is it max so far?
+				max_spread = spr;
+				cut_dim = d;
+			}
+		}
+	}
+										// ideal split at midpoint
+	ANNcoord ideal_cut_val = (bnds.lo[cut_dim] + bnds.hi[cut_dim])/2;
+
+	ANNcoord min, max;
+	annMinMax(pa, pidx, n, cut_dim, min, max);	// find min/max coordinates
+
+	if (ideal_cut_val < min)			// slide to min or max as needed
+		cut_val = min;
+	else if (ideal_cut_val > max)
+		cut_val = max;
+	else
+		cut_val = ideal_cut_val;
+
+										// permute points accordingly
+	int br1, br2;
+	annPlaneSplit(pa, pidx, n, cut_dim, cut_val, br1, br2);
+	//------------------------------------------------------------------
+	//	On return:		pa[0..br1-1] < cut_val
+	//					pa[br1..br2-1] == cut_val
+	//					pa[br2..n-1] > cut_val
+	//
+	//	We can set n_lo to any value in the range [br1..br2] to satisfy
+	//	the exit conditions of the procedure.
+	//
+	//	if ideal_cut_val < min (implying br2 >= 1),
+	//			then we select n_lo = 1 (so there is one point on left) and
+	//	if ideal_cut_val > max (implying br1 <= n-1),
+	//			then we select n_lo = n-1 (so there is one point on right).
+	//	Otherwise, we select n_lo as close to n/2 as possible within
+	//			[br1..br2].
+	//------------------------------------------------------------------
+	if (ideal_cut_val < min) n_lo = 1;
+	else if (ideal_cut_val > max) n_lo = n-1;
+	else if (br1 > n/2) n_lo = br1;
+	else if (br2 < n/2) n_lo = br2;
+	else n_lo = n/2;
+}
+
+//----------------------------------------------------------------------
+//	fair_split - fair-split splitting rule
+//
+//		This is a compromise between the kd-tree splitting rule (which
+//		always splits data points at their median) and the midpoint
+//		splitting rule (which always splits a box through its center.
+//		The goal of this procedure is to achieve both nicely balanced
+//		splits, and boxes of bounded aspect ratio.
+//
+//		A constant FS_ASPECT_RATIO is defined. Given a box, those sides
+//		which can be split so that the ratio of the longest to shortest
+//		side does not exceed ASPECT_RATIO are identified.  Among these
+//		sides, we select the one in which the points have the largest
+//		spread. We then split the points in a manner which most evenly
+//		distributes the points on either side of the splitting plane,
+//		subject to maintaining the bound on the ratio of long to short
+//		sides. To determine that the aspect ratio will be preserved,
+//		we determine the longest side (other than this side), and
+//		determine how narrowly we can cut this side, without causing the
+//		aspect ratio bound to be exceeded (small_piece).
+//
+//		This procedure is more robust than either kd_split or midpt_split,
+//		but is more complicated as well.  When point distribution is
+//		extremely skewed, this degenerates to midpt_split (actually
+//		1/3 point split), and when the points are most evenly distributed,
+//		this degenerates to kd-split.
+//----------------------------------------------------------------------
+
+void fair_split(
+	ANNpointArray		pa,				// point array
+	ANNidxArray			pidx,			// point indices (permuted on return)
+	const ANNorthRect	&bnds,			// bounding rectangle for cell
+	int					n,				// number of points
+	int					dim,			// dimension of space
+	int					&cut_dim,		// cutting dimension (returned)
+	ANNcoord			&cut_val,		// cutting value (returned)
+	int					&n_lo)			// num of points on low side (returned)
+{
+	int d;
+	ANNcoord max_length = bnds.hi[0] - bnds.lo[0];
+	cut_dim = 0;
+	for (d = 1; d < dim; d++) {			// find length of longest box side
+		ANNcoord length = bnds.hi[d] - bnds.lo[d];
+		if (length > max_length) {
+			max_length = length;
+			cut_dim = d;
+		}
+	}
+
+	ANNcoord max_spread = 0;			// find legal cut with max spread
+	cut_dim = 0;
+	for (d = 0; d < dim; d++) {
+		ANNcoord length = bnds.hi[d] - bnds.lo[d];
+										// is this side midpoint splitable
+										// without violating aspect ratio?
+		if (((double) max_length)*2.0/((double) length) <= FS_ASPECT_RATIO) {
+										// compute spread along this dim
+			ANNcoord spr = annSpread(pa, pidx, n, d);
+			if (spr > max_spread) {		// best spread so far
+				max_spread = spr;
+				cut_dim = d;			// this is dimension to cut
+			}
+		}
+	}
+
+	max_length = 0;						// find longest side other than cut_dim
+	for (d = 0; d < dim; d++) {
+		ANNcoord length = bnds.hi[d] - bnds.lo[d];
+		if (d != cut_dim && length > max_length)
+			max_length = length;
+	}
+										// consider most extreme splits
+	ANNcoord small_piece = max_length / FS_ASPECT_RATIO;
+	ANNcoord lo_cut = bnds.lo[cut_dim] + small_piece;// lowest legal cut
+	ANNcoord hi_cut = bnds.hi[cut_dim] - small_piece;// highest legal cut
+
+	int br1, br2;
+										// is median below lo_cut ?
+	if (annSplitBalance(pa, pidx, n, cut_dim, lo_cut) >= 0) {
+		cut_val = lo_cut;				// cut at lo_cut
+		annPlaneSplit(pa, pidx, n, cut_dim, cut_val, br1, br2);
+		n_lo = br1;
+	}
+										// is median above hi_cut?
+	else if (annSplitBalance(pa, pidx, n, cut_dim, hi_cut) <= 0) {
+		cut_val = hi_cut;				// cut at hi_cut
+		annPlaneSplit(pa, pidx, n, cut_dim, cut_val, br1, br2);
+		n_lo = br2;
+	}
+	else {								// median cut preserves asp ratio
+		n_lo = n/2;						// split about median
+		annMedianSplit(pa, pidx, n, cut_dim, cut_val, n_lo);
+	}
+}
+
+//----------------------------------------------------------------------
+//	sl_fair_split - sliding fair split splitting rule
+//
+//		Sliding fair split is a splitting rule that combines the
+//		strengths of both fair split with sliding midpoint split.
+//		Fair split tends to produce balanced splits when the points
+//		are roughly uniformly distributed, but it can produce many
+//		trivial splits when points are highly clustered.  Sliding
+//		midpoint never produces trivial splits, and shrinks boxes
+//		nicely if points are highly clustered, but it may produce
+//		rather unbalanced splits when points are unclustered but not
+//		quite uniform.
+//
+//		Sliding fair split is based on the theory that there are two
+//		types of splits that are "good": balanced splits that produce
+//		fat boxes, and unbalanced splits provided the cell with fewer
+//		points is fat.
+//
+//		This splitting rule operates by first computing the longest
+//		side of the current bounding box.  Then it asks which sides
+//		could be split (at the midpoint) and still satisfy the aspect
+//		ratio bound with respect to this side.	Among these, it selects
+//		the side with the largest spread (as fair split would).	 It
+//		then considers the most extreme cuts that would be allowed by
+//		the aspect ratio bound.	 This is done by dividing the longest
+//		side of the box by the aspect ratio bound.	If the median cut
+//		lies between these extreme cuts, then we use the median cut.
+//		If not, then consider the extreme cut that is closer to the
+//		median.	 If all the points lie to one side of this cut, then
+//		we slide the cut until it hits the first point.	 This may
+//		violate the aspect ratio bound, but will never generate empty
+//		cells.	However the sibling of every such skinny cell is fat,
+//		and hence packing arguments still apply.
+//
+//----------------------------------------------------------------------
+
+void sl_fair_split(
+	ANNpointArray		pa,				// point array
+	ANNidxArray			pidx,			// point indices (permuted on return)
+	const ANNorthRect	&bnds,			// bounding rectangle for cell
+	int					n,				// number of points
+	int					dim,			// dimension of space
+	int					&cut_dim,		// cutting dimension (returned)
+	ANNcoord			&cut_val,		// cutting value (returned)
+	int					&n_lo)			// num of points on low side (returned)
+{
+	int d;
+	ANNcoord min, max;					// min/max coordinates
+	int br1, br2;						// split break points
+
+	ANNcoord max_length = bnds.hi[0] - bnds.lo[0];
+	cut_dim = 0;
+	for (d = 1; d < dim; d++) {			// find length of longest box side
+		ANNcoord length = bnds.hi[d] - bnds.lo[d];
+		if (length	> max_length) {
+			max_length = length;
+			cut_dim = d;
+		}
+	}
+
+	ANNcoord max_spread = 0;			// find legal cut with max spread
+	cut_dim = 0;
+	for (d = 0; d < dim; d++) {
+		ANNcoord length = bnds.hi[d] - bnds.lo[d];
+										// is this side midpoint splitable
+										// without violating aspect ratio?
+		if (((double) max_length)*2.0/((double) length) <= FS_ASPECT_RATIO) {
+										// compute spread along this dim
+			ANNcoord spr = annSpread(pa, pidx, n, d);
+			if (spr > max_spread) {		// best spread so far
+				max_spread = spr;
+				cut_dim = d;			// this is dimension to cut
+			}
+		}
+	}
+
+	max_length = 0;						// find longest side other than cut_dim
+	for (d = 0; d < dim; d++) {
+		ANNcoord length = bnds.hi[d] - bnds.lo[d];
+		if (d != cut_dim && length > max_length)
+			max_length = length;
+	}
+										// consider most extreme splits
+	ANNcoord small_piece = max_length / FS_ASPECT_RATIO;
+	ANNcoord lo_cut = bnds.lo[cut_dim] + small_piece;// lowest legal cut
+	ANNcoord hi_cut = bnds.hi[cut_dim] - small_piece;// highest legal cut
+										// find min and max along cut_dim
+	annMinMax(pa, pidx, n, cut_dim, min, max);
+										// is median below lo_cut?
+	if (annSplitBalance(pa, pidx, n, cut_dim, lo_cut) >= 0) {
+		if (max > lo_cut) {				// are any points above lo_cut?
+			cut_val = lo_cut;			// cut at lo_cut
+			annPlaneSplit(pa, pidx, n, cut_dim, cut_val, br1, br2);
+			n_lo = br1;					// balance if there are ties
+		}
+		else {							// all points below lo_cut
+			cut_val = max;				// cut at max value
+			annPlaneSplit(pa, pidx, n, cut_dim, cut_val, br1, br2);
+			n_lo = n-1;
+		}
+	}
+										// is median above hi_cut?
+	else if (annSplitBalance(pa, pidx, n, cut_dim, hi_cut) <= 0) {
+		if (min < hi_cut) {				// are any points below hi_cut?
+			cut_val = hi_cut;			// cut at hi_cut
+			annPlaneSplit(pa, pidx, n, cut_dim, cut_val, br1, br2);
+			n_lo = br2;					// balance if there are ties
+		}
+		else {							// all points above hi_cut
+			cut_val = min;				// cut at min value
+			annPlaneSplit(pa, pidx, n, cut_dim, cut_val, br1, br2);
+			n_lo = 1;
+		}
+	}
+	else {								// median cut is good enough
+		n_lo = n/2;						// split about median
+		annMedianSplit(pa, pidx, n, cut_dim, cut_val, n_lo);
+	}
+}
diff --git a/contrib/ANN/src/kd_split.h b/contrib/ANN/src/kd_split.h
new file mode 100644
index 0000000000..ef80461ead
--- /dev/null
+++ b/contrib/ANN/src/kd_split.h
@@ -0,0 +1,85 @@
+//----------------------------------------------------------------------
+// File:			kd_split.h
+// Programmer:		Sunil Arya and David Mount
+// Description:		Methods for splitting kd-trees
+// Last modified:	01/04/05 (Version 1.0)
+//----------------------------------------------------------------------
+// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
+// David Mount.  All Rights Reserved.
+// 
+// This software and related documentation is part of the Approximate
+// Nearest Neighbor Library (ANN).  This software is provided under
+// the provisions of the Lesser GNU Public License (LGPL).  See the
+// file ../ReadMe.txt for further information.
+// 
+// The University of Maryland (U.M.) and the authors make no
+// representations about the suitability or fitness of this software for
+// any purpose.  It is provided "as is" without express or implied
+// warranty.
+//----------------------------------------------------------------------
+// History:
+//	Revision 0.1  03/04/98
+//		Initial release
+//----------------------------------------------------------------------
+
+#ifndef ANN_KD_SPLIT_H
+#define ANN_KD_SPLIT_H
+
+#include "kd_tree.h"					// kd-tree definitions
+
+//----------------------------------------------------------------------
+//	External entry points
+//		These are all splitting procedures for kd-trees.
+//----------------------------------------------------------------------
+
+void kd_split(							// standard (optimized) kd-splitter
+	ANNpointArray		pa,				// point array (unaltered)
+	ANNidxArray			pidx,			// point indices (permuted on return)
+	const ANNorthRect	&bnds,			// bounding rectangle for cell
+	int					n,				// number of points
+	int					dim,			// dimension of space
+	int					&cut_dim,		// cutting dimension (returned)
+	ANNcoord			&cut_val,		// cutting value (returned)
+	int					&n_lo);			// num of points on low side (returned)
+
+void midpt_split(						// midpoint kd-splitter
+	ANNpointArray		pa,				// point array (unaltered)
+	ANNidxArray			pidx,			// point indices (permuted on return)
+	const ANNorthRect	&bnds,			// bounding rectangle for cell
+	int					n,				// number of points
+	int					dim,			// dimension of space
+	int					&cut_dim,		// cutting dimension (returned)
+	ANNcoord			&cut_val,		// cutting value (returned)
+	int					&n_lo);			// num of points on low side (returned)
+
+void sl_midpt_split(					// sliding midpoint kd-splitter
+	ANNpointArray		pa,				// point array (unaltered)
+	ANNidxArray			pidx,			// point indices (permuted on return)
+	const ANNorthRect	&bnds,			// bounding rectangle for cell
+	int					n,				// number of points
+	int					dim,			// dimension of space
+	int					&cut_dim,		// cutting dimension (returned)
+	ANNcoord			&cut_val,		// cutting value (returned)
+	int					&n_lo);			// num of points on low side (returned)
+
+void fair_split(						// fair-split kd-splitter
+	ANNpointArray		pa,				// point array (unaltered)
+	ANNidxArray			pidx,			// point indices (permuted on return)
+	const ANNorthRect	&bnds,			// bounding rectangle for cell
+	int					n,				// number of points
+	int					dim,			// dimension of space
+	int					&cut_dim,		// cutting dimension (returned)
+	ANNcoord			&cut_val,		// cutting value (returned)
+	int					&n_lo);			// num of points on low side (returned)
+
+void sl_fair_split(						// sliding fair-split kd-splitter
+	ANNpointArray		pa,				// point array (unaltered)
+	ANNidxArray			pidx,			// point indices (permuted on return)
+	const ANNorthRect	&bnds,			// bounding rectangle for cell
+	int					n,				// number of points
+	int					dim,			// dimension of space
+	int					&cut_dim,		// cutting dimension (returned)
+	ANNcoord			&cut_val,		// cutting value (returned)
+	int					&n_lo);			// num of points on low side (returned)
+
+#endif
diff --git a/contrib/ANN/src/kd_tree.cpp b/contrib/ANN/src/kd_tree.cpp
new file mode 100644
index 0000000000..2828fd2cfd
--- /dev/null
+++ b/contrib/ANN/src/kd_tree.cpp
@@ -0,0 +1,405 @@
+//----------------------------------------------------------------------
+// File:			kd_tree.cpp
+// Programmer:		Sunil Arya and David Mount
+// Description:		Basic methods for kd-trees.
+// Last modified:	01/04/05 (Version 1.0)
+//----------------------------------------------------------------------
+// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
+// David Mount.  All Rights Reserved.
+// 
+// This software and related documentation is part of the Approximate
+// Nearest Neighbor Library (ANN).  This software is provided under
+// the provisions of the Lesser GNU Public License (LGPL).  See the
+// file ../ReadMe.txt for further information.
+// 
+// The University of Maryland (U.M.) and the authors make no
+// representations about the suitability or fitness of this software for
+// any purpose.  It is provided "as is" without express or implied
+// warranty.
+//----------------------------------------------------------------------
+// History:
+//	Revision 0.1  03/04/98
+//		Initial release
+//	Revision 1.0  04/01/05
+//		Increased aspect ratio bound (ANN_AR_TOOBIG) from 100 to 1000.
+//		Fixed leaf counts to count trivial leaves.
+//		Added optional pa, pi arguments to Skeleton kd_tree constructor
+//			for use in load constructor.
+//		Added annClose() to eliminate KD_TRIVIAL memory leak.
+//----------------------------------------------------------------------
+
+#include "kd_tree.h"					// kd-tree declarations
+#include "kd_split.h"					// kd-tree splitting rules
+#include "kd_util.h"					// kd-tree utilities
+#include <ANN/ANNperf.h>				// performance evaluation
+
+//----------------------------------------------------------------------
+//	Global data
+//
+//	For some splitting rules, especially with small bucket sizes,
+//	it is possible to generate a large number of empty leaf nodes.
+//	To save storage we allocate a single trivial leaf node which
+//	contains no points.  For messy coding reasons it is convenient
+//	to have it reference a trivial point index.
+//
+//	KD_TRIVIAL is allocated when the first kd-tree is created.  It
+//	must *never* deallocated (since it may be shared by more than
+//	one tree).
+//----------------------------------------------------------------------
+static int				IDX_TRIVIAL[] = {0};	// trivial point index
+ANNkd_leaf				*KD_TRIVIAL = NULL;		// trivial leaf node
+
+//----------------------------------------------------------------------
+//	Printing the kd-tree 
+//		These routines print a kd-tree in reverse inorder (high then
+//		root then low).  (This is so that if you look at the output
+//		from the right side it appear from left to right in standard
+//		inorder.)  When outputting leaves we output only the point
+//		indices rather than the point coordinates. There is an option
+//		to print the point coordinates separately.
+//
+//		The tree printing routine calls the printing routines on the
+//		individual nodes of the tree, passing in the level or depth
+//		in the tree.  The level in the tree is used to print indentation
+//		for readability.
+//----------------------------------------------------------------------
+
+void ANNkd_split::print(				// print splitting node
+		int level,						// depth of node in tree
+		ostream &out)					// output stream
+{
+	child[ANN_HI]->print(level+1, out);	// print high child
+	out << "    ";
+	for (int i = 0; i < level; i++)		// print indentation
+		out << "..";
+	out << "Split cd=" << cut_dim << " cv=" << cut_val;
+	out << " lbnd=" << cd_bnds[ANN_LO];
+	out << " hbnd=" << cd_bnds[ANN_HI];
+	out << "\n";
+	child[ANN_LO]->print(level+1, out);	// print low child
+}
+
+void ANNkd_leaf::print(					// print leaf node
+		int level,						// depth of node in tree
+		ostream &out)					// output stream
+{
+
+	out << "    ";
+	for (int i = 0; i < level; i++)		// print indentation
+		out << "..";
+
+	if (this == KD_TRIVIAL) {			// canonical trivial leaf node
+		out << "Leaf (trivial)\n";
+	}
+	else{
+		out << "Leaf n=" << n_pts << " <";
+		for (int j = 0; j < n_pts; j++) {
+			out << bkt[j];
+			if (j < n_pts-1) out << ",";
+		}
+		out << ">\n";
+	}
+}
+
+void ANNkd_tree::Print(					// print entire tree
+		ANNbool with_pts,				// print points as well?
+		ostream &out)					// output stream
+{
+	out << "ANN Version " << ANNversion << "\n";
+	if (with_pts) {						// print point coordinates
+		out << "    Points:\n";
+		for (int i = 0; i < n_pts; i++) {
+			out << "\t" << i << ": ";
+			annPrintPt(pts[i], dim, out);
+			out << "\n";
+		}
+	}
+	if (root == NULL)					// empty tree?
+		out << "    Null tree.\n";
+	else {
+		root->print(0, out);			// invoke printing at root
+	}
+}
+
+//----------------------------------------------------------------------
+//	kd_tree statistics (for performance evaluation)
+//		This routine compute various statistics information for
+//		a kd-tree.  It is used by the implementors for performance
+//		evaluation of the data structure.
+//----------------------------------------------------------------------
+
+#define MAX(a,b)		((a) > (b) ? (a) : (b))
+
+void ANNkdStats::merge(const ANNkdStats &st)	// merge stats from child 
+{
+	n_lf += st.n_lf;			n_tl += st.n_tl;
+	n_spl += st.n_spl;			n_shr += st.n_shr;
+	depth = MAX(depth, st.depth);
+	sum_ar += st.sum_ar;
+}
+
+//----------------------------------------------------------------------
+//	Update statistics for nodes
+//----------------------------------------------------------------------
+
+const double ANN_AR_TOOBIG = 1000;				// too big an aspect ratio
+
+void ANNkd_leaf::getStats(						// get subtree statistics
+	int					dim,					// dimension of space
+	ANNkdStats			&st,					// stats (modified)
+	ANNorthRect			&bnd_box)				// bounding box
+{
+	st.reset();
+	st.n_lf = 1;								// count this leaf
+	if (this == KD_TRIVIAL) st.n_tl = 1;		// count trivial leaf
+	double ar = annAspectRatio(dim, bnd_box);	// aspect ratio of leaf
+												// incr sum (ignore outliers)
+	st.sum_ar += float(ar < ANN_AR_TOOBIG ? ar : ANN_AR_TOOBIG);
+}
+
+void ANNkd_split::getStats(						// get subtree statistics
+	int					dim,					// dimension of space
+	ANNkdStats			&st,					// stats (modified)
+	ANNorthRect			&bnd_box)				// bounding box
+{
+	ANNkdStats ch_stats;						// stats for children
+												// get stats for low child
+	ANNcoord hv = bnd_box.hi[cut_dim];			// save box bounds
+	bnd_box.hi[cut_dim] = cut_val;				// upper bound for low child
+	ch_stats.reset();							// reset
+	child[ANN_LO]->getStats(dim, ch_stats, bnd_box);
+	st.merge(ch_stats);							// merge them
+	bnd_box.hi[cut_dim] = hv;					// restore bound
+												// get stats for high child
+	ANNcoord lv = bnd_box.lo[cut_dim];			// save box bounds
+	bnd_box.lo[cut_dim] = cut_val;				// lower bound for high child
+	ch_stats.reset();							// reset
+	child[ANN_HI]->getStats(dim, ch_stats, bnd_box);
+	st.merge(ch_stats);							// merge them
+	bnd_box.lo[cut_dim] = lv;					// restore bound
+
+	st.depth++;									// increment depth
+	st.n_spl++;									// increment number of splits
+}
+
+//----------------------------------------------------------------------
+//	getStats
+//		Collects a number of statistics related to kd_tree or
+//		bd_tree.
+//----------------------------------------------------------------------
+
+void ANNkd_tree::getStats(						// get tree statistics
+	ANNkdStats			&st)					// stats (modified)
+{
+	st.reset(dim, n_pts, bkt_size);				// reset stats
+												// create bounding box
+	ANNorthRect bnd_box(dim, bnd_box_lo, bnd_box_hi);
+	if (root != NULL) {							// if nonempty tree
+		root->getStats(dim, st, bnd_box);		// get statistics
+		st.avg_ar = st.sum_ar / st.n_lf;		// average leaf asp ratio
+	}
+}
+
+//----------------------------------------------------------------------
+//	kd_tree destructor
+//		The destructor just frees the various elements that were
+//		allocated in the construction process.
+//----------------------------------------------------------------------
+
+ANNkd_tree::~ANNkd_tree()				// tree destructor
+{
+	if (root != NULL) delete root;
+	if (pidx != NULL) delete [] pidx;
+	if (bnd_box_lo != NULL) annDeallocPt(bnd_box_lo);
+	if (bnd_box_hi != NULL) annDeallocPt(bnd_box_hi);
+}
+
+//----------------------------------------------------------------------
+//	This is called with all use of ANN is finished.  It eliminates the
+//	minor memory leak caused by the allocation of KD_TRIVIAL.
+//----------------------------------------------------------------------
+void annClose()				// close use of ANN
+{
+	if (KD_TRIVIAL != NULL) {
+		delete KD_TRIVIAL;
+		KD_TRIVIAL = NULL;
+	}
+}
+
+//----------------------------------------------------------------------
+//	kd_tree constructors
+//		There is a skeleton kd-tree constructor which sets up a
+//		trivial empty tree.	 The last optional argument allows
+//		the routine to be passed a point index array which is
+//		assumed to be of the proper size (n).  Otherwise, one is
+//		allocated and initialized to the identity.	Warning: In
+//		either case the destructor will deallocate this array.
+//
+//		As a kludge, we need to allocate KD_TRIVIAL if one has not
+//		already been allocated.	 (This is because I'm too dumb to
+//		figure out how to cause a pointer to be allocated at load
+//		time.)
+//----------------------------------------------------------------------
+
+void ANNkd_tree::SkeletonTree(			// construct skeleton tree
+		int n,							// number of points
+		int dd,							// dimension
+		int bs,							// bucket size
+		ANNpointArray pa,				// point array
+		ANNidxArray pi)					// point indices
+{
+	dim = dd;							// initialize basic elements
+	n_pts = n;
+	bkt_size = bs;
+	pts = pa;							// initialize points array
+
+	root = NULL;						// no associated tree yet
+
+	if (pi == NULL) {					// point indices provided?
+		pidx = new ANNidx[n];			// no, allocate space for point indices
+		for (int i = 0; i < n; i++) {
+			pidx[i] = i;				// initially identity
+		}
+	}
+	else {
+		pidx = pi;						// yes, use them
+	}
+
+	bnd_box_lo = bnd_box_hi = NULL;		// bounding box is nonexistent
+	if (KD_TRIVIAL == NULL)				// no trivial leaf node yet?
+		KD_TRIVIAL = new ANNkd_leaf(0, IDX_TRIVIAL);	// allocate it
+}
+
+ANNkd_tree::ANNkd_tree(					// basic constructor
+		int n,							// number of points
+		int dd,							// dimension
+		int bs)							// bucket size
+{  SkeletonTree(n, dd, bs);  }			// construct skeleton tree
+
+//----------------------------------------------------------------------
+//	rkd_tree - recursive procedure to build a kd-tree
+//
+//		Builds a kd-tree for points in pa as indexed through the
+//		array pidx[0..n-1] (typically a subarray of the array used in
+//		the top-level call).  This routine permutes the array pidx,
+//		but does not alter pa[].
+//
+//		The construction is based on a standard algorithm for constructing
+//		the kd-tree (see Friedman, Bentley, and Finkel, ``An algorithm for
+//		finding best matches in logarithmic expected time,'' ACM Transactions
+//		on Mathematical Software, 3(3):209-226, 1977).  The procedure
+//		operates by a simple divide-and-conquer strategy, which determines
+//		an appropriate orthogonal cutting plane (see below), and splits
+//		the points.  When the number of points falls below the bucket size,
+//		we simply store the points in a leaf node's bucket.
+//
+//		One of the arguments is a pointer to a splitting routine,
+//		whose prototype is:
+//		
+//				void split(
+//						ANNpointArray pa,  // complete point array
+//						ANNidxArray pidx,  // point array (permuted on return)
+//						ANNorthRect &bnds, // bounds of current cell
+//						int n,			   // number of points
+//						int dim,		   // dimension of space
+//						int &cut_dim,	   // cutting dimension
+//						ANNcoord &cut_val, // cutting value
+//						int &n_lo)		   // no. of points on low side of cut
+//
+//		This procedure selects a cutting dimension and cutting value,
+//		partitions pa about these values, and returns the number of
+//		points on the low side of the cut.
+//----------------------------------------------------------------------
+
+ANNkd_ptr rkd_tree(				// recursive construction of kd-tree
+	ANNpointArray		pa,				// point array
+	ANNidxArray			pidx,			// point indices to store in subtree
+	int					n,				// number of points
+	int					dim,			// dimension of space
+	int					bsp,			// bucket space
+	ANNorthRect			&bnd_box,		// bounding box for current node
+	ANNkd_splitter		splitter)		// splitting routine
+{
+	if (n <= bsp) {						// n small, make a leaf node
+		if (n == 0)						// empty leaf node
+			return KD_TRIVIAL;			// return (canonical) empty leaf
+		else							// construct the node and return
+			return new ANNkd_leaf(n, pidx); 
+	}
+	else {								// n large, make a splitting node
+		int cd;							// cutting dimension
+		ANNcoord cv;					// cutting value
+		int n_lo;						// number on low side of cut
+		ANNkd_node *lo, *hi;			// low and high children
+
+										// invoke splitting procedure
+		(*splitter)(pa, pidx, bnd_box, n, dim, cd, cv, n_lo);
+
+		ANNcoord lv = bnd_box.lo[cd];	// save bounds for cutting dimension
+		ANNcoord hv = bnd_box.hi[cd];
+
+		bnd_box.hi[cd] = cv;			// modify bounds for left subtree
+		lo = rkd_tree(					// build left subtree
+				pa, pidx, n_lo,			// ...from pidx[0..n_lo-1]
+				dim, bsp, bnd_box, splitter);
+		bnd_box.hi[cd] = hv;			// restore bounds
+
+		bnd_box.lo[cd] = cv;			// modify bounds for right subtree
+		hi = rkd_tree(					// build right subtree
+				pa, pidx + n_lo, n-n_lo,// ...from pidx[n_lo..n-1]
+				dim, bsp, bnd_box, splitter);
+		bnd_box.lo[cd] = lv;			// restore bounds
+
+										// create the splitting node
+		ANNkd_split *ptr = new ANNkd_split(cd, cv, lv, hv, lo, hi);
+
+		return ptr;						// return pointer to this node
+	}
+} 
+
+//----------------------------------------------------------------------
+// kd-tree constructor
+//		This is the main constructor for kd-trees given a set of points.
+//		It first builds a skeleton tree, then computes the bounding box
+//		of the data points, and then invokes rkd_tree() to actually
+//		build the tree, passing it the appropriate splitting routine.
+//----------------------------------------------------------------------
+
+ANNkd_tree::ANNkd_tree(					// construct from point array
+	ANNpointArray		pa,				// point array (with at least n pts)
+	int					n,				// number of points
+	int					dd,				// dimension
+	int					bs,				// bucket size
+	ANNsplitRule		split)			// splitting method
+{
+	SkeletonTree(n, dd, bs);			// set up the basic stuff
+	pts = pa;							// where the points are
+	if (n == 0) return;					// no points--no sweat
+
+	ANNorthRect bnd_box(dd);			// bounding box for points
+	annEnclRect(pa, pidx, n, dd, bnd_box);// construct bounding rectangle
+										// copy to tree structure
+	bnd_box_lo = annCopyPt(dd, bnd_box.lo);
+	bnd_box_hi = annCopyPt(dd, bnd_box.hi);
+
+	switch (split) {					// build by rule
+	case ANN_KD_STD:					// standard kd-splitting rule
+		root = rkd_tree(pa, pidx, n, dd, bs, bnd_box, kd_split);
+		break;
+	case ANN_KD_MIDPT:					// midpoint split
+		root = rkd_tree(pa, pidx, n, dd, bs, bnd_box, midpt_split);
+		break;
+	case ANN_KD_FAIR:					// fair split
+		root = rkd_tree(pa, pidx, n, dd, bs, bnd_box, fair_split);
+		break;
+	case ANN_KD_SUGGEST:				// best (in our opinion)
+	case ANN_KD_SL_MIDPT:				// sliding midpoint split
+		root = rkd_tree(pa, pidx, n, dd, bs, bnd_box, sl_midpt_split);
+		break;
+	case ANN_KD_SL_FAIR:				// sliding fair split
+		root = rkd_tree(pa, pidx, n, dd, bs, bnd_box, sl_fair_split);
+		break;
+	default:
+		annError("Illegal splitting method", ANNabort);
+	}
+}
diff --git a/contrib/ANN/src/kd_tree.h b/contrib/ANN/src/kd_tree.h
new file mode 100644
index 0000000000..81284b6fff
--- /dev/null
+++ b/contrib/ANN/src/kd_tree.h
@@ -0,0 +1,197 @@
+//----------------------------------------------------------------------
+// File:			kd_tree.h
+// Programmer:		Sunil Arya and David Mount
+// Description:		Declarations for standard kd-tree routines
+// Last modified:	05/03/05 (Version 1.1)
+//----------------------------------------------------------------------
+// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
+// David Mount.  All Rights Reserved.
+// 
+// This software and related documentation is part of the Approximate
+// Nearest Neighbor Library (ANN).  This software is provided under
+// the provisions of the Lesser GNU Public License (LGPL).  See the
+// file ../ReadMe.txt for further information.
+// 
+// The University of Maryland (U.M.) and the authors make no
+// representations about the suitability or fitness of this software for
+// any purpose.  It is provided "as is" without express or implied
+// warranty.
+//----------------------------------------------------------------------
+// History:
+//	Revision 0.1  03/04/98
+//		Initial release
+//	Revision 1.1  05/03/05
+//		Added fixed radius kNN search
+//----------------------------------------------------------------------
+
+#ifndef ANN_kd_tree_H
+#define ANN_kd_tree_H
+
+#include <ANN/ANNx.h>					// all ANN includes
+
+using namespace std;					// make std:: available
+
+//----------------------------------------------------------------------
+//	Generic kd-tree node
+//
+//		Nodes in kd-trees are of two types, splitting nodes which contain
+//		splitting information (a splitting hyperplane orthogonal to one
+//		of the coordinate axes) and leaf nodes which contain point
+//		information (an array of points stored in a bucket).  This is
+//		handled by making a generic class kd_node, which is essentially an
+//		empty shell, and then deriving the leaf and splitting nodes from
+//		this.
+//----------------------------------------------------------------------
+
+class ANNkd_node{						// generic kd-tree node (empty shell)
+public:
+	virtual ~ANNkd_node() {}					// virtual distroyer
+
+	virtual void ann_search(ANNdist) = 0;		// tree search
+	virtual void ann_pri_search(ANNdist) = 0;	// priority search
+	virtual void ann_FR_search(ANNdist) = 0;	// fixed-radius search
+
+	virtual void getStats(						// get tree statistics
+				int dim,						// dimension of space
+				ANNkdStats &st,					// statistics
+				ANNorthRect &bnd_box) = 0;		// bounding box
+												// print node
+	virtual void print(int level, ostream &out) = 0;
+	virtual void dump(ostream &out) = 0;		// dump node
+
+	friend class ANNkd_tree;					// allow kd-tree to access us
+};
+
+//----------------------------------------------------------------------
+//	kd-splitting function:
+//		kd_splitter is a pointer to a splitting routine for preprocessing.
+//		Different splitting procedures result in different strategies
+//		for building the tree.
+//----------------------------------------------------------------------
+
+typedef void (*ANNkd_splitter)(			// splitting routine for kd-trees
+	ANNpointArray		pa,				// point array (unaltered)
+	ANNidxArray			pidx,			// point indices (permuted on return)
+	const ANNorthRect	&bnds,			// bounding rectangle for cell
+	int					n,				// number of points
+	int					dim,			// dimension of space
+	int					&cut_dim,		// cutting dimension (returned)
+	ANNcoord			&cut_val,		// cutting value (returned)
+	int					&n_lo);			// num of points on low side (returned)
+
+//----------------------------------------------------------------------
+//	Leaf kd-tree node
+//		Leaf nodes of the kd-tree store the set of points associated
+//		with this bucket, stored as an array of point indices.  These
+//		are indices in the array points, which resides with the
+//		root of the kd-tree.  We also store the number of points
+//		that reside in this bucket.
+//----------------------------------------------------------------------
+
+class ANNkd_leaf: public ANNkd_node		// leaf node for kd-tree
+{
+	int					n_pts;			// no. points in bucket
+	ANNidxArray			bkt;			// bucket of points
+public:
+	ANNkd_leaf(							// constructor
+		int				n,				// number of points
+		ANNidxArray		b)				// bucket
+		{
+			n_pts		= n;			// number of points in bucket
+			bkt			= b;			// the bucket
+		}
+
+	~ANNkd_leaf() { }					// destructor (none)
+
+	virtual void getStats(						// get tree statistics
+				int dim,						// dimension of space
+				ANNkdStats &st,					// statistics
+				ANNorthRect &bnd_box);			// bounding box
+	virtual void print(int level, ostream &out);// print node
+	virtual void dump(ostream &out);			// dump node
+
+	virtual void ann_search(ANNdist);			// standard search
+	virtual void ann_pri_search(ANNdist);		// priority search
+	virtual void ann_FR_search(ANNdist);		// fixed-radius search
+};
+
+//----------------------------------------------------------------------
+//		KD_TRIVIAL is a special pointer to an empty leaf node. Since
+//		some splitting rules generate many (more than 50%) trivial
+//		leaves, we use this one shared node to save space.
+//
+//		The pointer is initialized to NULL, but whenever a kd-tree is
+//		created, we allocate this node, if it has not already been
+//		allocated. This node is *never* deallocated, so it produces
+//		a small memory leak.
+//----------------------------------------------------------------------
+
+extern ANNkd_leaf *KD_TRIVIAL;					// trivial (empty) leaf node
+
+//----------------------------------------------------------------------
+//	kd-tree splitting node.
+//		Splitting nodes contain a cutting dimension and a cutting value.
+//		These indicate the axis-parellel plane which subdivide the
+//		box for this node. The extent of the bounding box along the
+//		cutting dimension is maintained (this is used to speed up point
+//		to box distance calculations) [we do not store the entire bounding
+//		box since this may be wasteful of space in high dimensions].
+//		We also store pointers to the 2 children.
+//----------------------------------------------------------------------
+
+class ANNkd_split : public ANNkd_node	// splitting node of a kd-tree
+{
+	int					cut_dim;		// dim orthogonal to cutting plane
+	ANNcoord			cut_val;		// location of cutting plane
+	ANNcoord			cd_bnds[2];		// lower and upper bounds of
+										// rectangle along cut_dim
+	ANNkd_ptr			child[2];		// left and right children
+public:
+	ANNkd_split(						// constructor
+		int cd,							// cutting dimension
+		ANNcoord cv,					// cutting value
+		ANNcoord lv, ANNcoord hv,				// low and high values
+		ANNkd_ptr lc=NULL, ANNkd_ptr hc=NULL)	// children
+		{
+			cut_dim		= cd;					// cutting dimension
+			cut_val		= cv;					// cutting value
+			cd_bnds[ANN_LO] = lv;				// lower bound for rectangle
+			cd_bnds[ANN_HI] = hv;				// upper bound for rectangle
+			child[ANN_LO]	= lc;				// left child
+			child[ANN_HI]	= hc;				// right child
+		}
+
+	~ANNkd_split()						// destructor
+		{
+			if (child[ANN_LO]!= NULL && child[ANN_LO]!= KD_TRIVIAL)
+				delete child[ANN_LO];
+			if (child[ANN_HI]!= NULL && child[ANN_HI]!= KD_TRIVIAL)
+				delete child[ANN_HI];
+		}
+
+	virtual void getStats(						// get tree statistics
+				int dim,						// dimension of space
+				ANNkdStats &st,					// statistics
+				ANNorthRect &bnd_box);			// bounding box
+	virtual void print(int level, ostream &out);// print node
+	virtual void dump(ostream &out);			// dump node
+
+	virtual void ann_search(ANNdist);			// standard search
+	virtual void ann_pri_search(ANNdist);		// priority search
+	virtual void ann_FR_search(ANNdist);		// fixed-radius search
+};
+
+//----------------------------------------------------------------------
+//		External entry points
+//----------------------------------------------------------------------
+
+ANNkd_ptr rkd_tree(				// recursive construction of kd-tree
+	ANNpointArray		pa,				// point array (unaltered)
+	ANNidxArray			pidx,			// point indices to store in subtree
+	int					n,				// number of points
+	int					dim,			// dimension of space
+	int					bsp,			// bucket space
+	ANNorthRect			&bnd_box,		// bounding box for current node
+	ANNkd_splitter		splitter);		// splitting routine
+
+#endif
diff --git a/contrib/ANN/src/kd_util.cpp b/contrib/ANN/src/kd_util.cpp
new file mode 100644
index 0000000000..06d65b835d
--- /dev/null
+++ b/contrib/ANN/src/kd_util.cpp
@@ -0,0 +1,439 @@
+//----------------------------------------------------------------------
+// File:			kd_util.cpp
+// Programmer:		Sunil Arya and David Mount
+// Description:		Common utilities for kd-trees
+// Last modified:	01/04/05 (Version 1.0)
+//----------------------------------------------------------------------
+// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
+// David Mount.  All Rights Reserved.
+// 
+// This software and related documentation is part of the Approximate
+// Nearest Neighbor Library (ANN).  This software is provided under
+// the provisions of the Lesser GNU Public License (LGPL).  See the
+// file ../ReadMe.txt for further information.
+// 
+// The University of Maryland (U.M.) and the authors make no
+// representations about the suitability or fitness of this software for
+// any purpose.  It is provided "as is" without express or implied
+// warranty.
+//----------------------------------------------------------------------
+// History:
+//	Revision 0.1  03/04/98
+//		Initial release
+//----------------------------------------------------------------------
+
+#include "kd_util.h"					// kd-utility declarations
+
+#include <ANN/ANNperf.h>				// performance evaluation
+
+//----------------------------------------------------------------------
+// The following routines are utility functions for manipulating
+// points sets, used in determining splitting planes for kd-tree
+// construction.
+//----------------------------------------------------------------------
+
+//----------------------------------------------------------------------
+//	NOTE: Virtually all point indexing is done through an index (i.e.
+//	permutation) array pidx.  Consequently, a reference to the d-th
+//	coordinate of the i-th point is pa[pidx[i]][d].  The macro PA(i,d)
+//	is a shorthand for this.
+//----------------------------------------------------------------------
+										// standard 2-d indirect indexing
+#define PA(i,d)			(pa[pidx[(i)]][(d)])
+										// accessing a single point
+#define PP(i)			(pa[pidx[(i)]])
+
+//----------------------------------------------------------------------
+//	annAspectRatio
+//		Compute the aspect ratio (ratio of longest to shortest side)
+//		of a rectangle.
+//----------------------------------------------------------------------
+
+double annAspectRatio(
+	int					dim,			// dimension
+	const ANNorthRect	&bnd_box)		// bounding cube
+{
+	ANNcoord length = bnd_box.hi[0] - bnd_box.lo[0];
+	ANNcoord min_length = length;				// min side length
+	ANNcoord max_length = length;				// max side length
+	for (int d = 0; d < dim; d++) {
+		length = bnd_box.hi[d] - bnd_box.lo[d];
+		if (length < min_length) min_length = length;
+		if (length > max_length) max_length = length;
+	}
+	return max_length/min_length;
+}
+
+//----------------------------------------------------------------------
+//	annEnclRect, annEnclCube
+//		These utilities compute the smallest rectangle and cube enclosing
+//		a set of points, respectively.
+//----------------------------------------------------------------------
+
+void annEnclRect(
+	ANNpointArray		pa,				// point array
+	ANNidxArray			pidx,			// point indices
+	int					n,				// number of points
+	int					dim,			// dimension
+	ANNorthRect			&bnds)			// bounding cube (returned)
+{
+	for (int d = 0; d < dim; d++) {		// find smallest enclosing rectangle
+		ANNcoord lo_bnd = PA(0,d);		// lower bound on dimension d
+		ANNcoord hi_bnd = PA(0,d);		// upper bound on dimension d
+		for (int i = 0; i < n; i++) {
+			if (PA(i,d) < lo_bnd) lo_bnd = PA(i,d);
+			else if (PA(i,d) > hi_bnd) hi_bnd = PA(i,d);
+		}
+		bnds.lo[d] = lo_bnd;
+		bnds.hi[d] = hi_bnd;
+	}
+}
+
+void annEnclCube(						// compute smallest enclosing cube
+	ANNpointArray		pa,				// point array
+	ANNidxArray			pidx,			// point indices
+	int					n,				// number of points
+	int					dim,			// dimension
+	ANNorthRect			&bnds)			// bounding cube (returned)
+{
+	int d;
+										// compute smallest enclosing rect
+	annEnclRect(pa, pidx, n, dim, bnds);
+
+	ANNcoord max_len = 0;				// max length of any side
+	for (d = 0; d < dim; d++) {			// determine max side length
+		ANNcoord len = bnds.hi[d] - bnds.lo[d];
+		if (len > max_len) {			// update max_len if longest
+			max_len = len;
+		}
+	}
+	for (d = 0; d < dim; d++) {			// grow sides to match max
+		ANNcoord len = bnds.hi[d] - bnds.lo[d];
+		ANNcoord half_diff = (max_len - len) / 2;
+		bnds.lo[d] -= half_diff;
+		bnds.hi[d] += half_diff;
+	}
+}
+
+//----------------------------------------------------------------------
+//	annBoxDistance - utility routine which computes distance from point to
+//		box (Note: most distances to boxes are computed using incremental
+//		distance updates, not this function.)
+//----------------------------------------------------------------------
+
+ANNdist annBoxDistance(			// compute distance from point to box
+	const ANNpoint		q,				// the point
+	const ANNpoint		lo,				// low point of box
+	const ANNpoint		hi,				// high point of box
+	int					dim)			// dimension of space
+{
+	register ANNdist dist = 0.0;		// sum of squared distances
+	register ANNdist t;
+
+	for (register int d = 0; d < dim; d++) {
+		if (q[d] < lo[d]) {				// q is left of box
+			t = ANNdist(lo[d]) - ANNdist(q[d]);
+			dist = ANN_SUM(dist, ANN_POW(t));
+		}
+		else if (q[d] > hi[d]) {		// q is right of box
+			t = ANNdist(q[d]) - ANNdist(hi[d]);
+			dist = ANN_SUM(dist, ANN_POW(t));
+		}
+	}
+	ANN_FLOP(4*dim)						// increment floating op count
+
+	return dist;
+}
+
+//----------------------------------------------------------------------
+//	annSpread - find spread along given dimension
+//	annMinMax - find min and max coordinates along given dimension
+//	annMaxSpread - find dimension of max spread
+//----------------------------------------------------------------------
+
+ANNcoord annSpread(				// compute point spread along dimension
+	ANNpointArray		pa,				// point array
+	ANNidxArray			pidx,			// point indices
+	int					n,				// number of points
+	int					d)				// dimension to check
+{
+	ANNcoord min = PA(0,d);				// compute max and min coords
+	ANNcoord max = PA(0,d);
+	for (int i = 1; i < n; i++) {
+		ANNcoord c = PA(i,d);
+		if (c < min) min = c;
+		else if (c > max) max = c;
+	}
+	return (max - min);					// total spread is difference
+}
+
+void annMinMax(					// compute min and max coordinates along dim
+	ANNpointArray		pa,				// point array
+	ANNidxArray			pidx,			// point indices
+	int					n,				// number of points
+	int					d,				// dimension to check
+	ANNcoord			&min,			// minimum value (returned)
+	ANNcoord			&max)			// maximum value (returned)
+{
+	min = PA(0,d);						// compute max and min coords
+	max = PA(0,d);
+	for (int i = 1; i < n; i++) {
+		ANNcoord c = PA(i,d);
+		if (c < min) min = c;
+		else if (c > max) max = c;
+	}
+}
+
+int annMaxSpread(						// compute dimension of max spread
+	ANNpointArray		pa,				// point array
+	ANNidxArray			pidx,			// point indices
+	int					n,				// number of points
+	int					dim)			// dimension of space
+{
+	int max_dim = 0;					// dimension of max spread
+	ANNcoord max_spr = 0;				// amount of max spread
+
+	if (n == 0) return max_dim;			// no points, who cares?
+
+	for (int d = 0; d < dim; d++) {		// compute spread along each dim
+		ANNcoord spr = annSpread(pa, pidx, n, d);
+		if (spr > max_spr) {			// bigger than current max
+			max_spr = spr;
+			max_dim = d;
+		}
+	}
+	return max_dim;
+}
+
+//----------------------------------------------------------------------
+//	annMedianSplit - split point array about its median
+//		Splits a subarray of points pa[0..n] about an element of given
+//		rank (median: n_lo = n/2) with respect to dimension d.  It places
+//		the element of rank n_lo-1 correctly (because our splitting rule
+//		takes the mean of these two).  On exit, the array is permuted so
+//		that:
+//
+//		pa[0..n_lo-2][d] <= pa[n_lo-1][d] <= pa[n_lo][d] <= pa[n_lo+1..n-1][d].
+//
+//		The mean of pa[n_lo-1][d] and pa[n_lo][d] is returned as the
+//		splitting value.
+//
+//		All indexing is done indirectly through the index array pidx.
+//
+//		This function uses the well known selection algorithm due to
+//		C.A.R. Hoare.
+//----------------------------------------------------------------------
+
+										// swap two points in pa array
+#define PASWAP(a,b) { int tmp = pidx[a]; pidx[a] = pidx[b]; pidx[b] = tmp; }
+
+void annMedianSplit(
+	ANNpointArray		pa,				// points to split
+	ANNidxArray			pidx,			// point indices
+	int					n,				// number of points
+	int					d,				// dimension along which to split
+	ANNcoord			&cv,			// cutting value
+	int					n_lo)			// split into n_lo and n-n_lo
+{
+	int l = 0;							// left end of current subarray
+	int r = n-1;						// right end of current subarray
+	while (l < r) {
+		register int i = (r+l)/2;		// select middle as pivot
+		register int k;
+
+		if (PA(i,d) > PA(r,d))			// make sure last > pivot
+			PASWAP(i,r)
+		PASWAP(l,i);					// move pivot to first position
+
+		ANNcoord c = PA(l,d);			// pivot value
+		i = l;
+		k = r;
+		for(;;) {						// pivot about c
+			while (PA(++i,d) < c) ;
+			while (PA(--k,d) > c) ;
+			if (i < k) PASWAP(i,k) else break;
+		}
+		PASWAP(l,k);					// pivot winds up in location k
+
+		if (k > n_lo)	   r = k-1;		// recurse on proper subarray
+		else if (k < n_lo) l = k+1;
+		else break;						// got the median exactly
+	}
+	if (n_lo > 0) {						// search for next smaller item
+		ANNcoord c = PA(0,d);			// candidate for max
+		int k = 0;						// candidate's index
+		for (int i = 1; i < n_lo; i++) {
+			if (PA(i,d) > c) {
+				c = PA(i,d);
+				k = i;
+			}
+		}
+		PASWAP(n_lo-1, k);				// max among pa[0..n_lo-1] to pa[n_lo-1]
+	}
+										// cut value is midpoint value
+	cv = (PA(n_lo-1,d) + PA(n_lo,d))/2.0;
+}
+
+//----------------------------------------------------------------------
+//	annPlaneSplit - split point array about a cutting plane
+//		Split the points in an array about a given plane along a
+//		given cutting dimension.  On exit, br1 and br2 are set so
+//		that:
+//		
+//				pa[ 0 ..br1-1] <  cv
+//				pa[br1..br2-1] == cv
+//				pa[br2.. n -1] >  cv
+//
+//		All indexing is done indirectly through the index array pidx.
+//
+//----------------------------------------------------------------------
+
+void annPlaneSplit(				// split points by a plane
+	ANNpointArray		pa,				// points to split
+	ANNidxArray			pidx,			// point indices
+	int					n,				// number of points
+	int					d,				// dimension along which to split
+	ANNcoord			cv,				// cutting value
+	int					&br1,			// first break (values < cv)
+	int					&br2)			// second break (values == cv)
+{
+	int l = 0;
+	int r = n-1;
+	for(;;) {							// partition pa[0..n-1] about cv
+		while (l < n && PA(l,d) < cv) l++;
+		while (r >= 0 && PA(r,d) >= cv) r--;
+		if (l > r) break;
+		PASWAP(l,r);
+		l++; r--;
+	}
+	br1 = l;					// now: pa[0..br1-1] < cv <= pa[br1..n-1]
+	r = n-1;
+	for(;;) {							// partition pa[br1..n-1] about cv
+		while (l < n && PA(l,d) <= cv) l++;
+		while (r >= br1 && PA(r,d) > cv) r--;
+		if (l > r) break;
+		PASWAP(l,r);
+		l++; r--;
+	}
+	br2 = l;					// now: pa[br1..br2-1] == cv < pa[br2..n-1]
+}
+
+
+//----------------------------------------------------------------------
+//	annBoxSplit - split point array about a orthogonal rectangle
+//		Split the points in an array about a given orthogonal
+//		rectangle.  On exit, n_in is set to the number of points
+//		that are inside (or on the boundary of) the rectangle.
+//
+//		All indexing is done indirectly through the index array pidx.
+//
+//----------------------------------------------------------------------
+
+void annBoxSplit(				// split points by a box
+	ANNpointArray		pa,				// points to split
+	ANNidxArray			pidx,			// point indices
+	int					n,				// number of points
+	int					dim,			// dimension of space
+	ANNorthRect			&box,			// the box
+	int					&n_in)			// number of points inside (returned)
+{
+	int l = 0;
+	int r = n-1;
+	for(;;) {							// partition pa[0..n-1] about box
+		while (l < n && box.inside(dim, PP(l))) l++;
+		while (r >= 0 && !box.inside(dim, PP(r))) r--;
+		if (l > r) break;
+		PASWAP(l,r);
+		l++; r--;
+	}
+	n_in = l;					// now: pa[0..n_in-1] inside and rest outside
+}
+
+//----------------------------------------------------------------------
+//	annSplitBalance - compute balance factor for a given plane split
+//		Balance factor is defined as the number of points lying
+//		below the splitting value minus n/2 (median).  Thus, a
+//		median split has balance 0, left of this is negative and
+//		right of this is positive.  (The points are unchanged.)
+//----------------------------------------------------------------------
+
+int annSplitBalance(			// determine balance factor of a split
+	ANNpointArray		pa,				// points to split
+	ANNidxArray			pidx,			// point indices
+	int					n,				// number of points
+	int					d,				// dimension along which to split
+	ANNcoord			cv)				// cutting value
+{
+	int n_lo = 0;
+	for(int i = 0; i < n; i++) {		// count number less than cv
+		if (PA(i,d) < cv) n_lo++;
+	}
+	return n_lo - n/2;
+}
+
+//----------------------------------------------------------------------
+//	annBox2Bnds - convert bounding box to list of bounds
+//		Given two boxes, an inner box enclosed within a bounding
+//		box, this routine determines all the sides for which the
+//		inner box is strictly contained with the bounding box,
+//		and adds an appropriate entry to a list of bounds.  Then
+//		we allocate storage for the final list of bounds, and return
+//		the resulting list and its size.
+//----------------------------------------------------------------------
+
+void annBox2Bnds(						// convert inner box to bounds
+	const ANNorthRect	&inner_box,		// inner box
+	const ANNorthRect	&bnd_box,		// enclosing box
+	int					dim,			// dimension of space
+	int					&n_bnds,		// number of bounds (returned)
+	ANNorthHSArray		&bnds)			// bounds array (returned)
+{
+	int i;
+	n_bnds = 0;									// count number of bounds
+	for (i = 0; i < dim; i++) {
+		if (inner_box.lo[i] > bnd_box.lo[i])	// low bound is inside
+				n_bnds++;
+		if (inner_box.hi[i] < bnd_box.hi[i])	// high bound is inside
+				n_bnds++;
+	}
+
+	bnds = new ANNorthHalfSpace[n_bnds];		// allocate appropriate size
+
+	int j = 0;
+	for (i = 0; i < dim; i++) {					// fill the array
+		if (inner_box.lo[i] > bnd_box.lo[i]) {
+				bnds[j].cd = i;
+				bnds[j].cv = inner_box.lo[i];
+				bnds[j].sd = +1;
+				j++;
+		}
+		if (inner_box.hi[i] < bnd_box.hi[i]) {
+				bnds[j].cd = i;
+				bnds[j].cv = inner_box.hi[i];
+				bnds[j].sd = -1;
+				j++;
+		}
+	}
+}
+
+//----------------------------------------------------------------------
+//	annBnds2Box - convert list of bounds to bounding box
+//		Given an enclosing box and a list of bounds, this routine
+//		computes the corresponding inner box.  It is assumed that
+//		the box points have been allocated already.
+//----------------------------------------------------------------------
+
+void annBnds2Box(
+	const ANNorthRect	&bnd_box,		// enclosing box
+	int					dim,			// dimension of space
+	int					n_bnds,			// number of bounds
+	ANNorthHSArray		bnds,			// bounds array
+	ANNorthRect			&inner_box)		// inner box (returned)
+{
+	annAssignRect(dim, inner_box, bnd_box);		// copy bounding box to inner
+
+	for (int i = 0; i < n_bnds; i++) {
+		bnds[i].project(inner_box.lo);			// project each endpoint
+		bnds[i].project(inner_box.hi);
+	}
+}
diff --git a/contrib/ANN/src/kd_util.h b/contrib/ANN/src/kd_util.h
new file mode 100644
index 0000000000..6b4343048c
--- /dev/null
+++ b/contrib/ANN/src/kd_util.h
@@ -0,0 +1,124 @@
+//----------------------------------------------------------------------
+// File:			kd_util.h
+// Programmer:		Sunil Arya and David Mount
+// Description:		Common utilities for kd- trees
+// Last modified:	01/04/05 (Version 1.0)
+//----------------------------------------------------------------------
+// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
+// David Mount.  All Rights Reserved.
+// 
+// This software and related documentation is part of the Approximate
+// Nearest Neighbor Library (ANN).  This software is provided under
+// the provisions of the Lesser GNU Public License (LGPL).  See the
+// file ../ReadMe.txt for further information.
+// 
+// The University of Maryland (U.M.) and the authors make no
+// representations about the suitability or fitness of this software for
+// any purpose.  It is provided "as is" without express or implied
+// warranty.
+//----------------------------------------------------------------------
+// History:
+//	Revision 0.1  03/04/98
+//		Initial release
+//----------------------------------------------------------------------
+
+#ifndef ANN_kd_util_H
+#define ANN_kd_util_H
+
+#include "kd_tree.h"					// kd-tree declarations
+
+//----------------------------------------------------------------------
+//	externally accessible functions
+//----------------------------------------------------------------------
+
+double annAspectRatio(			// compute aspect ratio of box
+	int					dim,			// dimension
+	const ANNorthRect	&bnd_box);		// bounding cube
+
+void annEnclRect(				// compute smallest enclosing rectangle
+	ANNpointArray		pa,				// point array
+	ANNidxArray			pidx,			// point indices
+	int					n,				// number of points
+	int					dim,			// dimension
+	ANNorthRect &bnds);					// bounding cube (returned)
+
+void annEnclCube(				// compute smallest enclosing cube
+	ANNpointArray		pa,				// point array
+	ANNidxArray			pidx,			// point indices
+	int					n,				// number of points
+	int					dim,			// dimension
+	ANNorthRect &bnds);					// bounding cube (returned)
+
+ANNdist annBoxDistance(			// compute distance from point to box
+	const ANNpoint		q,				// the point
+	const ANNpoint		lo,				// low point of box
+	const ANNpoint		hi,				// high point of box
+	int					dim);			// dimension of space
+
+ANNcoord annSpread(				// compute point spread along dimension
+	ANNpointArray		pa,				// point array
+	ANNidxArray			pidx,			// point indices
+	int					n,				// number of points
+	int					d);				// dimension to check
+
+void annMinMax(					// compute min and max coordinates along dim
+	ANNpointArray		pa,				// point array
+	ANNidxArray			pidx,			// point indices
+	int					n,				// number of points
+	int					d,				// dimension to check
+	ANNcoord&			min,			// minimum value (returned)
+	ANNcoord&			max);			// maximum value (returned)
+
+int annMaxSpread(				// compute dimension of max spread
+	ANNpointArray		pa,				// point array
+	ANNidxArray			pidx,			// point indices
+	int					n,				// number of points
+	int					dim);			// dimension of space
+
+void annMedianSplit(			// split points along median value
+	ANNpointArray		pa,				// points to split
+	ANNidxArray			pidx,			// point indices
+	int					n,				// number of points
+	int					d,				// dimension along which to split
+	ANNcoord			&cv,			// cutting value
+	int					n_lo);			// split into n_lo and n-n_lo
+
+void annPlaneSplit(				// split points by a plane
+	ANNpointArray		pa,				// points to split
+	ANNidxArray			pidx,			// point indices
+	int					n,				// number of points
+	int					d,				// dimension along which to split
+	ANNcoord			cv,				// cutting value
+	int					&br1,			// first break (values < cv)
+	int					&br2);			// second break (values == cv)
+
+void annBoxSplit(				// split points by a box
+	ANNpointArray		pa,				// points to split
+	ANNidxArray			pidx,			// point indices
+	int					n,				// number of points
+	int					dim,			// dimension of space
+	ANNorthRect			&box,			// the box
+	int					&n_in);			// number of points inside (returned)
+
+int annSplitBalance(			// determine balance factor of a split
+	ANNpointArray		pa,				// points to split
+	ANNidxArray			pidx,			// point indices
+	int					n,				// number of points
+	int					d,				// dimension along which to split
+	ANNcoord			cv);			// cutting value
+
+void annBox2Bnds(				// convert inner box to bounds
+	const ANNorthRect	&inner_box,		// inner box
+	const ANNorthRect	&bnd_box,		// enclosing box
+	int					dim,			// dimension of space
+	int					&n_bnds,		// number of bounds (returned)
+	ANNorthHSArray		&bnds);			// bounds array (returned)
+
+void annBnds2Box(				// convert bounds to inner box
+	const ANNorthRect	&bnd_box,		// enclosing box
+	int					dim,			// dimension of space
+	int					n_bnds,			// number of bounds
+	ANNorthHSArray		bnds,			// bounds array
+	ANNorthRect			&inner_box);	// inner box (returned)
+
+#endif
diff --git a/contrib/ANN/src/perf.cpp b/contrib/ANN/src/perf.cpp
new file mode 100644
index 0000000000..91bb0444ae
--- /dev/null
+++ b/contrib/ANN/src/perf.cpp
@@ -0,0 +1,134 @@
+//----------------------------------------------------------------------
+// File:			perf.cpp
+// Programmer:		Sunil Arya and David Mount
+// Description:		Methods for performance stats
+// Last modified:	01/04/05 (Version 1.0)
+//----------------------------------------------------------------------
+// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
+// David Mount.  All Rights Reserved.
+// 
+// This software and related documentation is part of the Approximate
+// Nearest Neighbor Library (ANN).  This software is provided under
+// the provisions of the Lesser GNU Public License (LGPL).  See the
+// file ../ReadMe.txt for further information.
+// 
+// The University of Maryland (U.M.) and the authors make no
+// representations about the suitability or fitness of this software for
+// any purpose.  It is provided "as is" without express or implied
+// warranty.
+//----------------------------------------------------------------------
+// History:
+//	Revision 0.1  03/04/98
+//		Initial release
+//	Revision 1.0  04/01/05
+//		Changed names to avoid namespace conflicts.
+//		Added flush after printing performance stats to fix bug
+//			in Microsoft Windows version.
+//----------------------------------------------------------------------
+
+#include <ANN/ANN.h>					// basic ANN includes
+#include <ANN/ANNperf.h>				// performance includes
+
+using namespace std;					// make std:: available
+
+//----------------------------------------------------------------------
+//	Performance statistics
+//		The following data and routines are used for computing
+//		performance statistics for nearest neighbor searching.
+//		Because these routines can slow the code down, they can be
+//		activated and deactiviated by defining the PERF variable,
+//		by compiling with the option: -DPERF
+//----------------------------------------------------------------------
+
+//----------------------------------------------------------------------
+//	Global counters for performance measurement
+//----------------------------------------------------------------------
+
+int				ann_Ndata_pts  = 0;		// number of data points
+int				ann_Nvisit_lfs = 0;		// number of leaf nodes visited
+int				ann_Nvisit_spl = 0;		// number of splitting nodes visited
+int				ann_Nvisit_shr = 0;		// number of shrinking nodes visited
+int				ann_Nvisit_pts = 0;		// visited points for one query
+int				ann_Ncoord_hts = 0;		// coordinate hits for one query
+int				ann_Nfloat_ops = 0;		// floating ops for one query
+ANNsampStat		ann_visit_lfs;			// stats on leaf nodes visits
+ANNsampStat		ann_visit_spl;			// stats on splitting nodes visits
+ANNsampStat		ann_visit_shr;			// stats on shrinking nodes visits
+ANNsampStat		ann_visit_nds;			// stats on total nodes visits
+ANNsampStat		ann_visit_pts;			// stats on points visited
+ANNsampStat		ann_coord_hts;			// stats on coordinate hits
+ANNsampStat		ann_float_ops;			// stats on floating ops
+//
+ANNsampStat		ann_average_err;		// average error
+ANNsampStat		ann_rank_err;			// rank error
+
+//----------------------------------------------------------------------
+//	Routines for statistics.
+//----------------------------------------------------------------------
+
+DLL_API void annResetStats(int data_size) // reset stats for a set of queries
+{
+	ann_Ndata_pts  = data_size;
+	ann_visit_lfs.reset();
+	ann_visit_spl.reset();
+	ann_visit_shr.reset();
+	ann_visit_nds.reset();
+	ann_visit_pts.reset();
+	ann_coord_hts.reset();
+	ann_float_ops.reset();
+	ann_average_err.reset();
+	ann_rank_err.reset();
+}
+
+DLL_API void annResetCounts()				// reset counts for one query
+{
+	ann_Nvisit_lfs = 0;
+	ann_Nvisit_spl = 0;
+	ann_Nvisit_shr = 0;
+	ann_Nvisit_pts = 0;
+	ann_Ncoord_hts = 0;
+	ann_Nfloat_ops = 0;
+}
+
+DLL_API void annUpdateStats()				// update stats with current counts
+{
+	ann_visit_lfs += ann_Nvisit_lfs;
+	ann_visit_nds += ann_Nvisit_spl + ann_Nvisit_lfs;
+	ann_visit_spl += ann_Nvisit_spl;
+	ann_visit_shr += ann_Nvisit_shr;
+	ann_visit_pts += ann_Nvisit_pts;
+	ann_coord_hts += ann_Ncoord_hts;
+	ann_float_ops += ann_Nfloat_ops;
+}
+
+										// print a single statistic
+void print_one_stat(char *title, ANNsampStat s, double div)
+{
+	cout << title << "= [ ";
+	cout.width(9); cout << s.mean()/div			<< " : ";
+	cout.width(9); cout << s.stdDev()/div		<< " ]<";
+	cout.width(9); cout << s.min()/div			<< " , ";
+	cout.width(9); cout << s.max()/div			<< " >\n";
+}
+
+DLL_API void annPrintStats(				// print statistics for a run
+	ANNbool validate)					// true if average errors desired
+{
+	cout.precision(4);					// set floating precision
+	cout << "  (Performance stats: "
+		 << " [      mean :    stddev ]<      min ,       max >\n";
+	print_one_stat("    leaf_nodes       ", ann_visit_lfs, 1);
+	print_one_stat("    splitting_nodes  ", ann_visit_spl, 1);
+	print_one_stat("    shrinking_nodes  ", ann_visit_shr, 1);
+	print_one_stat("    total_nodes      ", ann_visit_nds, 1);
+	print_one_stat("    points_visited   ", ann_visit_pts, 1);
+	print_one_stat("    coord_hits/pt    ", ann_coord_hts, ann_Ndata_pts);
+	print_one_stat("    floating_ops_(K) ", ann_float_ops, 1000);
+	if (validate) {
+		print_one_stat("    average_error    ", ann_average_err, 1);
+		print_one_stat("    rank_error       ", ann_rank_err, 1);
+	}
+	cout.precision(0);					// restore the default
+	cout << "  )\n";
+	cout.flush();
+}
diff --git a/contrib/ANN/src/pr_queue.h b/contrib/ANN/src/pr_queue.h
new file mode 100644
index 0000000000..3f4b75c878
--- /dev/null
+++ b/contrib/ANN/src/pr_queue.h
@@ -0,0 +1,125 @@
+//----------------------------------------------------------------------
+// File:			pr_queue.h
+// Programmer:		Sunil Arya and David Mount
+// Description:		Include file for priority queue and related
+// 					structures.
+// Last modified:	01/04/05 (Version 1.0)
+//----------------------------------------------------------------------
+// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
+// David Mount.  All Rights Reserved.
+// 
+// This software and related documentation is part of the Approximate
+// Nearest Neighbor Library (ANN).  This software is provided under
+// the provisions of the Lesser GNU Public License (LGPL).  See the
+// file ../ReadMe.txt for further information.
+// 
+// The University of Maryland (U.M.) and the authors make no
+// representations about the suitability or fitness of this software for
+// any purpose.  It is provided "as is" without express or implied
+// warranty.
+//----------------------------------------------------------------------
+// History:
+//	Revision 0.1  03/04/98
+//		Initial release
+//----------------------------------------------------------------------
+
+#ifndef PR_QUEUE_H
+#define PR_QUEUE_H
+
+#include <ANN/ANNx.h>					// all ANN includes
+#include <ANN/ANNperf.h>				// performance evaluation
+
+//----------------------------------------------------------------------
+//	Basic types.
+//----------------------------------------------------------------------
+typedef void			*PQinfo;		// info field is generic pointer
+typedef ANNdist			PQkey;			// key field is distance
+
+//----------------------------------------------------------------------
+//	Priority queue
+//		A priority queue is a list of items, along with associated
+//		priorities.  The basic operations are insert and extract_minimum.
+//
+//		The priority queue is maintained using a standard binary heap.
+//		(Implementation note: Indexing is performed from [1..max] rather
+//		than the C standard of [0..max-1].  This simplifies parent/child
+//		computations.)  User information consists of a void pointer,
+//		and the user is responsible for casting this quantity into whatever
+//		useful form is desired.
+//
+//		Because the priority queue is so central to the efficiency of
+//		query processing, all the code is inline.
+//----------------------------------------------------------------------
+
+class ANNpr_queue {
+
+	struct pq_node {					// node in priority queue
+		PQkey			key;			// key value
+		PQinfo			info;			// info field
+	};
+	int			n;						// number of items in queue
+	int			max_size;				// maximum queue size
+	pq_node		*pq;					// the priority queue (array of nodes)
+
+public:
+	ANNpr_queue(int max)				// constructor (given max size)
+		{
+			n = 0;						// initially empty
+			max_size = max;				// maximum number of items
+			pq = new pq_node[max+1];	// queue is array [1..max] of nodes
+		}
+
+	~ANNpr_queue()						// destructor
+		{ delete [] pq; }
+
+	ANNbool empty()						// is queue empty?
+		{ if (n==0) return ANNtrue; else return ANNfalse; }
+
+	ANNbool non_empty()					// is queue nonempty?
+		{ if (n==0) return ANNfalse; else return ANNtrue; }
+
+	void reset()						// make existing queue empty
+		{ n = 0; }
+
+	inline void insert(					// insert item (inlined for speed)
+		PQkey kv,						// key value
+		PQinfo inf)						// item info
+		{
+			if (++n > max_size) annError("Priority queue overflow.", ANNabort);
+			register int r = n;
+			while (r > 1) {				// sift up new item
+				register int p = r/2;
+				ANN_FLOP(1)				// increment floating ops
+				if (pq[p].key <= kv)	// in proper order
+					break;
+				pq[r] = pq[p];			// else swap with parent
+				r = p;
+			}
+			pq[r].key = kv;				// insert new item at final location
+			pq[r].info = inf;
+		}
+
+	inline void extr_min(				// extract minimum (inlined for speed)
+		PQkey &kv,						// key (returned)
+		PQinfo &inf)					// item info (returned)
+		{
+			kv = pq[1].key;				// key of min item
+			inf = pq[1].info;			// information of min item
+			register PQkey kn = pq[n--].key;// last item in queue
+			register int p = 1;			// p points to item out of position
+			register int r = p<<1;		// left child of p
+			while (r <= n) {			// while r is still within the heap
+				ANN_FLOP(2)				// increment floating ops
+										// set r to smaller child of p
+				if (r < n  && pq[r].key > pq[r+1].key) r++;
+				if (kn <= pq[r].key)	// in proper order
+					break;
+				pq[p] = pq[r];			// else swap with child
+				p = r;					// advance pointers
+				r = p<<1;
+			}
+			pq[p] = pq[n+1];			// insert last item in proper place
+		}
+};
+
+#endif
diff --git a/contrib/ANN/src/pr_queue_k.h b/contrib/ANN/src/pr_queue_k.h
new file mode 100644
index 0000000000..c2f01c34a5
--- /dev/null
+++ b/contrib/ANN/src/pr_queue_k.h
@@ -0,0 +1,118 @@
+//----------------------------------------------------------------------
+// File:			pr_queue_k.h
+// Programmer:		Sunil Arya and David Mount
+// Description:		Include file for priority queue with k items.
+// Last modified:	01/04/05 (Version 1.0)
+//----------------------------------------------------------------------
+// Copyright (c) 1997-2005 University of Maryland and Sunil Arya and
+// David Mount.  All Rights Reserved.
+// 
+// This software and related documentation is part of the Approximate
+// Nearest Neighbor Library (ANN).  This software is provided under
+// the provisions of the Lesser GNU Public License (LGPL).  See the
+// file ../ReadMe.txt for further information.
+// 
+// The University of Maryland (U.M.) and the authors make no
+// representations about the suitability or fitness of this software for
+// any purpose.  It is provided "as is" without express or implied
+// warranty.
+//----------------------------------------------------------------------
+// History:
+//	Revision 0.1  03/04/98
+//		Initial release
+//----------------------------------------------------------------------
+
+#ifndef PR_QUEUE_K_H
+#define PR_QUEUE_K_H
+
+#include <ANN/ANNx.h>					// all ANN includes
+#include <ANN/ANNperf.h>				// performance evaluation
+
+//----------------------------------------------------------------------
+//	Basic types
+//----------------------------------------------------------------------
+typedef ANNdist			PQKkey;			// key field is distance
+typedef int				PQKinfo;		// info field is int
+
+//----------------------------------------------------------------------
+//	Constants
+//		The NULL key value is used to initialize the priority queue, and
+//		so it should be larger than any valid distance, so that it will
+//		be replaced as legal distance values are inserted.  The NULL
+//		info value must be a nonvalid array index, we use ANN_NULL_IDX,
+//		which is guaranteed to be negative.
+//----------------------------------------------------------------------
+
+const PQKkey	PQ_NULL_KEY  =  ANN_DIST_INF;	// nonexistent key value
+const PQKinfo	PQ_NULL_INFO =  ANN_NULL_IDX;	// nonexistent info value
+
+//----------------------------------------------------------------------
+//	ANNmin_k
+//		An ANNmin_k structure is one which maintains the smallest
+//		k values (of type PQKkey) and associated information (of type
+//		PQKinfo).  The special info and key values PQ_NULL_INFO and
+//		PQ_NULL_KEY means that thise entry is empty.
+//
+//		It is currently implemented using an array with k items.
+//		Items are stored in increasing sorted order, and insertions
+//		are made through standard insertion sort.  (This is quite
+//		inefficient, but current applications call for small values
+//		of k and relatively few insertions.)
+//		
+//		Note that the list contains k+1 entries, but the last entry
+//		is used as a simple placeholder and is otherwise ignored.
+//----------------------------------------------------------------------
+
+class ANNmin_k {
+	struct mk_node {					// node in min_k structure
+		PQKkey			key;			// key value
+		PQKinfo			info;			// info field (user defined)
+	};
+
+	int			k;						// max number of keys to store
+	int			n;						// number of keys currently active
+	mk_node		*mk;					// the list itself
+
+public:
+	ANNmin_k(int max)					// constructor (given max size)
+		{
+			n = 0;						// initially no items
+			k = max;					// maximum number of items
+			mk = new mk_node[max+1];	// sorted array of keys
+		}
+
+	~ANNmin_k()							// destructor
+		{ delete [] mk; }
+	
+	PQKkey ANNmin_key()					// return minimum key
+		{ return (n > 0 ? mk[0].key : PQ_NULL_KEY); }
+	
+	PQKkey max_key()					// return maximum key
+		{ return (n == k ? mk[k-1].key : PQ_NULL_KEY); }
+	
+	PQKkey ith_smallest_key(int i)		// ith smallest key (i in [0..n-1])
+		{ return (i < n ? mk[i].key : PQ_NULL_KEY); }
+	
+	PQKinfo ith_smallest_info(int i)	// info for ith smallest (i in [0..n-1])
+		{ return (i < n ? mk[i].info : PQ_NULL_INFO); }
+
+	inline void insert(					// insert item (inlined for speed)
+		PQKkey kv,						// key value
+		PQKinfo inf)					// item info
+		{
+			register int i;
+										// slide larger values up
+			for (i = n; i > 0; i--) {
+				if (mk[i-1].key > kv)
+					mk[i] = mk[i-1];
+				else
+					break;
+			}
+			mk[i].key = kv;				// store element here
+			mk[i].info = inf;
+			if (n < k) n++;				// increment number of items
+			ANN_FLOP(k-i+1)				// increment floating ops
+		}
+};
+
+#endif
diff --git a/contrib/MathEval/Makefile b/contrib/MathEval/Makefile
new file mode 100644
index 0000000000..aaef9432a3
--- /dev/null
+++ b/contrib/MathEval/Makefile
@@ -0,0 +1,79 @@
+# $Id: Makefile,v 1.1 2005-09-21 17:29:37 geuzaine Exp $
+#
+# Copyright (C) 1997-2005 C. Geuzaine, J.-F. Remacle
+#
+# 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.
+# 
+# Please report all bugs and problems to <gmsh@geuz.org>.
+
+include ../../variables
+
+LIB = ../../lib/libGmshMathEval.a
+INCLUDE = -I../../Common -I../../DataStr
+
+CFLAGS  = ${OPTIM} ${FLAGS} ${INCLUDE} 
+
+SRC = matheval.cpp\
+      node.cpp\
+      scanner.yy.cpp\
+      parser.tab.cpp\
+      symbol_table.cpp\
+      xmath.cpp
+
+OBJ = ${SRC:.cpp=.o}
+
+.SUFFIXES: .o .cpp
+
+${LIB}: ${OBJ}
+	${AR} ${LIB} ${OBJ}
+	${RANLIB} ${LIB}
+
+.cpp.o:
+	${CXX} ${CFLAGS} -c $<
+
+parser:
+	bison --output parser.tab.cpp -pme -d parser.y
+	if [ -r parser.tab.cpp.h ]; then mv parser.tab.cpp.h parser.tab.hpp ; fi
+	flex -oscanner.yy.cpp -Pme scanner.l
+
+clean:
+	rm -f *.o
+
+depend:
+	(sed '/^# DO NOT DELETE THIS LINE/q' Makefile && \
+	${CC} -MM ${CFLAGS} ${SRC} \
+	) >Makefile.new
+	cp Makefile Makefile.bak
+	cp Makefile.new Makefile
+	rm -f Makefile.new
+
+# DO NOT DELETE THIS LINE
+# 1 "/Users/geuzaine/.gmsh/MathEval//"
+matheval.o: matheval.cpp common.h ../DataStr/Malloc.h matheval.h node.h \
+  symbol_table.h
+# 1 "/Users/geuzaine/.gmsh/MathEval//"
+node.o: node.cpp common.h ../DataStr/Malloc.h node.h symbol_table.h
+# 1 "/Users/geuzaine/.gmsh/MathEval//"
+scanner.yy.o: scanner.yy.cpp common.h ../DataStr/Malloc.h node.h \
+  symbol_table.h parser.tab.hpp
+# 1 "/Users/geuzaine/.gmsh/MathEval//"
+parser.tab.o: parser.tab.cpp common.h ../DataStr/Malloc.h node.h \
+  symbol_table.h
+# 1 "/Users/geuzaine/.gmsh/MathEval//"
+symbol_table.o: symbol_table.cpp common.h ../DataStr/Malloc.h \
+  symbol_table.h xmath.h
+# 1 "/Users/geuzaine/.gmsh/MathEval//"
+xmath.o: xmath.cpp xmath.h
diff --git a/contrib/MathEval/README b/contrib/MathEval/README
new file mode 100644
index 0000000000..b66f65dc34
--- /dev/null
+++ b/contrib/MathEval/README
@@ -0,0 +1,25 @@
+
+This directory contains a (heavily) modified version of GNU
+libmatheval.
+
+The original libmatheval is Copyright (C) 1999, 2002, 2003 Aleksandar
+B. Samardzic
+
+Copying and distribution of this file, with or without modification, are
+permitted in any medium without royalty provided the copyright notice
+and this notice are preserved.
+
+WHAT IS IT?
+
+GNU libmatheval is a library which contains several procedures that make
+it possible to create an in-memory tree from the string representation
+of a mathematical function over single or multiple variables. This tree
+can be used later to evaluate a function for specified variable values,
+to create a corresponding tree for the function derivative over a
+specified variable, or to write a textual tree representation to a
+specified string. 
+
+BUGS
+
+Please report bugs and eventually send patches to
+<bug-libmatheval@gnu.org> mailing list.
diff --git a/contrib/MathEval/common.h b/contrib/MathEval/common.h
new file mode 100644
index 0000000000..781b6df4ad
--- /dev/null
+++ b/contrib/MathEval/common.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 1999, 2002, 2003  Free Software Foundation, Inc.
+ * 
+ * This file is part of GNU libmatheval
+ * 
+ * GNU libmatheval 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, or (at your option) any later
+ * version.
+ * 
+ * GNU libmatheval 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
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef COMMON_H
+#define COMMON_H
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "Malloc.h"
+
+/* Macro definitions to simplify corresponding function calls.  */
+#define XMALLOC(type, num) ((type *) Malloc ((num) * sizeof(type)))
+#define XREALLOC(type, p, num) ((type *) Realloc ((p), (num) * sizeof(type)))
+#define XCALLOC(type, num) ((type *) Calloc ((num), sizeof(type)))
+#define XFREE(stale) Free (stale);
+
+#endif
diff --git a/contrib/MathEval/matheval.cpp b/contrib/MathEval/matheval.cpp
new file mode 100644
index 0000000000..efc5e1b1cb
--- /dev/null
+++ b/contrib/MathEval/matheval.cpp
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 1999, 2002, 2003  Free Software Foundation, Inc.
+ *
+ * This file is part of GNU libmatheval
+ *
+ * GNU libmatheval 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, or (at your option) any later
+ * version.
+ *
+ * GNU libmatheval 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
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* This file was modified for inclusion in Gmsh */
+
+#include "common.h"
+#include "matheval.h"
+#include "node.h"
+#include "symbol_table.h"
+
+/* Minimal length of evaluator symbol table.  */
+#define MIN_TABLE_LENGTH 211
+
+/*
+ * Function used to parse string representing function (this function is
+ * generated by parser generator).
+ */
+extern int meparse();
+
+/*
+ * Following variables are needed for parsing (parser is able to communicate
+ * with program from which it is used through global variables only).
+ */
+char           *matheval_input_string;	/* String representing function.  */
+Node           *matheval_root;		/* Root of tree representation of
+					 * function.  */
+SymbolTable    *matheval_symbol_table;	/* Evaluator symbol table.  */
+int             matheval_ok;		/* Flag determining if parsing went OK.  */
+
+/* Data structure representing evaluator.  */
+typedef struct {
+  Node           *root;	/* Root of tree representation of
+			 * function.  */
+  SymbolTable    *symbol_table;	/* Evalutor symbol table.  */
+} Evaluator;
+
+void *
+evaluator_create(char *string)
+{
+  Evaluator      *evaluator;	/* Evaluator representing function given
+				 * by string.  */
+  char           *stringn;	/* Copy of string terminated by newline
+				 * character.  */
+  
+  /*
+   * Copy string representing function and terminate it with newline
+   * (this is necessary because parser expect newline character to
+   * terminate its input).
+   */
+  stringn = XMALLOC(char, strlen(string) + 2);
+  
+  strcpy(stringn, string);
+  strcat(stringn, "\n");
+  
+  /*
+   * Initialize global variables used by parser.
+   */
+  matheval_input_string = stringn;
+  matheval_root = NULL;
+  matheval_symbol_table = symbol_table_create(MIN_TABLE_LENGTH);
+  matheval_ok = 1;
+  
+  /*
+   * Do parsing.
+   */
+  meparse();
+  
+  /*
+   * Free copy of string representing function.
+   */
+  XFREE(stringn);
+  
+  /*
+   * Return null pointer as error indicator if parsing error occured.
+   */
+  if (!matheval_ok || !matheval_root){
+    node_destroy(matheval_root);
+    symbol_table_destroy(matheval_symbol_table);
+    return NULL;
+  }
+  
+  /*
+   * Simplify tree represention of function.
+   */
+  matheval_root = node_simplify(matheval_root);
+  
+  /*
+   * Allocate memory for and initialize evaluator data structure.
+   */
+  evaluator = XMALLOC(Evaluator, 1);
+  evaluator->root = matheval_root;
+  evaluator->symbol_table = matheval_symbol_table;
+  
+  return evaluator;
+}
+
+void
+evaluator_destroy(void *evaluator)
+{
+  /*
+   * Destroy tree represention of function, symbol table, as well as
+   * data structure representing evaluator.
+   */
+  node_destroy(((Evaluator *) evaluator)->root);
+  symbol_table_destroy(((Evaluator *) evaluator)->symbol_table);
+  XFREE(evaluator);
+}
+
+double
+evaluator_evaluate(void *evaluator, int count, char **names, double *values)
+{
+  Record         *record;	/* Symbol table record corresponding to
+				 * give variable name.  */
+  int             i;		/* Loop counter.  */
+  
+  /*
+   * Assign values to symbol table records corresponding to variable
+   * names.
+   */
+  for (i = 0; i < count; i++) {
+    record = symbol_table_lookup(((Evaluator *) evaluator)->symbol_table, names[i]);
+    if (record && record->type == 'v')
+      record->data.value = values[i];
+  }
+  
+  /*
+   * Evaluate function value using tree represention of function.
+   */
+  return node_evaluate(((Evaluator *) evaluator)->root);
+}
+
+int
+evaluator_calculate_length(void *evaluator)
+{
+  /*
+   * Calculate length of evaluator textual representation.
+   */
+  return node_calculate_length(((Evaluator *) evaluator)->root);
+}
+
+void
+evaluator_write(void *evaluator, char *string)
+{
+  /*
+   * Write evaluator textual representation to given string.
+   */
+  node_write(((Evaluator *) evaluator)->root, string);
+}
+
+void *
+evaluator_derivative(void *evaluator, char *name)
+{
+  Evaluator      *derivative;	/* Derivative function evaluator.  */
+  
+  /*
+   * Allocate memory for and initalize data structure for evaluator
+   * representing derivative of function given by evaluator.
+   */
+  derivative = XMALLOC(Evaluator, 1);
+  derivative->root = node_simplify(node_derivative(((Evaluator *) evaluator)->root, name,
+						   ((Evaluator *) evaluator)->symbol_table));
+  derivative->symbol_table = symbol_table_assign(((Evaluator *) evaluator)->symbol_table);
+  
+  return derivative;
+}
+
+double
+evaluator_evaluate_x(void *evaluator, double x)
+{
+  char           *names[] = {"x"};	/* Array of variable names.  */
+  double          values[] = {x};	/* Array of variable values.  */
+  
+  /*
+   * Evaluate function for given values of variable "x".
+   */
+  return evaluator_evaluate(evaluator, sizeof(names) / sizeof(names[0]), names, values);
+}
+
+double
+evaluator_evaluate_x_y(void *evaluator, double x, double y)
+{
+  char           *names[] = {"x", "y"};	/* Array of variable
+					 * names.  */
+  double          values[] = {x, y};	/* Array of variable values.  */
+  
+  /*
+   * Evaluate function for given values of variable "x" and "y".
+   */
+  return evaluator_evaluate(evaluator, sizeof(names) / sizeof(names[0]), names, values);
+}
+
+double
+evaluator_evaluate_x_y_z(void *evaluator, double x, double y, double z)
+{
+  char           *names[] = {"x", "y", "z"};	/* Array of variable
+						 * names.  */
+  double          values[] = {x, y, z};	/* Array of variable
+					 * values.  */
+  
+  /*
+   * Evaluate function for given values of variable "x", "y" and "z".
+   */
+  return evaluator_evaluate(evaluator, sizeof(names) / sizeof(names[0]), names, values);
+}
+
+void *
+evaluator_derivative_x(void *evaluator)
+{
+  /*
+   * Differentiate function using derivation variable "x".
+   */
+  return evaluator_derivative(evaluator, "x");
+}
+
+void *
+evaluator_derivative_y(void *evaluator)
+{
+  /*
+   * Differentiate function using derivation variable "y".
+   */
+  return evaluator_derivative(evaluator, "y");
+}
+
+void *
+evaluator_derivative_z(void *evaluator)
+{
+  /*
+   * Differentiate function using derivation variable "z".
+   */
+  return evaluator_derivative(evaluator, "z");
+}
diff --git a/contrib/MathEval/matheval.h b/contrib/MathEval/matheval.h
new file mode 100644
index 0000000000..b155f4003c
--- /dev/null
+++ b/contrib/MathEval/matheval.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 1999, 2002, 2003  Free Software Foundation, Inc.
+ * 
+ * This file is part of GNU libmatheval
+ * 
+ * GNU libmatheval 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, or (at your option) any later
+ * version.
+ * 
+ * GNU libmatheval 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
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* This file was modified for inclusion in Gmsh */
+
+#ifndef EVALUATOR_H
+#define EVALUATOR_H
+
+/*
+ * Create evaluator from string representing function.  Function
+ * returns pointer that should be passed as first argument to all
+ * other library functions.  If an error occurs, function will return
+ * null pointer.
+ */
+void    *evaluator_create(char *string);
+
+/*
+ * Destroy evaluator specified.
+ */
+void     evaluator_destroy(void *evaluator);
+
+/*
+ * Evaluate function represented by evaluator given.  Variable names
+ * and respective values are represented by function third and fourth
+ * argument. Number of variables i.e. length of these two arrays is
+ * given by second argument.  Function returns evaluated function
+ * value.  In case that function contains variables with names not
+ * given through third function argument, value of this variable is
+ * undeterminated.
+ */
+double   evaluator_evaluate(void *evaluator, int count, char **names, double *values);
+
+/*
+ * Calculate length of textual representation of evaluator. This
+ * procedure is inteded to be used along with evaluator_write()
+ * procedure that follows.
+ */
+int      evaluator_calculate_length(void *evaluator);
+
+/*
+ * Write textual representation of evaluator (i.e. corresponding
+ * function) to given string.  No string overflow is checked by this
+ * procedure; string of appropriate length (calculated beforehand
+ * using above evaluator_calculate_length() procedure) is expected to
+ * be allocated beforehand.
+ */
+void     evaluator_write(void *evaluator, char *length);
+
+/*
+ * Create evaluator for first derivative of function represented by
+ * evaluator given as first argument using derivative variable given
+ * as second argument.
+ */
+void    *evaluator_derivative(void *evaluator, char *name);
+
+/*
+ * Helper functions to simplify evaluation when variable names are
+ * "x", "x" and "y" or "x" and "y" and "z" respectively.
+ */
+double   evaluator_evaluate_x(void *evaluator, double x);
+double   evaluator_evaluate_x_y(void *evaluator, double x, double y);
+double   evaluator_evaluate_x_y_z(void *evaluator, double x, double y, double z);
+
+/*
+ * Helper functions to simplify differentiation when variable names
+ * are "x" or "y" or "z" respectively.
+ */
+void    *evaluator_derivative_x(void *evaluator);
+void    *evaluator_derivative_y(void *evaluator);
+void    *evaluator_derivative_z(void *evaluator);
+
+#endif
diff --git a/contrib/MathEval/node.cpp b/contrib/MathEval/node.cpp
new file mode 100644
index 0000000000..420dfb6717
--- /dev/null
+++ b/contrib/MathEval/node.cpp
@@ -0,0 +1,653 @@
+/*
+ * Copyright (C) 1999, 2002, 2003  Free Software Foundation, Inc.
+ *
+ * This file is part of GNU libmatheval
+ *
+ * GNU libmatheval 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, or (at your option) any later
+ * version.
+ *
+ * GNU libmatheval 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
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <assert.h>
+#include <stdarg.h>
+#include "common.h"
+#include "node.h"
+
+Node *
+node_create(char type,...)
+{
+  Node           *node;	/* New node.  */
+  va_list         ap;		/* Variable argument list.  */
+  
+  /*
+   * Allocate memory for node and initialize its type.
+   */
+  node = XMALLOC(Node, 1);
+  node->type = type;
+  
+  /*
+   * According to node type, initialize rest of the node from variable
+   * argument list.
+   */
+  va_start(ap, type);
+  switch (node->type) {
+  case 'c':
+    /*
+     * Initialize constant value.
+     */
+    node->data.constant = va_arg(ap, double);
+    break;
+    
+  case 'v':
+    /*
+     * Remember pointer to symbol table record describing
+     * variable.
+     */
+    node->data.variable = va_arg(ap, Record *);
+    break;
+    
+  case 'f':
+    /*
+     * Remember pointer to symbol table record describing
+     * function and initialize function argument.
+     */
+    node->data.function.record = va_arg(ap, Record *);
+    node->data.function.child = va_arg(ap, Node *);
+    break;
+    
+  case 'u':
+    /*
+     * Initialize operator type and operand.
+     */
+    node->data.un_op.operatorr = (char) va_arg(ap, int);
+    node->data.un_op.child = va_arg(ap, Node *);
+    break;
+    
+  case 'b':
+    /*
+     * Initialize operator type and operands.
+     */
+    node->data.un_op.operatorr = (char) va_arg(ap, int);
+    node->data.bin_op.left = va_arg(ap, Node *);
+    node->data.bin_op.right = va_arg(ap, Node *);
+    break;
+    
+  default:
+    assert(0);
+  }
+  va_end(ap);
+  
+  return node;
+}
+
+void
+node_destroy(Node * node)
+{
+  /*
+   * Skip if node already null (this may occur during simplification).
+   */
+  if (!node)
+    return;
+  
+  /*
+   * If necessary, destroy subtree rooted at node.
+   */
+  switch (node->type) {
+  case 'c':
+  case 'v':
+    break;
+    
+  case 'f':
+    node_destroy(node->data.function.child);
+    break;
+    
+  case 'u':
+    node_destroy(node->data.un_op.child);
+    break;
+    
+  case 'b':
+    node_destroy(node->data.bin_op.left);
+    node_destroy(node->data.bin_op.right);
+    break;
+  }
+  
+  /*
+   * Deallocate memory used by node.
+   */
+  XFREE(node);
+}
+
+Node *
+node_copy(Node * node)
+{
+  /*
+   * According to node type, create (deep) copy of subtree rooted at
+   * node.
+   */
+  switch (node->type) {
+  case 'c':
+    return node_create('c', node->data.constant);
+    
+  case 'v':
+    return node_create('v', node->data.variable);
+    
+  case 'f':
+    return node_create('f', node->data.function.record, node_copy(node->data.function.child));
+    
+  case 'u':
+    return node_create('u', node->data.un_op.operatorr, node_copy(node->data.un_op.child));
+    
+  case 'b':
+    return node_create('b', node->data.bin_op.operatorr, node_copy(node->data.bin_op.left), node_copy(node->data.bin_op.right));
+  }
+  
+  return NULL;
+}
+
+Node *
+node_simplify(Node * node)
+{
+  /*
+   * According to node type, apply further simplifications.
+   */
+  switch (node->type) {
+  case 'c':
+  case 'v':
+    return node;
+    
+  case 'f':
+    /*
+     * Simplify function argument and if constant evaluate function
+     * and replace function node with constant node (unless the
+     * function is Rand(x)).
+     */
+    node->data.function.child = node_simplify(node->data.function.child);
+    if (node->data.function.child->type == 'c' && 
+	strcmp(node->data.function.record->name, "Rand")) {
+      double          value = node_evaluate(node);
+      
+      node_destroy(node);
+      return node_create('c', value);
+    }
+    else
+      return node;
+    
+  case 'u':
+    /*
+     * Simplify unary operator operand and if constant apply
+     * operator and replace operator node with constant node.
+     */
+    node->data.un_op.child = node_simplify(node->data.un_op.child);
+    if (node->data.un_op.operatorr == '-' && node->data.un_op.child->type == 'c') {
+      double          value = node_evaluate(node);
+      
+      node_destroy(node);
+      return node_create('c', value);
+    } 
+    else
+      return node;
+    
+  case 'b':
+    /*
+     * Simplify binary operator operands.
+     */
+    node->data.bin_op.left = node_simplify(node->data.bin_op.left);
+    node->data.bin_op.right = node_simplify(node->data.bin_op.right);
+    
+    /*
+     * If operands constant apply operator and replace operator
+     * node with constant node.
+     */
+    if (node->data.bin_op.left->type == 'c' && node->data.bin_op.right->type == 'c') {
+      double          value = node_evaluate(node);
+      
+      node_destroy(node);
+      return node_create('c', value);
+    }
+    /*
+     * Eliminate 0 as neutral addition operand.
+     */
+    else if (node->data.bin_op.operatorr == '+')
+      if (node->data.bin_op.left->type == 'c' && node->data.bin_op.left->data.constant == 0) {
+	Node *right;
+	right = node->data.bin_op.right;
+	node->data.bin_op.right = NULL;
+	node_destroy(node);
+	return right;
+      } 
+      else if (node->data.bin_op.right->type == 'c' && node->data.bin_op.right->data.constant == 0) {
+	Node *left;
+	left = node->data.bin_op.left;
+	node->data.bin_op.left = NULL;
+	node_destroy(node);
+	return left;
+      }
+      else
+	return node;
+    /*
+     * Eliminate 0 as neutral subtraction right operand.
+     */
+    else if (node->data.bin_op.operatorr == '-')
+      if (node->data.bin_op.right->type == 'c' && node->data.bin_op.right->data.constant == 0) {
+	Node *left;
+	left = node->data.bin_op.left;
+	node->data.bin_op.left = NULL;
+	node_destroy(node);
+	return left;
+      }
+      else
+	return node;
+    /*
+     * Eliminate 1 as neutral multiplication operand.
+     */
+    else if (node->data.bin_op.operatorr == '*')
+      if (node->data.bin_op.left->type == 'c' && node->data.bin_op.left->data.constant == 1) {
+	Node *right;
+	right = node->data.bin_op.right;
+	node->data.bin_op.right = NULL;
+	node_destroy(node);
+	return right;
+      } 
+      else if (node->data.bin_op.right->type == 'c' && node->data.bin_op.right->data.constant == 1) {
+	Node *left;
+	left = node->data.bin_op.left;
+	node->data.bin_op.left = NULL;
+	node_destroy(node);
+	return left;
+      }
+      else
+	return node;
+    /*
+     * Eliminate 1 as neutral division right operand.
+     */
+    else if (node->data.bin_op.operatorr == '/')
+      if (node->data.bin_op.right->type == 'c' && node->data.bin_op.right->data.constant == 1) {
+	Node *left;
+	left = node->data.bin_op.left;
+	node->data.bin_op.left = NULL;
+	node_destroy(node);
+	return left;
+      }
+      else
+	return node;
+    /*
+     * Eliminate 0 and 1 as both left and right exponentiation
+     * operands.
+     */
+    else if (node->data.bin_op.operatorr == '^')
+      if (node->data.bin_op.left->type == 'c' && node->data.bin_op.left->data.constant == 0) {
+	node_destroy(node);
+	return node_create('c', 0.0);
+      } 
+      else if (node->data.bin_op.left->type == 'c' && node->data.bin_op.left->data.constant == 1) {
+	node_destroy(node);
+	return node_create('c', 1.0);
+      }
+      else if (node->data.bin_op.right->type == 'c' && node->data.bin_op.right->data.constant == 0) {
+	node_destroy(node);
+	return node_create('c', 1.0);
+      }
+      else if (node->data.bin_op.right->type == 'c' && node->data.bin_op.right->data.constant == 1) {
+	Node *left;
+	left = node->data.bin_op.left;
+	node->data.bin_op.left = NULL;
+	node_destroy(node);
+	return left;
+      }
+      else
+	return node;
+    else
+      return node;
+  }
+  
+  return NULL;
+}
+
+double
+node_evaluate(Node * node)
+{
+  /*
+   * According to node type, evaluate subtree rooted at node.
+   */
+  switch (node->type) {
+  case 'c':
+    return node->data.constant;
+    
+  case 'v':
+    /*
+     * Variable values are used from symbol table.
+     */
+    return node->data.variable->data.value;
+    
+  case 'f':
+    /*
+     * Functions are evaluated through symbol table.
+     */
+    return (*node->data.function.record->data.function) (node_evaluate(node->data.function.child));
+    
+  case 'u':
+    /*
+     * Unary operator node is evaluated according to operator
+     * type.
+     */
+    switch (node->data.un_op.operatorr) {
+    case '-':
+      return -node_evaluate(node->data.un_op.child);
+    }
+    
+  case 'b':
+    /*
+     * Binary operator node is evaluated according to operator
+     * type.
+     */
+    switch (node->data.un_op.operatorr) {
+    case '+':
+      return node_evaluate(node->data.bin_op.left) + node_evaluate(node->data.bin_op.right);
+      
+    case '-':
+      return node_evaluate(node->data.bin_op.left) - node_evaluate(node->data.bin_op.right);
+      
+    case '*':
+      return node_evaluate(node->data.bin_op.left) * node_evaluate(node->data.bin_op.right);
+      
+    case '/':
+      return node_evaluate(node->data.bin_op.left) / node_evaluate(node->data.bin_op.right);
+      
+    case '^':
+      return pow(node_evaluate(node->data.bin_op.left), node_evaluate(node->data.bin_op.right));
+    }
+  }
+  
+  return 0;
+}
+
+Node *
+node_derivative(Node * node, char *name, SymbolTable * symbol_table)
+{
+  /*
+   * According to node type, derivative tree for subtree rooted at node
+   * is created.
+   */
+  switch (node->type) {
+  case 'c':
+    /*
+     * Derivative of constant equals 0.
+     */
+    return node_create('c', 0.0);
+    
+  case 'v':
+    /*
+     * Derivative of variable equals 1 if variable is derivative
+     * variable, 0 otherwise.
+     */
+    return node_create('c', (!strcmp(name, node->data.variable->name)) ? 1.0 : 0.0);
+    
+  case 'f':
+    /*
+     * Apply rule of exponential function derivative.
+     */
+    if (!strcmp(node->data.function.record->name, "Exp"))
+      return node_create('b', '*', node_derivative(node->data.function.child, name, symbol_table), node_copy(node));
+    /*
+     * Apply rule of logarithmic function derivative.
+     */
+    else if (!strcmp(node->data.function.record->name, "Log"))
+      return node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_copy(node->data.function.child));
+    /*
+     * Apply rule of square root function derivative.
+     */
+    else if (!strcmp(node->data.function.record->name, "Sqrt"))
+      return node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('b', '*', node_create('c', 2.0), node_copy(node)));
+    /*
+     * Apply rule of sine function derivative.
+     */
+    else if (!strcmp(node->data.function.record->name, "Sin"))
+      return node_create('b', '*', node_derivative(node->data.function.child, name, symbol_table), node_create('f', symbol_table_lookup(symbol_table, "Cos"), node_copy(node->data.function.child)));
+    /*
+     * Apply rule of cosine function derivative.
+     */
+    else if (!strcmp(node->data.function.record->name, "Cos"))
+      return node_create('u', '-', node_create('b', '*', node_derivative(node->data.function.child, name, symbol_table), node_create('f', symbol_table_lookup(symbol_table, "Sin"), node_copy(node->data.function.child))));
+    /*
+     * Apply rule of tangent function derivative.
+     */
+    else if (!strcmp(node->data.function.record->name, "Tan"))
+      return node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('b', '^', node_create('f', symbol_table_lookup(symbol_table, "Cos"), node_copy(node->data.function.child)), node_create('c', 2.0)));
+    /*
+     * Apply rule of cotangent function derivative.
+     */
+    else if (!strcmp(node->data.function.record->name, "Ctan"))
+      return node_create('u', '-', node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('b', '^', node_create('f', symbol_table_lookup(symbol_table, "Sin"), node_copy(node->data.function.child)), node_create('c', 2.0))));
+    /*
+     * Apply rule of inverse sine function derivative.
+     */
+    else if (!strcmp(node->data.function.record->name, "Asin"))
+      return node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('f', symbol_table_lookup(symbol_table, "Sqrt"), node_create('b', '-', node_create('c', 1.0), node_create('b', '^', node_copy(node->data.function.child), node_create('c', 2.0)))));
+    /*
+     * Apply rule of inverse cosine function derivative.
+     */
+    else if (!strcmp(node->data.function.record->name, "Acos"))
+      return node_create('u', '-', node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('f', symbol_table_lookup(symbol_table, "Sqrt"), node_create('b', '-', node_create('c', 1.0), node_create('b', '^', node_copy(node->data.function.child), node_create('c', 2.0))))));
+    /*
+     * Apply rule of inverse tangent function derivative.
+     */
+    else if (!strcmp(node->data.function.record->name, "Atan"))
+      return node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('b', '+', node_create('c', 1.0), node_create('b', '^', node_copy(node->data.function.child), node_create('c', 2.0))));
+    /*
+     * Apply rule of inverse cotanget function derivative.
+     */
+    else if (!strcmp(node->data.function.record->name, "Actan"))
+      return node_create('u', '-', node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('b', '+', node_create('c', 1.0), node_create('b', '^', node_copy(node->data.function.child), node_create('c', 2.0)))));
+    /*
+     * Apply rule of hyperbolic sine function derivative.
+     */
+    else if (!strcmp(node->data.function.record->name, "Sinh"))
+      return node_create('b', '*', node_derivative(node->data.function.child, name, symbol_table), node_create('f', symbol_table_lookup(symbol_table, "Cosh"), node_copy(node->data.function.child)));
+    /*
+     * Apply rule of hyperbolic cosine function derivative.
+     */
+    else if (!strcmp(node->data.function.record->name, "Cosh"))
+      return node_create('b', '*', node_derivative(node->data.function.child, name, symbol_table), node_create('f', symbol_table_lookup(symbol_table, "Sinh"), node_copy(node->data.function.child)));
+    /*
+     * Apply rule of hyperbolic tangent function derivative.
+     */
+    else if (!strcmp(node->data.function.record->name, "Tanh"))
+      return node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('b', '^', node_create('f', symbol_table_lookup(symbol_table, "Cosh"), node_copy(node->data.function.child)), node_create('c', 2.0)));
+    /*
+     * Apply rule of hyperbolic cotangent function derivative.
+     */
+    else if (!strcmp(node->data.function.record->name, "Ctanh"))
+      return node_create('u', '-', node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('b', '^', node_create('f', symbol_table_lookup(symbol_table, "sinh"), node_copy(node->data.function.child)), node_create('c', 2.0))));
+    /*
+     * Apply rule of inverse hyperbolic sine function derivative.
+     */
+    else if (!strcmp(node->data.function.record->name, "Asinh"))
+      return node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('f', symbol_table_lookup(symbol_table, "Sqrt"), node_create('b', '-', node_create('c', 1.0), node_create('b', '^', node_copy(node->data.function.child), node_create('c', 2.0)))));
+    /*
+     * Apply rule of inverse hyperbolic cosine function
+     * derivative.
+     */
+    else if (!strcmp(node->data.function.record->name, "Acosh"))
+      return node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('f', symbol_table_lookup(symbol_table, "Sqrt"), node_create('b', '-', node_create('b', '^', node_copy(node->data.function.child), node_create('c', 2.0)), node_create('c', 1.0))));
+    /*
+     * Apply rule of inverse hyperbolic tangent function
+     * derivative.
+     */
+    else if (!strcmp(node->data.function.record->name, "Atanh"))
+      return node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('b', '-', node_create('c', 1.0), node_create('b', '^', node_copy(node->data.function.child), node_create('c', 2.0))));
+    /*
+     * Apply rule of inverse hyperbolic cotangent function
+     * derivative.
+     */
+    else if (!strcmp(node->data.function.record->name, "Actanh"))
+      return node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('b', '-', node_create('b', '^', node_copy(node->data.function.child), node_create('c', 2.0)), node_create('c', 1.0)));
+    /*
+     * Apply rule of absolute value function derivative.
+     */
+    else if (!strcmp(node->data.function.record->name, "Fabs"))
+      return node_create('b', '/', node_create('b', '*', node_derivative(node->data.function.child, name, symbol_table), node_copy(node->data.function.child)), node_create('f', symbol_table_lookup(symbol_table, "Sqrt"), node_create('b', '^', node_copy(node->data.function.child), node_create('c', 2.0))));
+    
+  case 'u':
+    switch (node->data.un_op.operatorr) {
+    case '-':
+      /*
+       * Apply (-f)'=-f' derivative rule.
+       */
+      return node_create('u', '-', node_derivative(node->data.un_op.child, name, symbol_table));
+    }
+    
+  case 'b':
+    switch (node->data.bin_op.operatorr) {
+    case '+':
+      /*
+       * Apply (f+g)'=f'+g' derivative rule.
+       */
+      return node_create('b', '+', node_derivative(node->data.bin_op.left, name, symbol_table), node_derivative(node->data.bin_op.right, name, symbol_table));
+      
+    case '-':
+      /*
+       * Apply (f-g)'=f'-g' derivative rule.
+       */
+      return node_create('b', '-', node_derivative(node->data.bin_op.left, name, symbol_table), node_derivative(node->data.bin_op.right, name, symbol_table));
+      
+    case '*':
+      /*
+       * Apply (f*g)'=f'*g+f*g' derivative rule.
+       */
+      return node_create('b', '+', node_create('b', '*', node_derivative(node->data.bin_op.left, name, symbol_table), node_copy(node->data.bin_op.right)), node_create('b', '*', node_copy(node->data.bin_op.left), node_derivative(node->data.bin_op.right, name, symbol_table)));
+      
+    case '/':
+      /*
+       * Apply (f/g)'=(f'*g-f*g')/g^2 derivative rule.
+       */
+      return node_create('b', '/', node_create('b', '-', node_create('b', '*', node_derivative(node->data.bin_op.left, name, symbol_table), node_copy(node->data.bin_op.right)), node_create('b', '*', node_copy(node->data.bin_op.left), node_derivative(node->data.bin_op.right, name, symbol_table))), node_create('b', '^', node_copy(node->data.bin_op.right), node_create('c', 2.0)));
+      
+    case '^':
+      /*
+       * If right operand of exponentiation constant apply
+       * (f^n)'=n*f^(n-1)*f' derivative rule.
+       */
+      if (node->data.bin_op.right->type == 'c')
+	return node_create('b', '*', node_create('b', '*', node_create('c', node->data.bin_op.right->data.constant), node_derivative(node->data.bin_op.left, name, symbol_table)), node_create('b', '^', node_copy(node->data.bin_op.left), node_create('c', node->data.bin_op.right->data.constant - 1.0)));
+      /*
+       * Otherwise, apply logaritmhic derivative rule:
+       * (log(f^g))'=(f^g)'/f^g =>
+       * (f^g)'=f^g*(log(f^g))'=f^g*(g*log(f))'
+       */
+      else {
+	Node *log_node, *derivative;
+	log_node = node_create('b', '*', node_copy(node->data.bin_op.right), node_create('f', symbol_table_lookup(symbol_table, "Log"), node_copy(node->data.bin_op.left)));
+	derivative = node_create('b', '*', node_copy(node), node_derivative(log_node, name, symbol_table));
+	node_destroy(log_node);
+	return derivative;
+      }
+    }
+  }
+  
+  return NULL;
+}
+
+int
+node_calculate_length(Node * node)
+{
+  char            string[1024];	/* String representing constant node
+				 * value. */
+  int             length;	/* Length of above string. */
+  
+  /*
+   * According to node type, calculate length of string representing
+   * subtree rooted at node.
+   */
+  switch (node->type) {
+  case 'c':
+    length = 0;
+    if (node->data.constant < 0)
+      length += 1;
+    sprintf(string, "%g", node->data.constant);
+    length += strlen(string);
+    if (node->data.constant < 0)
+      length += 1;
+    return length;
+    
+  case 'v':
+    return strlen(node->data.variable->name);
+    
+  case 'f':
+    return strlen(node->data.function.record->name) + 1 + node_calculate_length(node->data.function.child) + 1;
+    break;
+    
+  case 'u':
+    return 1 + 1 + node_calculate_length(node->data.un_op.child) + 1;
+    
+  case 'b':
+    return 1 + node_calculate_length(node->data.bin_op.left) + 1 + node_calculate_length(node->data.bin_op.right) + 1;
+  }
+  
+  return 0;
+}
+
+void
+node_write(Node * node, char *string)
+{
+  /*
+   * According to node type, write subtree rooted at node to node
+   * string variable.  Always use parenthesis to resolve operator
+   * precedence.
+   */
+  switch (node->type) {
+  case 'c':
+    if (node->data.constant < 0) {
+      sprintf(string, "%c", '(');
+      string += strlen(string);
+    }
+    sprintf(string, "%g", node->data.constant);
+    string += strlen(string);
+    if (node->data.constant < 0)
+      sprintf(string, "%c", ')');
+    break;
+    
+  case 'v':
+    sprintf(string, "%s", node->data.variable->name);
+    break;
+    
+  case 'f':
+    sprintf(string, "%s%c", node->data.function.record->name, '(');
+    string += strlen(string);
+    node_write(node->data.function.child, string);
+    string += strlen(string);
+    sprintf(string, "%c", ')');
+    break;
+    
+  case 'u':
+    sprintf(string, "%c", '(');
+    string += strlen(string);
+    sprintf(string, "%c", node->data.un_op.operatorr);
+    string += strlen(string);
+    node_write(node->data.un_op.child, string);
+    string += strlen(string);
+    sprintf(string, "%c", ')');
+    break;
+    
+  case 'b':
+    sprintf(string, "%c", '(');
+    string += strlen(string);
+    node_write(node->data.bin_op.left, string);
+    string += strlen(string);
+    sprintf(string, "%c", node->data.bin_op.operatorr);
+    string += strlen(string);
+    node_write(node->data.bin_op.right, string);
+    string += strlen(string);
+    sprintf(string, "%c", ')');
+    break;
+  }
+}
diff --git a/contrib/MathEval/node.h b/contrib/MathEval/node.h
new file mode 100644
index 0000000000..c2744aae19
--- /dev/null
+++ b/contrib/MathEval/node.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 1999, 2002, 2003  Free Software Foundation, Inc.
+ * 
+ * This file is part of GNU libmatheval
+ * 
+ * GNU libmatheval 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, or (at your option) any later
+ * version.
+ * 
+ * GNU libmatheval 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
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* This file was modified for inclusion in Gmsh */
+
+#ifndef NODE_H
+#define NODE_H 1
+
+#include "symbol_table.h"
+
+/* Data structure representing function tree node.  */
+typedef struct _Node {
+  char            type;	/* Node type ('c' for constant, 'v' for
+			 * variable, 'f' for function, 'u' for unary
+			 * operator, 'b' for binary operator).  */
+  union {
+    double          constant;	/* Constant value.  */
+    Record         *variable;	/* Symbol table record for
+				 * variable.  */
+    struct {
+      Record         *record;	/* Symbol table record for
+				 * function.  */
+      struct _Node   *child;	/* Function argument node.  */
+    }               function;	/* Structure representing
+				 * function.  */
+    struct {
+      char            operatorr;/* Operator type ('-'
+				 * for unary minus).  */
+      struct _Node   *child;	/* Operand node.  */
+    }               un_op;	/* Structure representing unary
+				 * operator.  */
+    struct {
+      char            operatorr;/* Operator type ('+'
+				 * for adition, '-' for
+				 * subtraction, '*' for
+				 * multiplication, '/'
+				 * for division and '^'
+				 * for exponentiation).  */
+      struct _Node   *left, *right;	/* Operands nodes.  */
+    }               bin_op;	/* Structure representing binary
+				 * operator.  */
+  }               data;
+}               Node;
+
+/*
+ * Create node of given type and initialize it from optional arguments.
+ * Function returns pointer to node object that should be passed as first
+ * argument to all other node functions.
+ */
+Node           *node_create(char type,...);
+
+/* Destroy subtree rooted at specified node.  */
+void            node_destroy(Node * node);
+
+/*
+ * Make a copy of subtree rooted at given node.  Deep copy operation is
+ * employed.
+ */
+Node           *node_copy(Node * node);
+
+/*
+ * Simplify subtree rooted at given node.  Function returns root of
+ * simplified subtree (that may or may not be original node).
+ */
+Node           *node_simplify(Node * node);
+
+/*
+ * Evaluate subtree rooted at given node.  For variables, values from symbol
+ * table are used.
+ */
+double          node_evaluate(Node * node);
+
+/*
+ * Create derivative tree for subtree rooted at given node.  Second argument
+ * is derivation variable, third argument is symbol table (needed for
+ * functions derivatives).  Function returns root of corresponding derivation
+ * tree.
+ */
+Node           *node_derivative(Node * node, char *name, SymbolTable * symbol_table);
+
+/*
+ * Calculate length of the string representing subtree rooted at specified
+ * node.
+ */
+int             node_calculate_length(Node * node);
+
+/*
+ * Write subtree rooted at specified node to given string variable.  No
+ * checking of string overflow is done by this procedure; it is expected that
+ * string of appropriate length is passed as argument.
+ */
+void            node_write(Node * node, char *string);
+
+#endif
diff --git a/contrib/MathEval/parser.tab.cpp b/contrib/MathEval/parser.tab.cpp
new file mode 100644
index 0000000000..87a34199c0
--- /dev/null
+++ b/contrib/MathEval/parser.tab.cpp
@@ -0,0 +1,1044 @@
+
+/*  A Bison parser, made from parser.y
+    by GNU Bison version 1.28  */
+
+#define YYBISON 1  /* Identify Bison output.  */
+
+#define yyparse meparse
+#define yylex melex
+#define yyerror meerror
+#define yylval melval
+#define yychar mechar
+#define yydebug medebug
+#define yynerrs menerrs
+#define	NUMBER	257
+#define	VARIABLE	258
+#define	FUNCTION	259
+#define	NEG	260
+
+#line 1 "parser.y"
+
+/*
+ * Copyright (C) 1999, 2002, 2003  Free Software Foundation, Inc.
+ * 
+ * This file is part of GNU libmatheval
+ *
+ * GNU libmatheval 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, or (at your option)
+ * any later version.
+ *
+ * GNU libmatheval 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 program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/* This file was modified for inclusion in Gmsh */
+
+#include "common.h"
+#include "node.h"
+
+/* Variables used to communicate with code using parser.  */
+extern Node* matheval_root; /* Root of tree representation of function.  */
+extern int matheval_ok; /* Flag representing success of parsing.  */
+
+/* Report parsing error.  */
+int yyerror (char *s);
+
+/* Function used to tokenize string representing function (this function
+   is generated by scanner generator).  */
+int yylex (void);
+
+/* Function used to flush the internal flex buffer when we exit
+   prematurely (i.e., in case of a parse error) so that the next time
+   we call the scanner, all is nicely reset. Without this, the
+   behaviour of the next call after an error is unpredictable.  */
+void force_buffer_flush(void);
+
+
+#line 48 "parser.y"
+typedef union {
+  Node *node;
+  Record *record;
+} YYSTYPE;
+#include <stdio.h>
+
+#ifndef __cplusplus
+#ifndef __STDC__
+#define const
+#endif
+#endif
+
+
+
+#define	YYFINAL		26
+#define	YYFLAG		-32768
+#define	YYNTBASE	15
+
+#define YYTRANSLATE(x) ((unsigned)(x) <= 260 ? yytranslate[x] : 17)
+
+static const char yytranslate[] = {     0,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,    12,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,    13,
+    14,     8,     7,     2,     6,     2,     9,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,    11,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     1,     3,     4,     5,    10
+};
+
+#if YYDEBUG != 0
+static const short yyprhs[] = {     0,
+     0,     3,     5,     7,    11,    15,    19,    23,    26,    30,
+    35
+};
+
+static const short yyrhs[] = {    16,
+    12,     0,     3,     0,     4,     0,    16,     7,    16,     0,
+    16,     6,    16,     0,    16,     8,    16,     0,    16,     9,
+    16,     0,     6,    16,     0,    16,    11,    16,     0,     5,
+    13,    16,    14,     0,    13,    16,    14,     0
+};
+
+#endif
+
+#if YYDEBUG != 0
+static const short yyrline[] = { 0,
+    71,    78,    81,    84,    88,    92,    96,   100,   104,   108,
+   112
+};
+#endif
+
+
+#if YYDEBUG != 0 || defined (YYERROR_VERBOSE)
+
+static const char * const yytname[] = {   "$","error","$undefined.","NUMBER",
+"VARIABLE","FUNCTION","'-'","'+'","'*'","'/'","NEG","'^'","'\\n'","'('","')'",
+"input","expression", NULL
+};
+#endif
+
+static const short yyr1[] = {     0,
+    15,    16,    16,    16,    16,    16,    16,    16,    16,    16,
+    16
+};
+
+static const short yyr2[] = {     0,
+     2,     1,     1,     3,     3,     3,     3,     2,     3,     4,
+     3
+};
+
+static const short yydefact[] = {     0,
+     2,     3,     0,     0,     0,     0,     0,     8,     0,     0,
+     0,     0,     0,     0,     1,     0,    11,     5,     4,     6,
+     7,     9,    10,     0,     0,     0
+};
+
+static const short yydefgoto[] = {    24,
+     6
+};
+
+static const short yypact[] = {     8,
+-32768,-32768,   -11,     8,     8,    27,     8,    -7,     9,     8,
+     8,     8,     8,     8,-32768,    18,-32768,    32,    32,    -7,
+    -7,-32768,-32768,     5,    19,-32768
+};
+
+static const short yypgoto[] = {-32768,
+    -4
+};
+
+
+#define	YYLAST		43
+
+
+static const short yytable[] = {     8,
+     9,     7,    16,    14,    25,    18,    19,    20,    21,    22,
+     1,     2,     3,     4,    10,    11,    12,    13,    26,    14,
+     5,     0,    17,    10,    11,    12,    13,     0,    14,     0,
+     0,    23,    10,    11,    12,    13,     0,    14,    15,    12,
+    13,     0,    14
+};
+
+static const short yycheck[] = {     4,
+     5,    13,     7,    11,     0,    10,    11,    12,    13,    14,
+     3,     4,     5,     6,     6,     7,     8,     9,     0,    11,
+    13,    -1,    14,     6,     7,     8,     9,    -1,    11,    -1,
+    -1,    14,     6,     7,     8,     9,    -1,    11,    12,     8,
+     9,    -1,    11
+};
+/* -*-C-*-  Note some compilers choke on comments on `#line' lines.  */
+#line 3 "/usr/share/bison.simple"
+/* This file comes from bison-1.28.  */
+
+/* Skeleton output parser for bison,
+   Copyright (C) 1984, 1989, 1990 Free Software Foundation, Inc.
+
+   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, 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.  */
+
+/* As a special exception, when this file is copied by Bison into a
+   Bison output file, you may use that output file without restriction.
+   This special exception was added by the Free Software Foundation
+   in version 1.24 of Bison.  */
+
+/* This is the parser code that is written into each bison parser
+  when the %semantic_parser declaration is not specified in the grammar.
+  It was written by Richard Stallman by simplifying the hairy parser
+  used when %semantic_parser is specified.  */
+
+#ifndef YYSTACK_USE_ALLOCA
+#ifdef alloca
+#define YYSTACK_USE_ALLOCA
+#else /* alloca not defined */
+#ifdef __GNUC__
+#define YYSTACK_USE_ALLOCA
+#define alloca __builtin_alloca
+#else /* not GNU C.  */
+#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi) || (defined (__sun) && defined (__i386))
+#define YYSTACK_USE_ALLOCA
+#include <alloca.h>
+#else /* not sparc */
+/* We think this test detects Watcom and Microsoft C.  */
+/* This used to test MSDOS, but that is a bad idea
+   since that symbol is in the user namespace.  */
+#if (defined (_MSDOS) || defined (_MSDOS_)) && !defined (__TURBOC__)
+#if 0 /* No need for malloc.h, which pollutes the namespace;
+	 instead, just don't use alloca.  */
+#include <malloc.h>
+#endif
+#else /* not MSDOS, or __TURBOC__ */
+#if defined(_AIX)
+/* I don't know what this was needed for, but it pollutes the namespace.
+   So I turned it off.   rms, 2 May 1997.  */
+/* #include <malloc.h>  */
+ #pragma alloca
+#define YYSTACK_USE_ALLOCA
+#else /* not MSDOS, or __TURBOC__, or _AIX */
+#if 0
+#ifdef __hpux /* haible@ilog.fr says this works for HPUX 9.05 and up,
+		 and on HPUX 10.  Eventually we can turn this on.  */
+#define YYSTACK_USE_ALLOCA
+#define alloca __builtin_alloca
+#endif /* __hpux */
+#endif
+#endif /* not _AIX */
+#endif /* not MSDOS, or __TURBOC__ */
+#endif /* not sparc */
+#endif /* not GNU C */
+#endif /* alloca not defined */
+#endif /* YYSTACK_USE_ALLOCA not defined */
+
+#ifdef YYSTACK_USE_ALLOCA
+#define YYSTACK_ALLOC alloca
+#else
+#define YYSTACK_ALLOC malloc
+#endif
+
+/* Note: there must be only one dollar sign in this file.
+   It is replaced by the list of actions, each action
+   as one case of the switch.  */
+
+#define yyerrok		(yyerrstatus = 0)
+#define yyclearin	(yychar = YYEMPTY)
+#define YYEMPTY		-2
+#define YYEOF		0
+#define YYACCEPT	goto yyacceptlab
+#define YYABORT 	goto yyabortlab
+#define YYERROR		goto yyerrlab1
+/* Like YYERROR except do call yyerror.
+   This remains here temporarily to ease the
+   transition to the new meaning of YYERROR, for GCC.
+   Once GCC version 2 has supplanted version 1, this can go.  */
+#define YYFAIL		goto yyerrlab
+#define YYRECOVERING()  (!!yyerrstatus)
+#define YYBACKUP(token, value) \
+do								\
+  if (yychar == YYEMPTY && yylen == 1)				\
+    { yychar = (token), yylval = (value);			\
+      yychar1 = YYTRANSLATE (yychar);				\
+      YYPOPSTACK;						\
+      goto yybackup;						\
+    }								\
+  else								\
+    { yyerror ("syntax error: cannot back up"); YYERROR; }	\
+while (0)
+
+#define YYTERROR	1
+#define YYERRCODE	256
+
+#ifndef YYPURE
+#define YYLEX		yylex()
+#endif
+
+#ifdef YYPURE
+#ifdef YYLSP_NEEDED
+#ifdef YYLEX_PARAM
+#define YYLEX		yylex(&yylval, &yylloc, YYLEX_PARAM)
+#else
+#define YYLEX		yylex(&yylval, &yylloc)
+#endif
+#else /* not YYLSP_NEEDED */
+#ifdef YYLEX_PARAM
+#define YYLEX		yylex(&yylval, YYLEX_PARAM)
+#else
+#define YYLEX		yylex(&yylval)
+#endif
+#endif /* not YYLSP_NEEDED */
+#endif
+
+/* If nonreentrant, generate the variables here */
+
+#ifndef YYPURE
+
+int	yychar;			/*  the lookahead symbol		*/
+YYSTYPE	yylval;			/*  the semantic value of the		*/
+				/*  lookahead symbol			*/
+
+#ifdef YYLSP_NEEDED
+YYLTYPE yylloc;			/*  location data for the lookahead	*/
+				/*  symbol				*/
+#endif
+
+int yynerrs;			/*  number of parse errors so far       */
+#endif  /* not YYPURE */
+
+#if YYDEBUG != 0
+int yydebug;			/*  nonzero means print parse trace	*/
+/* Since this is uninitialized, it does not stop multiple parsers
+   from coexisting.  */
+#endif
+
+/*  YYINITDEPTH indicates the initial size of the parser's stacks	*/
+
+#ifndef	YYINITDEPTH
+#define YYINITDEPTH 200
+#endif
+
+/*  YYMAXDEPTH is the maximum size the stacks can grow to
+    (effective only if the built-in stack extension method is used).  */
+
+#if YYMAXDEPTH == 0
+#undef YYMAXDEPTH
+#endif
+
+#ifndef YYMAXDEPTH
+#define YYMAXDEPTH 10000
+#endif
+
+/* Define __yy_memcpy.  Note that the size argument
+   should be passed with type unsigned int, because that is what the non-GCC
+   definitions require.  With GCC, __builtin_memcpy takes an arg
+   of type size_t, but it can handle unsigned int.  */
+
+#if __GNUC__ > 1		/* GNU C and GNU C++ define this.  */
+#define __yy_memcpy(TO,FROM,COUNT)	__builtin_memcpy(TO,FROM,COUNT)
+#else				/* not GNU C or C++ */
+#ifndef __cplusplus
+
+/* This is the most reliable way to avoid incompatibilities
+   in available built-in functions on various systems.  */
+static void
+__yy_memcpy (to, from, count)
+     char *to;
+     char *from;
+     unsigned int count;
+{
+  register char *f = from;
+  register char *t = to;
+  register int i = count;
+
+  while (i-- > 0)
+    *t++ = *f++;
+}
+
+#else /* __cplusplus */
+
+/* This is the most reliable way to avoid incompatibilities
+   in available built-in functions on various systems.  */
+static void
+__yy_memcpy (char *to, char *from, unsigned int count)
+{
+  register char *t = to;
+  register char *f = from;
+  register int i = count;
+
+  while (i-- > 0)
+    *t++ = *f++;
+}
+
+#endif
+#endif
+
+#line 217 "/usr/share/bison.simple"
+
+/* The user can define YYPARSE_PARAM as the name of an argument to be passed
+   into yyparse.  The argument should have type void *.
+   It should actually point to an object.
+   Grammar actions can access the variable by casting it
+   to the proper pointer type.  */
+
+#ifdef YYPARSE_PARAM
+#ifdef __cplusplus
+#define YYPARSE_PARAM_ARG void *YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL
+#else /* not __cplusplus */
+#define YYPARSE_PARAM_ARG YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL void *YYPARSE_PARAM;
+#endif /* not __cplusplus */
+#else /* not YYPARSE_PARAM */
+#define YYPARSE_PARAM_ARG
+#define YYPARSE_PARAM_DECL
+#endif /* not YYPARSE_PARAM */
+
+/* Prevent warning if -Wstrict-prototypes.  */
+#ifdef __GNUC__
+#ifdef YYPARSE_PARAM
+int yyparse (void *);
+#else
+int yyparse (void);
+#endif
+#endif
+
+int
+yyparse(YYPARSE_PARAM_ARG)
+     YYPARSE_PARAM_DECL
+{
+  register int yystate;
+  register int yyn;
+  register short *yyssp;
+  register YYSTYPE *yyvsp;
+  int yyerrstatus;	/*  number of tokens to shift before error messages enabled */
+  int yychar1 = 0;		/*  lookahead token as an internal (translated) token number */
+
+  short	yyssa[YYINITDEPTH];	/*  the state stack			*/
+  YYSTYPE yyvsa[YYINITDEPTH];	/*  the semantic value stack		*/
+
+  short *yyss = yyssa;		/*  refer to the stacks thru separate pointers */
+  YYSTYPE *yyvs = yyvsa;	/*  to allow yyoverflow to reallocate them elsewhere */
+
+#ifdef YYLSP_NEEDED
+  YYLTYPE yylsa[YYINITDEPTH];	/*  the location stack			*/
+  YYLTYPE *yyls = yylsa;
+  YYLTYPE *yylsp;
+
+#define YYPOPSTACK   (yyvsp--, yyssp--, yylsp--)
+#else
+#define YYPOPSTACK   (yyvsp--, yyssp--)
+#endif
+
+  int yystacksize = YYINITDEPTH;
+  int yyfree_stacks = 0;
+
+#ifdef YYPURE
+  int yychar;
+  YYSTYPE yylval;
+  int yynerrs;
+#ifdef YYLSP_NEEDED
+  YYLTYPE yylloc;
+#endif
+#endif
+
+  YYSTYPE yyval;		/*  the variable used to return		*/
+				/*  semantic values from the action	*/
+				/*  routines				*/
+
+  int yylen;
+
+#if YYDEBUG != 0
+  if (yydebug)
+    fprintf(stderr, "Starting parse\n");
+#endif
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY;		/* Cause a token to be read.  */
+
+  /* Initialize stack pointers.
+     Waste one element of value and location stack
+     so that they stay on the same level as the state stack.
+     The wasted elements are never initialized.  */
+
+  yyssp = yyss - 1;
+  yyvsp = yyvs;
+#ifdef YYLSP_NEEDED
+  yylsp = yyls;
+#endif
+
+/* Push a new state, which is found in  yystate  .  */
+/* In all cases, when you get here, the value and location stacks
+   have just been pushed. so pushing a state here evens the stacks.  */
+yynewstate:
+
+  *++yyssp = yystate;
+
+  if (yyssp >= yyss + yystacksize - 1)
+    {
+      /* Give user a chance to reallocate the stack */
+      /* Use copies of these so that the &'s don't force the real ones into memory. */
+      YYSTYPE *yyvs1 = yyvs;
+      short *yyss1 = yyss;
+#ifdef YYLSP_NEEDED
+      YYLTYPE *yyls1 = yyls;
+#endif
+
+      /* Get the current used size of the three stacks, in elements.  */
+      int size = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      /* Each stack pointer address is followed by the size of
+	 the data in use in that stack, in bytes.  */
+#ifdef YYLSP_NEEDED
+      /* This used to be a conditional around just the two extra args,
+	 but that might be undefined if yyoverflow is a macro.  */
+      yyoverflow("parser stack overflow",
+		 &yyss1, size * sizeof (*yyssp),
+		 &yyvs1, size * sizeof (*yyvsp),
+		 &yyls1, size * sizeof (*yylsp),
+		 &yystacksize);
+#else
+      yyoverflow("parser stack overflow",
+		 &yyss1, size * sizeof (*yyssp),
+		 &yyvs1, size * sizeof (*yyvsp),
+		 &yystacksize);
+#endif
+
+      yyss = yyss1; yyvs = yyvs1;
+#ifdef YYLSP_NEEDED
+      yyls = yyls1;
+#endif
+#else /* no yyoverflow */
+      /* Extend the stack our own way.  */
+      if (yystacksize >= YYMAXDEPTH)
+	{
+	  yyerror("parser stack overflow");
+	  if (yyfree_stacks)
+	    {
+	      free (yyss);
+	      free (yyvs);
+#ifdef YYLSP_NEEDED
+	      free (yyls);
+#endif
+	    }
+	  return 2;
+	}
+      yystacksize *= 2;
+      if (yystacksize > YYMAXDEPTH)
+	yystacksize = YYMAXDEPTH;
+#ifndef YYSTACK_USE_ALLOCA
+      yyfree_stacks = 1;
+#endif
+      yyss = (short *) YYSTACK_ALLOC (yystacksize * sizeof (*yyssp));
+      __yy_memcpy ((char *)yyss, (char *)yyss1,
+		   size * (unsigned int) sizeof (*yyssp));
+      yyvs = (YYSTYPE *) YYSTACK_ALLOC (yystacksize * sizeof (*yyvsp));
+      __yy_memcpy ((char *)yyvs, (char *)yyvs1,
+		   size * (unsigned int) sizeof (*yyvsp));
+#ifdef YYLSP_NEEDED
+      yyls = (YYLTYPE *) YYSTACK_ALLOC (yystacksize * sizeof (*yylsp));
+      __yy_memcpy ((char *)yyls, (char *)yyls1,
+		   size * (unsigned int) sizeof (*yylsp));
+#endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + size - 1;
+      yyvsp = yyvs + size - 1;
+#ifdef YYLSP_NEEDED
+      yylsp = yyls + size - 1;
+#endif
+
+#if YYDEBUG != 0
+      if (yydebug)
+	fprintf(stderr, "Stack size increased to %d\n", yystacksize);
+#endif
+
+      if (yyssp >= yyss + yystacksize - 1)
+	YYABORT;
+    }
+
+#if YYDEBUG != 0
+  if (yydebug)
+    fprintf(stderr, "Entering state %d\n", yystate);
+#endif
+
+  goto yybackup;
+ yybackup:
+
+/* Do appropriate processing given the current state.  */
+/* Read a lookahead token if we need one and don't already have one.  */
+/* yyresume: */
+
+  /* First try to decide what to do without reference to lookahead token.  */
+
+  yyn = yypact[yystate];
+  if (yyn == YYFLAG)
+    goto yydefault;
+
+  /* Not known => get a lookahead token if don't already have one.  */
+
+  /* yychar is either YYEMPTY or YYEOF
+     or a valid token in external form.  */
+
+  if (yychar == YYEMPTY)
+    {
+#if YYDEBUG != 0
+      if (yydebug)
+	fprintf(stderr, "Reading a token: ");
+#endif
+      yychar = YYLEX;
+    }
+
+  /* Convert token to internal form (in yychar1) for indexing tables with */
+
+  if (yychar <= 0)		/* This means end of input. */
+    {
+      yychar1 = 0;
+      yychar = YYEOF;		/* Don't call YYLEX any more */
+
+#if YYDEBUG != 0
+      if (yydebug)
+	fprintf(stderr, "Now at end of input.\n");
+#endif
+    }
+  else
+    {
+      yychar1 = YYTRANSLATE(yychar);
+
+#if YYDEBUG != 0
+      if (yydebug)
+	{
+	  fprintf (stderr, "Next token is %d (%s", yychar, yytname[yychar1]);
+	  /* Give the individual parser a way to print the precise meaning
+	     of a token, for further debugging info.  */
+#ifdef YYPRINT
+	  YYPRINT (stderr, yychar, yylval);
+#endif
+	  fprintf (stderr, ")\n");
+	}
+#endif
+    }
+
+  yyn += yychar1;
+  if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1)
+    goto yydefault;
+
+  yyn = yytable[yyn];
+
+  /* yyn is what to do for this token type in this state.
+     Negative => reduce, -yyn is rule number.
+     Positive => shift, yyn is new state.
+       New state is final state => don't bother to shift,
+       just return success.
+     0, or most negative number => error.  */
+
+  if (yyn < 0)
+    {
+      if (yyn == YYFLAG)
+	goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+  else if (yyn == 0)
+    goto yyerrlab;
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  /* Shift the lookahead token.  */
+
+#if YYDEBUG != 0
+  if (yydebug)
+    fprintf(stderr, "Shifting token %d (%s), ", yychar, yytname[yychar1]);
+#endif
+
+  /* Discard the token being shifted unless it is eof.  */
+  if (yychar != YYEOF)
+    yychar = YYEMPTY;
+
+  *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+  *++yylsp = yylloc;
+#endif
+
+  /* count tokens shifted since error; after three, turn off error status.  */
+  if (yyerrstatus) yyerrstatus--;
+
+  yystate = yyn;
+  goto yynewstate;
+
+/* Do the default action for the current state.  */
+yydefault:
+
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+
+/* Do a reduction.  yyn is the number of a rule to reduce with.  */
+yyreduce:
+  yylen = yyr2[yyn];
+  if (yylen > 0)
+    yyval = yyvsp[1-yylen]; /* implement default value of the action */
+
+#if YYDEBUG != 0
+  if (yydebug)
+    {
+      int i;
+
+      fprintf (stderr, "Reducing via rule %d (line %d), ",
+	       yyn, yyrline[yyn]);
+
+      /* Print the symbols being reduced, and their result.  */
+      for (i = yyprhs[yyn]; yyrhs[i] > 0; i++)
+	fprintf (stderr, "%s ", yytname[yyrhs[i]]);
+      fprintf (stderr, " -> %s\n", yytname[yyr1[yyn]]);
+    }
+#endif
+
+
+  switch (yyn) {
+
+case 1:
+#line 71 "parser.y"
+{
+  matheval_root = yyvsp[-1].node;
+  return 1;
+;
+    break;}
+case 2:
+#line 78 "parser.y"
+{
+  yyval.node = yyvsp[0].node;
+;
+    break;}
+case 3:
+#line 81 "parser.y"
+{
+  yyval.node = yyvsp[0].node;
+;
+    break;}
+case 4:
+#line 84 "parser.y"
+{
+  /* Create addition binary operator node.  */
+  yyval.node = node_create ('b', '+', yyvsp[-2].node, yyvsp[0].node);
+;
+    break;}
+case 5:
+#line 88 "parser.y"
+{
+  /* Create subtraction binary operator node.  */
+  yyval.node = node_create ('b', '-', yyvsp[-2].node, yyvsp[0].node);
+;
+    break;}
+case 6:
+#line 92 "parser.y"
+{
+  /* Create multiplication binary operator node.  */
+  yyval.node = node_create ('b', '*', yyvsp[-2].node, yyvsp[0].node);
+;
+    break;}
+case 7:
+#line 96 "parser.y"
+{
+  /* Create division binary operator node.  */
+  yyval.node = node_create ('b', '/', yyvsp[-2].node, yyvsp[0].node);
+;
+    break;}
+case 8:
+#line 100 "parser.y"
+{
+  /* Create minus unary operator node.  */
+  yyval.node = node_create ('u', '-', yyvsp[0].node);
+;
+    break;}
+case 9:
+#line 104 "parser.y"
+{
+  /* Create exponentiation unary operator node.  */
+  yyval.node = node_create ('b', '^', yyvsp[-2].node, yyvsp[0].node);
+;
+    break;}
+case 10:
+#line 108 "parser.y"
+{
+  /* Create function node.  */
+  yyval.node = node_create ('f', yyvsp[-3].record, yyvsp[-1].node);
+;
+    break;}
+case 11:
+#line 112 "parser.y"
+{
+  yyval.node = yyvsp[-1].node;
+;
+    break;}
+}
+   /* the action file gets copied in in place of this dollarsign */
+#line 543 "/usr/share/bison.simple"
+
+  yyvsp -= yylen;
+  yyssp -= yylen;
+#ifdef YYLSP_NEEDED
+  yylsp -= yylen;
+#endif
+
+#if YYDEBUG != 0
+  if (yydebug)
+    {
+      short *ssp1 = yyss - 1;
+      fprintf (stderr, "state stack now");
+      while (ssp1 != yyssp)
+	fprintf (stderr, " %d", *++ssp1);
+      fprintf (stderr, "\n");
+    }
+#endif
+
+  *++yyvsp = yyval;
+
+#ifdef YYLSP_NEEDED
+  yylsp++;
+  if (yylen == 0)
+    {
+      yylsp->first_line = yylloc.first_line;
+      yylsp->first_column = yylloc.first_column;
+      yylsp->last_line = (yylsp-1)->last_line;
+      yylsp->last_column = (yylsp-1)->last_column;
+      yylsp->text = 0;
+    }
+  else
+    {
+      yylsp->last_line = (yylsp+yylen-1)->last_line;
+      yylsp->last_column = (yylsp+yylen-1)->last_column;
+    }
+#endif
+
+  /* Now "shift" the result of the reduction.
+     Determine what state that goes to,
+     based on the state we popped back to
+     and the rule number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
+  if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTBASE];
+
+  goto yynewstate;
+
+yyerrlab:   /* here on detecting error */
+
+  if (! yyerrstatus)
+    /* If not already recovering from an error, report this error.  */
+    {
+      ++yynerrs;
+
+#ifdef YYERROR_VERBOSE
+      yyn = yypact[yystate];
+
+      if (yyn > YYFLAG && yyn < YYLAST)
+	{
+	  int size = 0;
+	  char *msg;
+	  int x, count;
+
+	  count = 0;
+	  /* Start X at -yyn if nec to avoid negative indexes in yycheck.  */
+	  for (x = (yyn < 0 ? -yyn : 0);
+	       x < (sizeof(yytname) / sizeof(char *)); x++)
+	    if (yycheck[x + yyn] == x)
+	      size += strlen(yytname[x]) + 15, count++;
+	  msg = (char *) malloc(size + 15);
+	  if (msg != 0)
+	    {
+	      strcpy(msg, "parse error");
+
+	      if (count < 5)
+		{
+		  count = 0;
+		  for (x = (yyn < 0 ? -yyn : 0);
+		       x < (sizeof(yytname) / sizeof(char *)); x++)
+		    if (yycheck[x + yyn] == x)
+		      {
+			strcat(msg, count == 0 ? ", expecting `" : " or `");
+			strcat(msg, yytname[x]);
+			strcat(msg, "'");
+			count++;
+		      }
+		}
+	      yyerror(msg);
+	      free(msg);
+	    }
+	  else
+	    yyerror ("parse error; also virtual memory exceeded");
+	}
+      else
+#endif /* YYERROR_VERBOSE */
+	yyerror("parse error");
+    }
+
+  goto yyerrlab1;
+yyerrlab1:   /* here on error raised explicitly by an action */
+
+  if (yyerrstatus == 3)
+    {
+      /* if just tried and failed to reuse lookahead token after an error, discard it.  */
+
+      /* return failure if at end of input */
+      if (yychar == YYEOF)
+	YYABORT;
+
+#if YYDEBUG != 0
+      if (yydebug)
+	fprintf(stderr, "Discarding token %d (%s).\n", yychar, yytname[yychar1]);
+#endif
+
+      yychar = YYEMPTY;
+    }
+
+  /* Else will try to reuse lookahead token
+     after shifting the error token.  */
+
+  yyerrstatus = 3;		/* Each real token shifted decrements this */
+
+  goto yyerrhandle;
+
+yyerrdefault:  /* current state does not do anything special for the error token. */
+
+#if 0
+  /* This is wrong; only states that explicitly want error tokens
+     should shift them.  */
+  yyn = yydefact[yystate];  /* If its default is to accept any token, ok.  Otherwise pop it.*/
+  if (yyn) goto yydefault;
+#endif
+
+yyerrpop:   /* pop the current state because it cannot handle the error token */
+
+  if (yyssp == yyss) YYABORT;
+  yyvsp--;
+  yystate = *--yyssp;
+#ifdef YYLSP_NEEDED
+  yylsp--;
+#endif
+
+#if YYDEBUG != 0
+  if (yydebug)
+    {
+      short *ssp1 = yyss - 1;
+      fprintf (stderr, "Error: state stack now");
+      while (ssp1 != yyssp)
+	fprintf (stderr, " %d", *++ssp1);
+      fprintf (stderr, "\n");
+    }
+#endif
+
+yyerrhandle:
+
+  yyn = yypact[yystate];
+  if (yyn == YYFLAG)
+    goto yyerrdefault;
+
+  yyn += YYTERROR;
+  if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR)
+    goto yyerrdefault;
+
+  yyn = yytable[yyn];
+  if (yyn < 0)
+    {
+      if (yyn == YYFLAG)
+	goto yyerrpop;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+  else if (yyn == 0)
+    goto yyerrpop;
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+#if YYDEBUG != 0
+  if (yydebug)
+    fprintf(stderr, "Shifting error token, ");
+#endif
+
+  *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+  *++yylsp = yylloc;
+#endif
+
+  yystate = yyn;
+  goto yynewstate;
+
+ yyacceptlab:
+  /* YYACCEPT comes here.  */
+  if (yyfree_stacks)
+    {
+      free (yyss);
+      free (yyvs);
+#ifdef YYLSP_NEEDED
+      free (yyls);
+#endif
+    }
+  return 0;
+
+ yyabortlab:
+  /* YYABORT comes here.  */
+  if (yyfree_stacks)
+    {
+      free (yyss);
+      free (yyvs);
+#ifdef YYLSP_NEEDED
+      free (yyls);
+#endif
+    }
+  return 1;
+}
+#line 117 "parser.y"
+
+
+int yyerror(char* s)
+{
+  /* Indicate parsing error through appropriate flag and stop
+     parsing.  */
+  matheval_ok = 0;
+  force_buffer_flush();
+  return 0;
+}
diff --git a/contrib/MathEval/parser.tab.hpp b/contrib/MathEval/parser.tab.hpp
new file mode 100644
index 0000000000..561b245ad9
--- /dev/null
+++ b/contrib/MathEval/parser.tab.hpp
@@ -0,0 +1,11 @@
+typedef union {
+  Node *node;
+  Record *record;
+} YYSTYPE;
+#define	NUMBER	257
+#define	VARIABLE	258
+#define	FUNCTION	259
+#define	NEG	260
+
+
+extern YYSTYPE melval;
diff --git a/contrib/MathEval/parser.y b/contrib/MathEval/parser.y
new file mode 100644
index 0000000000..9b5817bf81
--- /dev/null
+++ b/contrib/MathEval/parser.y
@@ -0,0 +1,126 @@
+%{
+/*
+ * Copyright (C) 1999, 2002, 2003  Free Software Foundation, Inc.
+ * 
+ * This file is part of GNU libmatheval
+ *
+ * GNU libmatheval 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, or (at your option)
+ * any later version.
+ *
+ * GNU libmatheval 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 program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/* This file was modified for inclusion in Gmsh */
+
+#include "common.h"
+#include "node.h"
+
+/* Variables used to communicate with code using parser.  */
+extern Node* matheval_root; /* Root of tree representation of function.  */
+extern int matheval_ok; /* Flag representing success of parsing.  */
+
+/* Report parsing error.  */
+int yyerror (char *s);
+
+/* Function used to tokenize string representing function (this function
+   is generated by scanner generator).  */
+int yylex (void);
+
+/* Function used to flush the internal flex buffer when we exit
+   prematurely (i.e., in case of a parse error) so that the next time
+   we call the scanner, all is nicely reset. Without this, the
+   behaviour of the next call after an error is unpredictable.  */
+void force_buffer_flush(void);
+
+%}
+
+/* Parser semantic values type.  */
+%union {
+  Node *node;
+  Record *record;
+}
+
+/* Grammar terminal symbols.  */
+%token <node> NUMBER VARIABLE
+%token <record> FUNCTION
+
+/* Grammar non-terminal symbols.  */
+%type <node> expression
+
+%left '-' '+'
+%left '*' '/'
+%left NEG
+%left '^'
+
+/* Grammar start non-terminal.  */
+%start input
+
+%%
+
+input
+: expression '\n' {
+  matheval_root = $1;
+  return 1;
+}
+;
+
+expression
+: NUMBER {
+  $$ = $1;
+}
+| VARIABLE {
+  $$ = $1;
+}
+| expression '+' expression {
+  /* Create addition binary operator node.  */
+  $$ = node_create ('b', '+', $1, $3);
+}
+| expression '-' expression {
+  /* Create subtraction binary operator node.  */
+  $$ = node_create ('b', '-', $1, $3);
+}
+| expression '*' expression {
+  /* Create multiplication binary operator node.  */
+  $$ = node_create ('b', '*', $1, $3);
+}
+| expression '/' expression {
+  /* Create division binary operator node.  */
+  $$ = node_create ('b', '/', $1, $3);
+}
+| '-' expression %prec NEG {
+  /* Create minus unary operator node.  */
+  $$ = node_create ('u', '-', $2);
+}
+| expression '^' expression {
+  /* Create exponentiation unary operator node.  */
+  $$ = node_create ('b', '^', $1, $3);
+}
+| FUNCTION '(' expression ')' {
+  /* Create function node.  */
+  $$ = node_create ('f', $1, $3);
+}
+| '(' expression ')' {
+  $$ = $2;
+}
+;
+
+%%
+
+int yyerror(char* s)
+{
+  /* Indicate parsing error through appropriate flag and stop
+     parsing.  */
+  matheval_ok = 0;
+  force_buffer_flush();
+  return 0;
+}
diff --git a/contrib/MathEval/scanner.l b/contrib/MathEval/scanner.l
new file mode 100644
index 0000000000..0b92fa78c8
--- /dev/null
+++ b/contrib/MathEval/scanner.l
@@ -0,0 +1,130 @@
+%{
+/*
+ * Copyright (C) 1999, 2002, 2003  Free Software Foundation, Inc.
+ * 
+ * This file is part of GNU libmatheval
+ *
+ * GNU libmatheval 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, or (at your option)
+ * any later version.
+ *
+ * GNU libmatheval 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 program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/* This file was modified for inclusion in Gmsh */
+
+#include "common.h"
+#include "node.h"
+#include "parser.tab.hpp"
+#include "symbol_table.h"
+
+#define YY_ALWAYS_INTERACTIVE 1
+
+/* Redefine macro to redirect scanner input from string instead of
+   standard input.  */
+#define YY_INPUT( buffer, result, max_size ) \
+{ result = input_from_string (buffer, max_size); }
+
+/* Variables used to communicate with code using scanner.  */
+extern SymbolTable *matheval_symbol_table; /* Evaluator symbol table.  */
+extern char *matheval_input_string; /* String representing function.  */
+
+/* Read next max_size character from string into buffer.  */
+static int input_from_string (char *buffer, int max_size);
+%}
+
+/* Token definitions.  */
+whitespace [\ \t]+
+digit [0-9]
+number ({digit}+|{digit}+"."{digit}*|{digit}*"."{digit}+)([Ee][-+]?{digit}+)?
+function "Exp"|"Log"|"Sqrt"|"Sin"|"Cos"|"Tan"|"Ctan"|"Asin"|"Acos"|"Atan"|"Actan"|"Sinh"|"Cosh"|"Tanh"|"Ctanh"|"Asinh"|"Acosh"|"Atanh"|"Actanh"|"Fabs"|"Rand"
+identifier [a-zA-Z\_][a-zA-Z0-9\_]*
+
+%%
+
+{whitespace}
+
+{number} {
+  /* Create node representing constant with appropriate value.  */
+  melval.node = node_create ('c', atof (metext));
+  return NUMBER;
+}
+
+{function} {
+  /* Find symbol table record corresponding to function name.  */
+  melval.record = symbol_table_lookup (matheval_symbol_table, metext);
+  return FUNCTION;
+}
+
+{identifier} {
+  Record *record; /* Symbol table record.  */
+  /* Inserty variable into symbol table.  */
+  record = symbol_table_insert (matheval_symbol_table, metext, 'v');
+  melval.node = node_create ('v', record);
+  return VARIABLE;
+}
+
+"+" {
+  return '+';
+}
+
+"-" {
+  return '-';
+}
+
+"*" {
+  return '*';
+}
+
+"/" {
+  return '/';
+}
+
+"^" {
+  return '^';
+}
+
+"(" {
+  return '(';
+}
+
+")" {
+  return ')';
+}
+
+"\n" {
+  return '\n';
+}
+
+%%
+
+#undef mewrap
+
+int mewrap() { return 1; }
+
+static int input_from_string (char *buffer, int max_size)
+{
+  int count; /* Count of characters to copy from input string to buffer.  */
+
+  /* Calculate count of characters to copy.  */
+  count = strlen (matheval_input_string);
+  if (count > max_size)
+    count = max_size;
+
+  /* Perform copy operation and update input string.  */
+  memcpy(buffer, matheval_input_string, count);
+  matheval_input_string += count;
+
+  return count;
+}
+
+void force_buffer_flush() { YY_FLUSH_BUFFER; }
diff --git a/contrib/MathEval/scanner.yy.cpp b/contrib/MathEval/scanner.yy.cpp
new file mode 100644
index 0000000000..f23bb52aa5
--- /dev/null
+++ b/contrib/MathEval/scanner.yy.cpp
@@ -0,0 +1,1716 @@
+#define yy_create_buffer me_create_buffer
+#define yy_delete_buffer me_delete_buffer
+#define yy_scan_buffer me_scan_buffer
+#define yy_scan_string me_scan_string
+#define yy_scan_bytes me_scan_bytes
+#define yy_flex_debug me_flex_debug
+#define yy_init_buffer me_init_buffer
+#define yy_flush_buffer me_flush_buffer
+#define yy_load_buffer_state me_load_buffer_state
+#define yy_switch_to_buffer me_switch_to_buffer
+#define yyin mein
+#define yyleng meleng
+#define yylex melex
+#define yyout meout
+#define yyrestart merestart
+#define yytext metext
+#define yywrap mewrap
+
+#line 20 "scanner.yy.cpp"
+/* A lexical scanner generated by flex */
+
+/* Scanner skeleton version:
+ * $Header: /cvsroot/gmsh/contrib/MathEval/scanner.yy.cpp,v 1.1 2005-09-21 17:29:37 geuzaine Exp $
+ */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+
+#include <stdio.h>
+
+
+/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */
+#ifdef c_plusplus
+#ifndef __cplusplus
+#define __cplusplus
+#endif
+#endif
+
+
+#ifdef __cplusplus
+
+#include <stdlib.h>
+#include <unistd.h>
+
+/* Use prototypes in function declarations. */
+#define YY_USE_PROTOS
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else	/* ! __cplusplus */
+
+#if __STDC__
+
+#define YY_USE_PROTOS
+#define YY_USE_CONST
+
+#endif	/* __STDC__ */
+#endif	/* ! __cplusplus */
+
+#ifdef __TURBOC__
+ #pragma warn -rch
+ #pragma warn -use
+#include <io.h>
+#include <stdlib.h>
+#define YY_USE_CONST
+#define YY_USE_PROTOS
+#endif
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+
+#ifdef YY_USE_PROTOS
+#define YY_PROTO(proto) proto
+#else
+#define YY_PROTO(proto) ()
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index.  If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition.  This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yy_start = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state.  The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yy_start - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart( yyin )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#define YY_BUF_SIZE 16384
+
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+
+extern int yyleng;
+extern FILE *yyin, *yyout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+/* The funky do-while in the following #define is used to turn the definition
+ * int a single C statement (which needs a semi-colon terminator).  This
+ * avoids problems with code like:
+ *
+ * 	if ( condition_holds )
+ *		yyless( 5 );
+ *	else
+ *		do_something_else();
+ *
+ * Prior to using the do-while the compiler would get upset at the
+ * "else" because it interpreted the "if" statement as being all
+ * done when it reached the ';' after the yyless() call.
+ */
+
+/* Return all but the first 'n' matched characters back to the input stream. */
+
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+		*yy_cp = yy_hold_char; \
+		YY_RESTORE_YY_MORE_OFFSET \
+		yy_c_buf_p = yy_cp = yy_bp + n - YY_MORE_ADJ; \
+		YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+		} \
+	while ( 0 )
+
+#define unput(c) yyunput( c, yytext_ptr )
+
+/* The following is because we cannot portably get our hands on size_t
+ * (without autoconf's help, which isn't available because we want
+ * flex-generated scanners to compile on their own).
+ */
+typedef unsigned int yy_size_t;
+
+
+struct yy_buffer_state
+	{
+	FILE *yy_input_file;
+
+	char *yy_ch_buf;		/* input buffer */
+	char *yy_buf_pos;		/* current position in input buffer */
+
+	/* Size of input buffer in bytes, not including room for EOB
+	 * characters.
+	 */
+	yy_size_t yy_buf_size;
+
+	/* Number of characters read into yy_ch_buf, not including EOB
+	 * characters.
+	 */
+	int yy_n_chars;
+
+	/* Whether we "own" the buffer - i.e., we know we created it,
+	 * and can realloc() it to grow it, and should free() it to
+	 * delete it.
+	 */
+	int yy_is_our_buffer;
+
+	/* Whether this is an "interactive" input source; if so, and
+	 * if we're using stdio for input, then we want to use getc()
+	 * instead of fread(), to make sure we stop fetching input after
+	 * each newline.
+	 */
+	int yy_is_interactive;
+
+	/* Whether we're considered to be at the beginning of a line.
+	 * If so, '^' rules will be active on the next match, otherwise
+	 * not.
+	 */
+	int yy_at_bol;
+
+	/* Whether to try to fill the input buffer when we reach the
+	 * end of it.
+	 */
+	int yy_fill_buffer;
+
+	int yy_buffer_status;
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+	/* When an EOF's been seen but there's still some text to process
+	 * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+	 * shouldn't try reading from the input source any more.  We might
+	 * still have a bunch of tokens to match, though, because of
+	 * possible backing-up.
+	 *
+	 * When we actually see the EOF, we change the status to "new"
+	 * (via yyrestart()), so that the user can continue scanning by
+	 * just pointing yyin at a new input file.
+	 */
+#define YY_BUFFER_EOF_PENDING 2
+	};
+
+static YY_BUFFER_STATE yy_current_buffer = 0;
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ */
+#define YY_CURRENT_BUFFER yy_current_buffer
+
+
+/* yy_hold_char holds the character lost when yytext is formed. */
+static char yy_hold_char;
+
+static int yy_n_chars;		/* number of characters read into yy_ch_buf */
+
+
+int yyleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 1;		/* whether we need to initialize */
+static int yy_start = 0;	/* start state number */
+
+/* Flag which is used to allow yywrap()'s to do buffer switches
+ * instead of setting up a fresh yyin.  A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void yyrestart YY_PROTO(( FILE *input_file ));
+
+void yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer ));
+void yy_load_buffer_state YY_PROTO(( void ));
+YY_BUFFER_STATE yy_create_buffer YY_PROTO(( FILE *file, int size ));
+void yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b ));
+void yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file ));
+void yy_flush_buffer YY_PROTO(( YY_BUFFER_STATE b ));
+#define YY_FLUSH_BUFFER yy_flush_buffer( yy_current_buffer )
+
+YY_BUFFER_STATE yy_scan_buffer YY_PROTO(( char *base, yy_size_t size ));
+YY_BUFFER_STATE yy_scan_string YY_PROTO(( yyconst char *yy_str ));
+YY_BUFFER_STATE yy_scan_bytes YY_PROTO(( yyconst char *bytes, int len ));
+
+static void *yy_flex_alloc YY_PROTO(( yy_size_t ));
+static void *yy_flex_realloc YY_PROTO(( void *, yy_size_t ));
+static void yy_flex_free YY_PROTO(( void * ));
+
+#define yy_new_buffer yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+	{ \
+	if ( ! yy_current_buffer ) \
+		yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
+	yy_current_buffer->yy_is_interactive = is_interactive; \
+	}
+
+#define yy_set_bol(at_bol) \
+	{ \
+	if ( ! yy_current_buffer ) \
+		yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
+	yy_current_buffer->yy_at_bol = at_bol; \
+	}
+
+#define YY_AT_BOL() (yy_current_buffer->yy_at_bol)
+
+typedef unsigned char YY_CHAR;
+FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0;
+typedef int yy_state_type;
+extern char *yytext;
+#define yytext_ptr yytext
+
+static yy_state_type yy_get_previous_state YY_PROTO(( void ));
+static yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state ));
+static int yy_get_next_buffer YY_PROTO(( void ));
+static void yy_fatal_error YY_PROTO(( yyconst char msg[] ));
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+	yytext_ptr = yy_bp; \
+	yyleng = (int) (yy_cp - yy_bp); \
+	yy_hold_char = *yy_cp; \
+	*yy_cp = '\0'; \
+	yy_c_buf_p = yy_cp;
+
+#define YY_NUM_RULES 13
+#define YY_END_OF_BUFFER 14
+static yyconst short int yy_accept[65] =
+    {   0,
+        0,    0,   14,   13,    1,   12,   10,   11,    7,    5,
+        6,   13,    8,    2,    4,    4,    4,    4,    4,    4,
+        4,    4,    4,    9,    1,    2,    2,    2,    0,    4,
+        4,    4,    4,    4,    4,    4,    4,    4,    4,    4,
+        4,    4,    2,    0,    2,    4,    4,    4,    4,    3,
+        4,    3,    4,    4,    3,    4,    3,    3,    4,    3,
+        3,    3,    3,    0
+    } ;
+
+static yyconst int yy_ec[256] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    2,    3,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    2,    1,    1,    1,    1,    1,    1,    1,    4,
+        5,    6,    7,    1,    8,    9,   10,   11,   11,   11,
+       11,   11,   11,   11,   11,   11,   11,    1,    1,    1,
+        1,    1,    1,    1,   12,   13,   14,   13,   15,   16,
+       13,   13,   13,   13,   13,   17,   13,   13,   13,   13,
+       13,   18,   19,   20,   13,   13,   13,   13,   13,   13,
+        1,    1,    1,   21,   13,    1,   22,   23,   24,   25,
+
+       26,   13,   27,   28,   29,   13,   13,   13,   13,   30,
+       31,   32,   33,   34,   35,   36,   13,   13,   13,   37,
+       13,   13,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1
+    } ;
+
+static yyconst int yy_meta[38] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        2,    2,    2,    2,    2,    2,    2,    2,    2,    2,
+        1,    2,    2,    2,    2,    2,    2,    2,    2,    2,
+        2,    2,    2,    2,    2,    2,    2
+    } ;
+
+static yyconst short int yy_base[66] =
+    {   0,
+        0,    0,  106,  107,  103,  107,  107,  107,  107,  107,
+      107,   93,  107,   29,   15,    0,   10,   66,   80,   70,
+       78,   14,   77,  107,   96,   34,   37,   47,   46,    0,
+       28,   68,   74,   60,   72,   61,   69,   64,   60,   59,
+       54,   57,   50,   75,   74,   49,   61,   52,   51,   52,
+       49,    0,   43,   52,   47,   38,   44,   43,   40,   41,
+       40,   39,   38,  107,   40
+    } ;
+
+static yyconst short int yy_def[66] =
+    {   0,
+       64,    1,   64,   64,   64,   64,   64,   64,   64,   64,
+       64,   64,   64,   64,   65,   65,   65,   65,   65,   65,
+       65,   65,   65,   64,   64,   64,   64,   64,   64,   65,
+       65,   65,   65,   65,   65,   65,   65,   65,   65,   65,
+       65,   65,   64,   64,   64,   65,   65,   65,   65,   65,
+       65,   65,   65,   65,   65,   65,   65,   65,   65,   65,
+       65,   65,   65,    0,   64
+    } ;
+
+static yyconst short int yy_nxt[145] =
+    {   0,
+        4,    5,    6,    7,    8,    9,   10,   11,   12,   13,
+       14,   15,   16,   17,   18,   19,   20,   21,   22,   23,
+       24,   16,   16,   16,   16,   16,   16,   16,   16,   16,
+       16,   16,   16,   16,   16,   16,   16,   27,   31,   28,
+       34,   30,   40,   29,   26,   35,   41,   43,   29,   32,
+       33,   29,   44,   44,   29,   27,   45,   28,   46,   29,
+       43,   29,   29,   47,   29,   52,   52,   52,   52,   63,
+       52,   52,   29,   52,   52,   29,   52,   52,   62,   52,
+       61,   60,   59,   58,   45,   45,   57,   56,   55,   54,
+       52,   53,   52,   51,   50,   49,   48,   25,   42,   39,
+
+       38,   37,   36,   26,   25,   64,    3,   64,   64,   64,
+       64,   64,   64,   64,   64,   64,   64,   64,   64,   64,
+       64,   64,   64,   64,   64,   64,   64,   64,   64,   64,
+       64,   64,   64,   64,   64,   64,   64,   64,   64,   64,
+       64,   64,   64,   64
+    } ;
+
+static yyconst short int yy_chk[145] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,   14,   15,   14,
+       17,   65,   22,   14,   26,   17,   22,   27,   26,   15,
+       15,   27,   29,   29,   14,   28,   29,   28,   31,   26,
+       43,   28,   27,   31,   43,   63,   62,   61,   60,   59,
+       58,   57,   28,   56,   55,   43,   54,   53,   51,   50,
+       49,   48,   47,   46,   45,   44,   42,   41,   40,   39,
+       38,   37,   36,   35,   34,   33,   32,   25,   23,   21,
+
+       20,   19,   18,   12,    5,    3,   64,   64,   64,   64,
+       64,   64,   64,   64,   64,   64,   64,   64,   64,   64,
+       64,   64,   64,   64,   64,   64,   64,   64,   64,   64,
+       64,   64,   64,   64,   64,   64,   64,   64,   64,   64,
+       64,   64,   64,   64
+    } ;
+
+static yy_state_type yy_last_accepting_state;
+static char *yy_last_accepting_cpos;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *yytext;
+#line 1 "scanner.l"
+#define INITIAL 0
+#line 2 "scanner.l"
+/*
+ * Copyright (C) 1999, 2002, 2003  Free Software Foundation, Inc.
+ * 
+ * This file is part of GNU libmatheval
+ *
+ * GNU libmatheval 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, or (at your option)
+ * any later version.
+ *
+ * GNU libmatheval 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 program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/* This file was modified for inclusion in Gmsh */
+
+#include "common.h"
+#include "node.h"
+#include "parser.tab.hpp"
+#include "symbol_table.h"
+
+#define YY_ALWAYS_INTERACTIVE 1
+
+/* Redefine macro to redirect scanner input from string instead of
+   standard input.  */
+#define YY_INPUT( buffer, result, max_size ) \
+{ result = input_from_string (buffer, max_size); }
+
+/* Variables used to communicate with code using scanner.  */
+extern SymbolTable *matheval_symbol_table; /* Evaluator symbol table.  */
+extern char *matheval_input_string; /* String representing function.  */
+
+/* Read next max_size character from string into buffer.  */
+static int input_from_string (char *buffer, int max_size);
+/* Token definitions.  */
+#line 476 "scanner.yy.cpp"
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap YY_PROTO(( void ));
+#else
+extern int yywrap YY_PROTO(( void ));
+#endif
+#endif
+
+#ifndef YY_NO_UNPUT
+static void yyunput YY_PROTO(( int c, char *buf_ptr ));
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy YY_PROTO(( char *, yyconst char *, int ));
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen YY_PROTO(( yyconst char * ));
+#endif
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+static int yyinput YY_PROTO(( void ));
+#else
+static int input YY_PROTO(( void ));
+#endif
+#endif
+
+#if YY_STACK_USED
+static int yy_start_stack_ptr = 0;
+static int yy_start_stack_depth = 0;
+static int *yy_start_stack = 0;
+#ifndef YY_NO_PUSH_STATE
+static void yy_push_state YY_PROTO(( int new_state ));
+#endif
+#ifndef YY_NO_POP_STATE
+static void yy_pop_state YY_PROTO(( void ));
+#endif
+#ifndef YY_NO_TOP_STATE
+static int yy_top_state YY_PROTO(( void ));
+#endif
+
+#else
+#define YY_NO_PUSH_STATE 1
+#define YY_NO_POP_STATE 1
+#define YY_NO_TOP_STATE 1
+#endif
+
+#ifdef YY_MALLOC_DECL
+YY_MALLOC_DECL
+#else
+#if __STDC__
+#ifndef __cplusplus
+#include <stdlib.h>
+#endif
+#else
+/* Just try to get by without declaring the routines.  This will fail
+ * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int)
+ * or sizeof(void*) != sizeof(int).
+ */
+#endif
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO (void) fwrite( yytext, yyleng, 1, yyout )
+#endif
+
+/* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+	if ( yy_current_buffer->yy_is_interactive ) \
+		{ \
+		int c = '*', n; \
+		for ( n = 0; n < max_size && \
+			     (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+			buf[n] = (char) c; \
+		if ( c == '\n' ) \
+			buf[n++] = (char) c; \
+		if ( c == EOF && ferror( yyin ) ) \
+			YY_FATAL_ERROR( "input in flex scanner failed" ); \
+		result = n; \
+		} \
+	else if ( ((result = fread( buf, 1, max_size, yyin )) == 0) \
+		  && ferror( yyin ) ) \
+		YY_FATAL_ERROR( "input in flex scanner failed" );
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL int yylex YY_PROTO(( void ))
+#endif
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+	YY_USER_ACTION
+
+YY_DECL
+	{
+	register yy_state_type yy_current_state;
+	register char *yy_cp, *yy_bp;
+	register int yy_act;
+
+#line 52 "scanner.l"
+
+
+#line 630 "scanner.yy.cpp"
+
+	if ( yy_init )
+		{
+		yy_init = 0;
+
+#ifdef YY_USER_INIT
+		YY_USER_INIT;
+#endif
+
+		if ( ! yy_start )
+			yy_start = 1;	/* first start state */
+
+		if ( ! yyin )
+			yyin = stdin;
+
+		if ( ! yyout )
+			yyout = stdout;
+
+		if ( ! yy_current_buffer )
+			yy_current_buffer =
+				yy_create_buffer( yyin, YY_BUF_SIZE );
+
+		yy_load_buffer_state();
+		}
+
+	while ( 1 )		/* loops until end-of-file is reached */
+		{
+		yy_cp = yy_c_buf_p;
+
+		/* Support of yytext. */
+		*yy_cp = yy_hold_char;
+
+		/* yy_bp points to the position in yy_ch_buf of the start of
+		 * the current run.
+		 */
+		yy_bp = yy_cp;
+
+		yy_current_state = yy_start;
+yy_match:
+		do
+			{
+			register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+			if ( yy_accept[yy_current_state] )
+				{
+				yy_last_accepting_state = yy_current_state;
+				yy_last_accepting_cpos = yy_cp;
+				}
+			while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+				{
+				yy_current_state = (int) yy_def[yy_current_state];
+				if ( yy_current_state >= 65 )
+					yy_c = yy_meta[(unsigned int) yy_c];
+				}
+			yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+			++yy_cp;
+			}
+		while ( yy_base[yy_current_state] != 107 );
+
+yy_find_action:
+		yy_act = yy_accept[yy_current_state];
+		if ( yy_act == 0 )
+			{ /* have to back up */
+			yy_cp = yy_last_accepting_cpos;
+			yy_current_state = yy_last_accepting_state;
+			yy_act = yy_accept[yy_current_state];
+			}
+
+		YY_DO_BEFORE_ACTION;
+
+
+do_action:	/* This label is used only to access EOF actions. */
+
+
+		switch ( yy_act )
+	{ /* beginning of action switch */
+			case 0: /* must back up */
+			/* undo the effects of YY_DO_BEFORE_ACTION */
+			*yy_cp = yy_hold_char;
+			yy_cp = yy_last_accepting_cpos;
+			yy_current_state = yy_last_accepting_state;
+			goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 54 "scanner.l"
+
+	YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 56 "scanner.l"
+{
+  /* Create node representing constant with appropriate value.  */
+  melval.node = node_create ('c', atof (metext));
+  return NUMBER;
+}
+	YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 62 "scanner.l"
+{
+  /* Find symbol table record corresponding to function name.  */
+  melval.record = symbol_table_lookup (matheval_symbol_table, metext);
+  return FUNCTION;
+}
+	YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 68 "scanner.l"
+{
+  Record *record; /* Symbol table record.  */
+  /* Inserty variable into symbol table.  */
+  record = symbol_table_insert (matheval_symbol_table, metext, 'v');
+  melval.node = node_create ('v', record);
+  return VARIABLE;
+}
+	YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 76 "scanner.l"
+{
+  return '+';
+}
+	YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 80 "scanner.l"
+{
+  return '-';
+}
+	YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 84 "scanner.l"
+{
+  return '*';
+}
+	YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 88 "scanner.l"
+{
+  return '/';
+}
+	YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 92 "scanner.l"
+{
+  return '^';
+}
+	YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 96 "scanner.l"
+{
+  return '(';
+}
+	YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 100 "scanner.l"
+{
+  return ')';
+}
+	YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 104 "scanner.l"
+{
+  return '\n';
+}
+	YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 108 "scanner.l"
+ECHO;
+	YY_BREAK
+#line 808 "scanner.yy.cpp"
+case YY_STATE_EOF(INITIAL):
+	yyterminate();
+
+	case YY_END_OF_BUFFER:
+		{
+		/* Amount of text matched not including the EOB char. */
+		int yy_amount_of_matched_text = (int) (yy_cp - yytext_ptr) - 1;
+
+		/* Undo the effects of YY_DO_BEFORE_ACTION. */
+		*yy_cp = yy_hold_char;
+		YY_RESTORE_YY_MORE_OFFSET
+
+		if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_NEW )
+			{
+			/* We're scanning a new file or input source.  It's
+			 * possible that this happened because the user
+			 * just pointed yyin at a new source and called
+			 * yylex().  If so, then we have to assure
+			 * consistency between yy_current_buffer and our
+			 * globals.  Here is the right place to do so, because
+			 * this is the first action (other than possibly a
+			 * back-up) that will match for the new input source.
+			 */
+			yy_n_chars = yy_current_buffer->yy_n_chars;
+			yy_current_buffer->yy_input_file = yyin;
+			yy_current_buffer->yy_buffer_status = YY_BUFFER_NORMAL;
+			}
+
+		/* Note that here we test for yy_c_buf_p "<=" to the position
+		 * of the first EOB in the buffer, since yy_c_buf_p will
+		 * already have been incremented past the NUL character
+		 * (since all states make transitions on EOB to the
+		 * end-of-buffer state).  Contrast this with the test
+		 * in input().
+		 */
+		if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] )
+			{ /* This was really a NUL. */
+			yy_state_type yy_next_state;
+
+			yy_c_buf_p = yytext_ptr + yy_amount_of_matched_text;
+
+			yy_current_state = yy_get_previous_state();
+
+			/* Okay, we're now positioned to make the NUL
+			 * transition.  We couldn't have
+			 * yy_get_previous_state() go ahead and do it
+			 * for us because it doesn't know how to deal
+			 * with the possibility of jamming (and we don't
+			 * want to build jamming into it because then it
+			 * will run more slowly).
+			 */
+
+			yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+			yy_bp = yytext_ptr + YY_MORE_ADJ;
+
+			if ( yy_next_state )
+				{
+				/* Consume the NUL. */
+				yy_cp = ++yy_c_buf_p;
+				yy_current_state = yy_next_state;
+				goto yy_match;
+				}
+
+			else
+				{
+				yy_cp = yy_c_buf_p;
+				goto yy_find_action;
+				}
+			}
+
+		else switch ( yy_get_next_buffer() )
+			{
+			case EOB_ACT_END_OF_FILE:
+				{
+				yy_did_buffer_switch_on_eof = 0;
+
+				if ( yywrap() )
+					{
+					/* Note: because we've taken care in
+					 * yy_get_next_buffer() to have set up
+					 * yytext, we can now set up
+					 * yy_c_buf_p so that if some total
+					 * hoser (like flex itself) wants to
+					 * call the scanner after we return the
+					 * YY_NULL, it'll still work - another
+					 * YY_NULL will get returned.
+					 */
+					yy_c_buf_p = yytext_ptr + YY_MORE_ADJ;
+
+					yy_act = YY_STATE_EOF(YY_START);
+					goto do_action;
+					}
+
+				else
+					{
+					if ( ! yy_did_buffer_switch_on_eof )
+						YY_NEW_FILE;
+					}
+				break;
+				}
+
+			case EOB_ACT_CONTINUE_SCAN:
+				yy_c_buf_p =
+					yytext_ptr + yy_amount_of_matched_text;
+
+				yy_current_state = yy_get_previous_state();
+
+				yy_cp = yy_c_buf_p;
+				yy_bp = yytext_ptr + YY_MORE_ADJ;
+				goto yy_match;
+
+			case EOB_ACT_LAST_MATCH:
+				yy_c_buf_p =
+				&yy_current_buffer->yy_ch_buf[yy_n_chars];
+
+				yy_current_state = yy_get_previous_state();
+
+				yy_cp = yy_c_buf_p;
+				yy_bp = yytext_ptr + YY_MORE_ADJ;
+				goto yy_find_action;
+			}
+		break;
+		}
+
+	default:
+		YY_FATAL_ERROR(
+			"fatal flex scanner internal error--no action found" );
+	} /* end of action switch */
+		} /* end of scanning one token */
+	} /* end of yylex */
+
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ *	EOB_ACT_LAST_MATCH -
+ *	EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ *	EOB_ACT_END_OF_FILE - end of file
+ */
+
+static int yy_get_next_buffer()
+	{
+	register char *dest = yy_current_buffer->yy_ch_buf;
+	register char *source = yytext_ptr;
+	register int number_to_move, i;
+	int ret_val;
+
+	if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] )
+		YY_FATAL_ERROR(
+		"fatal flex scanner internal error--end of buffer missed" );
+
+	if ( yy_current_buffer->yy_fill_buffer == 0 )
+		{ /* Don't try to fill the buffer, so this is an EOF. */
+		if ( yy_c_buf_p - yytext_ptr - YY_MORE_ADJ == 1 )
+			{
+			/* We matched a single character, the EOB, so
+			 * treat this as a final EOF.
+			 */
+			return EOB_ACT_END_OF_FILE;
+			}
+
+		else
+			{
+			/* We matched some text prior to the EOB, first
+			 * process it.
+			 */
+			return EOB_ACT_LAST_MATCH;
+			}
+		}
+
+	/* Try to read more data. */
+
+	/* First move last chars to start of buffer. */
+	number_to_move = (int) (yy_c_buf_p - yytext_ptr) - 1;
+
+	for ( i = 0; i < number_to_move; ++i )
+		*(dest++) = *(source++);
+
+	if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+		/* don't do the read, it's not guaranteed to return an EOF,
+		 * just force an EOF
+		 */
+		yy_current_buffer->yy_n_chars = yy_n_chars = 0;
+
+	else
+		{
+		int num_to_read =
+			yy_current_buffer->yy_buf_size - number_to_move - 1;
+
+		while ( num_to_read <= 0 )
+			{ /* Not enough room in the buffer - grow it. */
+#ifdef YY_USES_REJECT
+			YY_FATAL_ERROR(
+"input buffer overflow, can't enlarge buffer because scanner uses REJECT" );
+#else
+
+			/* just a shorter name for the current buffer */
+			YY_BUFFER_STATE b = yy_current_buffer;
+
+			int yy_c_buf_p_offset =
+				(int) (yy_c_buf_p - b->yy_ch_buf);
+
+			if ( b->yy_is_our_buffer )
+				{
+				int new_size = b->yy_buf_size * 2;
+
+				if ( new_size <= 0 )
+					b->yy_buf_size += b->yy_buf_size / 8;
+				else
+					b->yy_buf_size *= 2;
+
+				b->yy_ch_buf = (char *)
+					/* Include room in for 2 EOB chars. */
+					yy_flex_realloc( (void *) b->yy_ch_buf,
+							 b->yy_buf_size + 2 );
+				}
+			else
+				/* Can't grow it, we don't own it. */
+				b->yy_ch_buf = 0;
+
+			if ( ! b->yy_ch_buf )
+				YY_FATAL_ERROR(
+				"fatal error - scanner input buffer overflow" );
+
+			yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+			num_to_read = yy_current_buffer->yy_buf_size -
+						number_to_move - 1;
+#endif
+			}
+
+		if ( num_to_read > YY_READ_BUF_SIZE )
+			num_to_read = YY_READ_BUF_SIZE;
+
+		/* Read in more data. */
+		YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]),
+			yy_n_chars, num_to_read );
+
+		yy_current_buffer->yy_n_chars = yy_n_chars;
+		}
+
+	if ( yy_n_chars == 0 )
+		{
+		if ( number_to_move == YY_MORE_ADJ )
+			{
+			ret_val = EOB_ACT_END_OF_FILE;
+			yyrestart( yyin );
+			}
+
+		else
+			{
+			ret_val = EOB_ACT_LAST_MATCH;
+			yy_current_buffer->yy_buffer_status =
+				YY_BUFFER_EOF_PENDING;
+			}
+		}
+
+	else
+		ret_val = EOB_ACT_CONTINUE_SCAN;
+
+	yy_n_chars += number_to_move;
+	yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+	yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+	yytext_ptr = &yy_current_buffer->yy_ch_buf[0];
+
+	return ret_val;
+	}
+
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+static yy_state_type yy_get_previous_state()
+	{
+	register yy_state_type yy_current_state;
+	register char *yy_cp;
+
+	yy_current_state = yy_start;
+
+	for ( yy_cp = yytext_ptr + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp )
+		{
+		register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+		if ( yy_accept[yy_current_state] )
+			{
+			yy_last_accepting_state = yy_current_state;
+			yy_last_accepting_cpos = yy_cp;
+			}
+		while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+			{
+			yy_current_state = (int) yy_def[yy_current_state];
+			if ( yy_current_state >= 65 )
+				yy_c = yy_meta[(unsigned int) yy_c];
+			}
+		yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+		}
+
+	return yy_current_state;
+	}
+
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ *	next_state = yy_try_NUL_trans( current_state );
+ */
+
+#ifdef YY_USE_PROTOS
+static yy_state_type yy_try_NUL_trans( yy_state_type yy_current_state )
+#else
+static yy_state_type yy_try_NUL_trans( yy_current_state )
+yy_state_type yy_current_state;
+#endif
+	{
+	register int yy_is_jam;
+	register char *yy_cp = yy_c_buf_p;
+
+	register YY_CHAR yy_c = 1;
+	if ( yy_accept[yy_current_state] )
+		{
+		yy_last_accepting_state = yy_current_state;
+		yy_last_accepting_cpos = yy_cp;
+		}
+	while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+		{
+		yy_current_state = (int) yy_def[yy_current_state];
+		if ( yy_current_state >= 65 )
+			yy_c = yy_meta[(unsigned int) yy_c];
+		}
+	yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+	yy_is_jam = (yy_current_state == 64);
+
+	return yy_is_jam ? 0 : yy_current_state;
+	}
+
+
+#ifndef YY_NO_UNPUT
+#ifdef YY_USE_PROTOS
+static void yyunput( int c, register char *yy_bp )
+#else
+static void yyunput( c, yy_bp )
+int c;
+register char *yy_bp;
+#endif
+	{
+	register char *yy_cp = yy_c_buf_p;
+
+	/* undo effects of setting up yytext */
+	*yy_cp = yy_hold_char;
+
+	if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )
+		{ /* need to shift things up to make room */
+		/* +2 for EOB chars. */
+		register int number_to_move = yy_n_chars + 2;
+		register char *dest = &yy_current_buffer->yy_ch_buf[
+					yy_current_buffer->yy_buf_size + 2];
+		register char *source =
+				&yy_current_buffer->yy_ch_buf[number_to_move];
+
+		while ( source > yy_current_buffer->yy_ch_buf )
+			*--dest = *--source;
+
+		yy_cp += (int) (dest - source);
+		yy_bp += (int) (dest - source);
+		yy_current_buffer->yy_n_chars =
+			yy_n_chars = yy_current_buffer->yy_buf_size;
+
+		if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )
+			YY_FATAL_ERROR( "flex scanner push-back overflow" );
+		}
+
+	*--yy_cp = (char) c;
+
+
+	yytext_ptr = yy_bp;
+	yy_hold_char = *yy_cp;
+	yy_c_buf_p = yy_cp;
+	}
+#endif	/* ifndef YY_NO_UNPUT */
+
+
+#ifdef __cplusplus
+static int yyinput()
+#else
+static int input()
+#endif
+	{
+	int c;
+
+	*yy_c_buf_p = yy_hold_char;
+
+	if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+		{
+		/* yy_c_buf_p now points to the character we want to return.
+		 * If this occurs *before* the EOB characters, then it's a
+		 * valid NUL; if not, then we've hit the end of the buffer.
+		 */
+		if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] )
+			/* This was really a NUL. */
+			*yy_c_buf_p = '\0';
+
+		else
+			{ /* need more input */
+			int offset = yy_c_buf_p - yytext_ptr;
+			++yy_c_buf_p;
+
+			switch ( yy_get_next_buffer() )
+				{
+				case EOB_ACT_LAST_MATCH:
+					/* This happens because yy_g_n_b()
+					 * sees that we've accumulated a
+					 * token and flags that we need to
+					 * try matching the token before
+					 * proceeding.  But for input(),
+					 * there's no matching to consider.
+					 * So convert the EOB_ACT_LAST_MATCH
+					 * to EOB_ACT_END_OF_FILE.
+					 */
+
+					/* Reset buffer status. */
+					yyrestart( yyin );
+
+					/* fall through */
+
+				case EOB_ACT_END_OF_FILE:
+					{
+					if ( yywrap() )
+						return EOF;
+
+					if ( ! yy_did_buffer_switch_on_eof )
+						YY_NEW_FILE;
+#ifdef __cplusplus
+					return yyinput();
+#else
+					return input();
+#endif
+					}
+
+				case EOB_ACT_CONTINUE_SCAN:
+					yy_c_buf_p = yytext_ptr + offset;
+					break;
+				}
+			}
+		}
+
+	c = *(unsigned char *) yy_c_buf_p;	/* cast for 8-bit char's */
+	*yy_c_buf_p = '\0';	/* preserve yytext */
+	yy_hold_char = *++yy_c_buf_p;
+
+
+	return c;
+	}
+
+
+#ifdef YY_USE_PROTOS
+void yyrestart( FILE *input_file )
+#else
+void yyrestart( input_file )
+FILE *input_file;
+#endif
+	{
+	if ( ! yy_current_buffer )
+		yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE );
+
+	yy_init_buffer( yy_current_buffer, input_file );
+	yy_load_buffer_state();
+	}
+
+
+#ifdef YY_USE_PROTOS
+void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )
+#else
+void yy_switch_to_buffer( new_buffer )
+YY_BUFFER_STATE new_buffer;
+#endif
+	{
+	if ( yy_current_buffer == new_buffer )
+		return;
+
+	if ( yy_current_buffer )
+		{
+		/* Flush out information for old buffer. */
+		*yy_c_buf_p = yy_hold_char;
+		yy_current_buffer->yy_buf_pos = yy_c_buf_p;
+		yy_current_buffer->yy_n_chars = yy_n_chars;
+		}
+
+	yy_current_buffer = new_buffer;
+	yy_load_buffer_state();
+
+	/* We don't actually know whether we did this switch during
+	 * EOF (yywrap()) processing, but the only time this flag
+	 * is looked at is after yywrap() is called, so it's safe
+	 * to go ahead and always set it.
+	 */
+	yy_did_buffer_switch_on_eof = 1;
+	}
+
+
+#ifdef YY_USE_PROTOS
+void yy_load_buffer_state( void )
+#else
+void yy_load_buffer_state()
+#endif
+	{
+	yy_n_chars = yy_current_buffer->yy_n_chars;
+	yytext_ptr = yy_c_buf_p = yy_current_buffer->yy_buf_pos;
+	yyin = yy_current_buffer->yy_input_file;
+	yy_hold_char = *yy_c_buf_p;
+	}
+
+
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_create_buffer( FILE *file, int size )
+#else
+YY_BUFFER_STATE yy_create_buffer( file, size )
+FILE *file;
+int size;
+#endif
+	{
+	YY_BUFFER_STATE b;
+
+	b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+	b->yy_buf_size = size;
+
+	/* yy_ch_buf has to be 2 characters longer than the size given because
+	 * we need to put in 2 end-of-buffer characters.
+	 */
+	b->yy_ch_buf = (char *) yy_flex_alloc( b->yy_buf_size + 2 );
+	if ( ! b->yy_ch_buf )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+	b->yy_is_our_buffer = 1;
+
+	yy_init_buffer( b, file );
+
+	return b;
+	}
+
+
+#ifdef YY_USE_PROTOS
+void yy_delete_buffer( YY_BUFFER_STATE b )
+#else
+void yy_delete_buffer( b )
+YY_BUFFER_STATE b;
+#endif
+	{
+	if ( ! b )
+		return;
+
+	if ( b == yy_current_buffer )
+		yy_current_buffer = (YY_BUFFER_STATE) 0;
+
+	if ( b->yy_is_our_buffer )
+		yy_flex_free( (void *) b->yy_ch_buf );
+
+	yy_flex_free( (void *) b );
+	}
+
+
+#ifndef YY_ALWAYS_INTERACTIVE
+#ifndef YY_NEVER_INTERACTIVE
+extern int isatty YY_PROTO(( int ));
+#endif
+#endif
+
+#ifdef YY_USE_PROTOS
+void yy_init_buffer( YY_BUFFER_STATE b, FILE *file )
+#else
+void yy_init_buffer( b, file )
+YY_BUFFER_STATE b;
+FILE *file;
+#endif
+
+
+	{
+	yy_flush_buffer( b );
+
+	b->yy_input_file = file;
+	b->yy_fill_buffer = 1;
+
+#if YY_ALWAYS_INTERACTIVE
+	b->yy_is_interactive = 1;
+#else
+#if YY_NEVER_INTERACTIVE
+	b->yy_is_interactive = 0;
+#else
+	b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+#endif
+#endif
+	}
+
+
+#ifdef YY_USE_PROTOS
+void yy_flush_buffer( YY_BUFFER_STATE b )
+#else
+void yy_flush_buffer( b )
+YY_BUFFER_STATE b;
+#endif
+
+	{
+	if ( ! b )
+		return;
+
+	b->yy_n_chars = 0;
+
+	/* We always need two end-of-buffer characters.  The first causes
+	 * a transition to the end-of-buffer state.  The second causes
+	 * a jam in that state.
+	 */
+	b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+	b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+	b->yy_buf_pos = &b->yy_ch_buf[0];
+
+	b->yy_at_bol = 1;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	if ( b == yy_current_buffer )
+		yy_load_buffer_state();
+	}
+
+
+#ifndef YY_NO_SCAN_BUFFER
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_buffer( char *base, yy_size_t size )
+#else
+YY_BUFFER_STATE yy_scan_buffer( base, size )
+char *base;
+yy_size_t size;
+#endif
+	{
+	YY_BUFFER_STATE b;
+
+	if ( size < 2 ||
+	     base[size-2] != YY_END_OF_BUFFER_CHAR ||
+	     base[size-1] != YY_END_OF_BUFFER_CHAR )
+		/* They forgot to leave room for the EOB's. */
+		return 0;
+
+	b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+	b->yy_buf_size = size - 2;	/* "- 2" to take care of EOB's */
+	b->yy_buf_pos = b->yy_ch_buf = base;
+	b->yy_is_our_buffer = 0;
+	b->yy_input_file = 0;
+	b->yy_n_chars = b->yy_buf_size;
+	b->yy_is_interactive = 0;
+	b->yy_at_bol = 1;
+	b->yy_fill_buffer = 0;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	yy_switch_to_buffer( b );
+
+	return b;
+	}
+#endif
+
+
+#ifndef YY_NO_SCAN_STRING
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_string( yyconst char *yy_str )
+#else
+YY_BUFFER_STATE yy_scan_string( yy_str )
+yyconst char *yy_str;
+#endif
+	{
+	int len;
+	for ( len = 0; yy_str[len]; ++len )
+		;
+
+	return yy_scan_bytes( yy_str, len );
+	}
+#endif
+
+
+#ifndef YY_NO_SCAN_BYTES
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_bytes( yyconst char *bytes, int len )
+#else
+YY_BUFFER_STATE yy_scan_bytes( bytes, len )
+yyconst char *bytes;
+int len;
+#endif
+	{
+	YY_BUFFER_STATE b;
+	char *buf;
+	yy_size_t n;
+	int i;
+
+	/* Get memory for full buffer, including space for trailing EOB's. */
+	n = len + 2;
+	buf = (char *) yy_flex_alloc( n );
+	if ( ! buf )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+	for ( i = 0; i < len; ++i )
+		buf[i] = bytes[i];
+
+	buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR;
+
+	b = yy_scan_buffer( buf, n );
+	if ( ! b )
+		YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+	/* It's okay to grow etc. this buffer, and we should throw it
+	 * away when we're done.
+	 */
+	b->yy_is_our_buffer = 1;
+
+	return b;
+	}
+#endif
+
+
+#ifndef YY_NO_PUSH_STATE
+#ifdef YY_USE_PROTOS
+static void yy_push_state( int new_state )
+#else
+static void yy_push_state( new_state )
+int new_state;
+#endif
+	{
+	if ( yy_start_stack_ptr >= yy_start_stack_depth )
+		{
+		yy_size_t new_size;
+
+		yy_start_stack_depth += YY_START_STACK_INCR;
+		new_size = yy_start_stack_depth * sizeof( int );
+
+		if ( ! yy_start_stack )
+			yy_start_stack = (int *) yy_flex_alloc( new_size );
+
+		else
+			yy_start_stack = (int *) yy_flex_realloc(
+					(void *) yy_start_stack, new_size );
+
+		if ( ! yy_start_stack )
+			YY_FATAL_ERROR(
+			"out of memory expanding start-condition stack" );
+		}
+
+	yy_start_stack[yy_start_stack_ptr++] = YY_START;
+
+	BEGIN(new_state);
+	}
+#endif
+
+
+#ifndef YY_NO_POP_STATE
+static void yy_pop_state()
+	{
+	if ( --yy_start_stack_ptr < 0 )
+		YY_FATAL_ERROR( "start-condition stack underflow" );
+
+	BEGIN(yy_start_stack[yy_start_stack_ptr]);
+	}
+#endif
+
+
+#ifndef YY_NO_TOP_STATE
+static int yy_top_state()
+	{
+	return yy_start_stack[yy_start_stack_ptr - 1];
+	}
+#endif
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+#ifdef YY_USE_PROTOS
+static void yy_fatal_error( yyconst char msg[] )
+#else
+static void yy_fatal_error( msg )
+char msg[];
+#endif
+	{
+	(void) fprintf( stderr, "%s\n", msg );
+	exit( YY_EXIT_FAILURE );
+	}
+
+
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+		yytext[yyleng] = yy_hold_char; \
+		yy_c_buf_p = yytext + n; \
+		yy_hold_char = *yy_c_buf_p; \
+		*yy_c_buf_p = '\0'; \
+		yyleng = n; \
+		} \
+	while ( 0 )
+
+
+/* Internal utility routines. */
+
+#ifndef yytext_ptr
+#ifdef YY_USE_PROTOS
+static void yy_flex_strncpy( char *s1, yyconst char *s2, int n )
+#else
+static void yy_flex_strncpy( s1, s2, n )
+char *s1;
+yyconst char *s2;
+int n;
+#endif
+	{
+	register int i;
+	for ( i = 0; i < n; ++i )
+		s1[i] = s2[i];
+	}
+#endif
+
+#ifdef YY_NEED_STRLEN
+#ifdef YY_USE_PROTOS
+static int yy_flex_strlen( yyconst char *s )
+#else
+static int yy_flex_strlen( s )
+yyconst char *s;
+#endif
+	{
+	register int n;
+	for ( n = 0; s[n]; ++n )
+		;
+
+	return n;
+	}
+#endif
+
+
+#ifdef YY_USE_PROTOS
+static void *yy_flex_alloc( yy_size_t size )
+#else
+static void *yy_flex_alloc( size )
+yy_size_t size;
+#endif
+	{
+	return (void *) malloc( size );
+	}
+
+#ifdef YY_USE_PROTOS
+static void *yy_flex_realloc( void *ptr, yy_size_t size )
+#else
+static void *yy_flex_realloc( ptr, size )
+void *ptr;
+yy_size_t size;
+#endif
+	{
+	/* The cast to (char *) in the following accommodates both
+	 * implementations that use char* generic pointers, and those
+	 * that use void* generic pointers.  It works with the latter
+	 * because both ANSI C and C++ allow castless assignment from
+	 * any pointer type to void*, and deal with argument conversions
+	 * as though doing an assignment.
+	 */
+	return (void *) realloc( (char *) ptr, size );
+	}
+
+#ifdef YY_USE_PROTOS
+static void yy_flex_free( void *ptr )
+#else
+static void yy_flex_free( ptr )
+void *ptr;
+#endif
+	{
+	free( ptr );
+	}
+
+#if YY_MAIN
+int main()
+	{
+	yylex();
+	return 0;
+	}
+#endif
+#line 108 "scanner.l"
+
+
+#undef mewrap
+
+int mewrap() { return 1; }
+
+static int input_from_string (char *buffer, int max_size)
+{
+  int count; /* Count of characters to copy from input string to buffer.  */
+
+  /* Calculate count of characters to copy.  */
+  count = strlen (matheval_input_string);
+  if (count > max_size)
+    count = max_size;
+
+  /* Perform copy operation and update input string.  */
+  memcpy(buffer, matheval_input_string, count);
+  matheval_input_string += count;
+
+  return count;
+}
+
+void force_buffer_flush() { YY_FLUSH_BUFFER; }
diff --git a/contrib/MathEval/symbol_table.cpp b/contrib/MathEval/symbol_table.cpp
new file mode 100644
index 0000000000..34e01043a4
--- /dev/null
+++ b/contrib/MathEval/symbol_table.cpp
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 1999, 2002, 2003  Free Software Foundation, Inc.
+ *
+ * This file is part of GNU libmatheval
+ *
+ * GNU libmatheval 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, or (at your option) any later
+ * version.
+ *
+ * GNU libmatheval 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
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* This file was modified for inclusion in Gmsh */
+
+#include <assert.h>
+#include <stdarg.h>
+
+#include "common.h"
+#include "symbol_table.h"
+#include "xmath.h"
+
+/*
+ * Type definition for function accepting single argument of double type and
+ * returning double value.
+ */
+typedef double (*function_type) (double);
+
+/* Calculate hash value for given name and hash table length.  */
+static int hash(char *name, int length);
+
+SymbolTable *
+symbol_table_create(int length)
+{
+  SymbolTable    *symbol_table;	/* Pointer to symbol table.  */
+  static char    *names[] = {"Exp", "Log", "Sqrt", "Sin", "Cos", "Tan", "Ctan", 
+			     "Asin", "Acos", "Atan", "Actan", "Sinh", "Cosh", "Tanh",
+			     "Ctanh", "Asinh", "Acosh", "Atanh", "Actanh", "Fabs", "Rand" };
+  static double   (*functions[]) (double) = { exp, log, sqrt, sin, cos, tan, x_ctan, 
+					      asin, acos, atan, x_actan, sinh, cosh, tanh, 
+					      x_ctanh, x_asinh, x_acosh, x_atanh, x_actanh,
+					      fabs, x_rand};
+  unsigned int i;
+  
+  /*
+   * Allocate memory for symbol table data structure as well as for
+   * corresponding hash table.
+   */
+  symbol_table = XMALLOC(SymbolTable, 1);
+  symbol_table->length = length;
+  symbol_table->records = XCALLOC(Record, symbol_table->length);
+  
+  /*
+   * Insert predefined functions into symbol table.
+   */
+  for (i = 0; i < sizeof(names) / sizeof(names[0]); i++)
+    symbol_table_insert(symbol_table, names[i], 'f', functions[i]);
+  
+  /*
+   * Initialize symbol table reference count.
+   */
+  symbol_table->reference_count = 1;
+  
+  return symbol_table;
+}
+
+void
+symbol_table_destroy(SymbolTable * symbol_table)
+{
+  Record         *curr, *next; /* Pointers to current and next record
+				* while traversing hash table bucket.  */
+  int             i;
+
+  if(!symbol_table)
+    return;
+  
+  /*
+   * Decrement refernce count and return if symbol table still used
+   * elsewhere.
+   */
+  if (--symbol_table->reference_count > 0)
+    return;
+  
+  /*
+   * Delete hash table as well as data structure representing symbol
+   * table.
+   */
+  for (i = 0; i < symbol_table->length; i++)
+    for (curr = symbol_table->records[i].next; curr;) {
+      next = curr->next;
+      XFREE(curr->name);
+      XFREE(curr);
+      curr = next;
+    }
+  XFREE(symbol_table->records);
+  XFREE(symbol_table);
+}
+
+Record *
+symbol_table_insert(SymbolTable * symbol_table, char *name, char type,...)
+{
+  Record         *record;	/* Pointer to symbol table record
+				 * corresponding to name given.  */
+  va_list         ap;		/* Function variable argument list.  */
+  int             i;
+  
+  /*
+   * Check if symbol already in table and, if affirmative and record
+   * type same as type given, return corresponding record immediately.
+   */
+  if ((record = symbol_table_lookup(symbol_table, name))) {
+    assert(record->type == type);
+    return record;
+  }
+  /*
+   * Allocate memory for and initialize new record.
+   */
+  record = XMALLOC(Record, 1);
+  record->name = XMALLOC(char, strlen(name) + 1);
+  strcpy(record->name, name);
+  record->type = type;
+  
+  /*
+   * Parse function variable argument list to complete record
+   * initialization.
+   */
+  va_start(ap, type);
+  switch (record->type) {
+  case 'v':
+    record->data.value = 0;
+    break;
+    
+  case 'f':
+    record->data.function = va_arg(ap, function_type);
+    break;
+  }
+  va_end(ap);
+  
+  /*
+   * Calculate hash value and put record in corresponding hash table
+   * bucket.
+   */
+  i = hash(name, symbol_table->length);
+  record->next = symbol_table->records[i].next;
+  symbol_table->records[i].next = record;
+  
+  return record;
+}
+
+Record *
+symbol_table_lookup(SymbolTable * symbol_table, char *name)
+{
+  int             i;	/* Hash value. */
+  Record         *curr;	/* Pointer to current symbol table record.  */
+  
+  /*
+   * Calcuate hash value for name given.
+   */
+  i = hash(name, symbol_table->length);
+  
+  /*
+   * Lookup for name in hash table bucket corresponding to above hash
+   * value.
+   */
+  for (curr = symbol_table->records[i].next; curr; curr = curr->next)
+    if (!strcmp(curr->name, name))
+      return curr;
+  
+  return NULL;
+}
+
+SymbolTable *
+symbol_table_assign(SymbolTable * symbol_table)
+{
+  /*
+   * Increase symbol table reference count and return pointer to data
+   * structure representing table.
+   */
+  symbol_table->reference_count++;
+  return symbol_table;
+}
+
+/*
+ * Function below reused from A.V. Aho, R. Sethi, J.D. Ullman, "Compilers -
+ * Principle, Techniques, and Tools", Addison-Wesley, 1986, pp 435-437, and
+ * in turn from P.J. Weineberger's C compiler.
+ */
+static int
+hash(char *s, int n)
+{
+  char           *p;
+  unsigned        h, g;
+  
+  h = 0;
+  
+  for (p = s; *p; p++) {
+    h = (h << 4) + *p;
+    if ((g = h & 0xf0000000)) {
+      h = h ^ (g >> 24);
+      h = h ^ g;
+    }
+  }
+  
+  return h % n;
+}
diff --git a/contrib/MathEval/symbol_table.h b/contrib/MathEval/symbol_table.h
new file mode 100644
index 0000000000..0862c956ef
--- /dev/null
+++ b/contrib/MathEval/symbol_table.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 1999, 2002, 2003  Free Software Foundation, Inc.
+ * 
+ * This file is part of GNU libmatheval
+ * 
+ * GNU libmatheval 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, or (at your option) any later
+ * version.
+ * 
+ * GNU libmatheval 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
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* This file was modified for inclusion in Gmsh */
+
+#ifndef SYMBOL_TABLE_H
+#define SYMBOL_TABLE_H
+
+/* Data structure representing symbol table record.  */
+typedef struct _Record {
+  struct _Record *next;	/* Pointer to next record.  */
+  char *name; /* Symbol name.  */
+  char type; /* Symbol type ('v' for variable, 'f' for function).  */
+  union {
+    double value; /* Variable value.  */
+    double (*function) (double); /* Pointer to function to calculate
+				  * its value.  */
+  } data;
+} Record;
+
+/*
+ * Data structure representing symbol table (hash table is used for this
+ * purpose).
+ */
+typedef struct {
+  int length; /* Hash table length.  */
+  Record  *records; /* Hash table buckets.  */
+  int reference_count; /* Reference count for symbol table (evaluator
+			* for derivative uses same symbol table as
+			* evaluator for corresponding function).  */
+} SymbolTable;
+
+/* Create symbol table using specified length of hash table.  */
+SymbolTable *symbol_table_create(int length);
+
+/* Destroy symbol table.  */
+void symbol_table_destroy(SymbolTable * symbol_table);
+
+/*
+ * Insert symbol into given symbol table.  Further arguments are symbol name
+ * and its type, as well as additional arguments according to symbol type.
+ * Return value is pointer to symobl table record created to represent
+ * symbol.  If symbol already in symbol table, pointer to its record is
+ * returned immediately.
+ */
+Record *symbol_table_insert(SymbolTable * symbol_table, char *name, char type,...);
+
+/*
+ * Lookup symbol by name from given symbol table.  Pointer to symbol record
+ * is returned if symbol found, null pointer otherwise.
+ */
+Record *symbol_table_lookup(SymbolTable * symbol_table, char *name);
+
+/*
+ * Return symbol table pointer to be assigned to variable.  This function
+ * should be used instead of simple pointer assignement for proper reference
+ * counting.  Users willing to manage reference counts by themselves are free
+ * to ignore this functions.
+ */
+SymbolTable *symbol_table_assign(SymbolTable * symbol_table);
+
+#endif
diff --git a/contrib/MathEval/xmath.cpp b/contrib/MathEval/xmath.cpp
new file mode 100644
index 0000000000..da1479b032
--- /dev/null
+++ b/contrib/MathEval/xmath.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 1999, 2002, 2003  Free Software Foundation, Inc.
+ *
+ * This file is part of GNU libmatheval
+ *
+ * GNU libmatheval 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, or (at your option) any later
+ * version.
+ *
+ * GNU libmatheval 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
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* This file was modified for inclusion in Gmsh */
+
+#include "xmath.h"
+#include <math.h>
+#include <stdlib.h>
+
+double
+x_ctan(double x)
+{
+  /*
+   * Calculate cotangent value.
+   */
+  return 1 / tan(x);
+}
+
+double
+x_actan(double x)
+{
+  /*
+   * Calculate inverse cotangent value.
+   */
+  return atan(1 / x);
+}
+
+double
+x_ctanh(double x)
+{
+  /*
+   * Calculate hyperbolic cotangent value.
+   */
+  return 1 / tanh(x);
+}
+
+double
+x_asinh(double x)
+{
+  /*
+   * Calculate inverse hyperbolic sine value using relation between
+   * hyperbolic and exponential functions.
+   */
+  return log(x + sqrt(x * x + 1));
+}
+
+double
+x_acosh(double x)
+{
+  /*
+   * Calculate inverse hyperbolic cosine value using relation between
+   * hyperbolic and exponential functions.
+   */
+  return log(x + sqrt(x * x - 1));
+}
+
+double
+x_atanh(double x)
+{
+  /*
+   * Calculate inverse hyperbolic tangent value using relation between
+   * hyperbolic and exponential functions.
+   */
+  return 0.5 * log((1 + x) / (1 - x));
+}
+
+double
+x_actanh(double x)
+{
+  /*
+   * Calculate inverse hyperbolic cotangent value.
+   */
+  return atanh(1 / x);
+}
+
+double
+x_rand(double x)
+{
+  /*
+   * Calculate random value in [0, x].
+   */
+  
+  return x*(double)rand()/(double)RAND_MAX;
+}
diff --git a/contrib/MathEval/xmath.h b/contrib/MathEval/xmath.h
new file mode 100644
index 0000000000..f086ad916a
--- /dev/null
+++ b/contrib/MathEval/xmath.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 1999, 2002, 2003  Free Software Foundation, Inc.
+ * 
+ * This file is part of GNU libmatheval
+ * 
+ * GNU libmatheval 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, or (at your option) any later
+ * version.
+ * 
+ * GNU libmatheval 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
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* This file was modified for inclusion in Gmsh */
+
+#ifndef XMATH_H
+#define XMATH_H
+
+double x_ctan(double x);
+double x_actan(double x);
+double x_ctanh(double x);
+double x_asinh(double x);
+double x_acosh(double x);
+double x_atanh(double x);
+double x_actanh(double x);
+double x_rand(double x);
+
+#endif
diff --git a/contrib/Metis/Makefile b/contrib/Metis/Makefile
new file mode 100644
index 0000000000..b239ce9cfd
--- /dev/null
+++ b/contrib/Metis/Makefile
@@ -0,0 +1,103 @@
+# $Id: Makefile,v 1.1 2005-09-21 17:29:37 geuzaine Exp $
+#
+# Copyright (C) 1997-2005 C. Geuzaine, J.-F. Remacle
+#
+# 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.
+# 
+# Please report all bugs and problems to <gmsh@geuz.org>.
+
+include ../../variables
+
+LIB     = ../../lib/libGmshMetis.a
+INCLUDE = -I.
+CFLAGS  = ${OPTIM} ${FLAGS} ${INCLUDE}
+
+SRC = balance.c \
+      fm.c        \
+      kwayfm.c    \
+      mcoarsen.c  \
+      minitpart2.c \
+      mpmetis.c   \
+      pmetis.c     \
+      subdomains.c\
+      bucketsort.c  \
+      fortran.c  \
+      kwayrefine.c\
+      memory.c  \
+      minitpart.c  \
+      mrefine2.c\
+      pqueue.c\
+      timing.c\
+      ccgraph.c \
+      frename.c \
+      kwayvolfm.c\
+      mesh.c\
+      mkmetis.c\
+      mrefine.c \
+      refine.c  \
+      util.c\
+      coarsen.c \
+      graph.c\
+      kwayvolrefine.c  \
+      meshpart.c  \
+      mkwayfmh.c \
+      mutil.c \
+      separator.c\
+      compress.c\
+      initpart.c\
+      match.c\
+      mfm2.c \
+      mkwayrefine.c\
+      myqsort.c\
+      sfm.c\
+      debug.c \
+      kmetis.c  \
+      mbalance2.c\
+      mfm.c  \
+      mmatch.c \
+      ometis.c \
+      srefine.c\
+      estmem.c \
+      kvmetis.c\
+      mbalance.c \
+      mincover.c  \
+      mmd.c  \
+      parmetis.c \
+     stat.c
+
+OBJ = ${SRC:.c=.o}
+
+.SUFFIXES: .o .c
+
+${LIB}: ${OBJ} 
+	${AR} ${LIB} ${OBJ} 
+	${RANLIB} ${LIB}
+
+.c.o:
+	${CC} ${CFLAGS} -c $< -o ${<:.c=.o}
+
+clean:
+	rm -f *.o
+
+depend:
+	(sed '/^# DO NOT DELETE THIS LINE/q' Makefile && \
+	${CXX} -MM ${CFLAGS} ${SRC} \
+	) >Makefile.new
+	cp Makefile Makefile.bak
+	cp Makefile.new Makefile
+	rm -f Makefile.new
+
+# DO NOT DELETE THIS LINE
diff --git a/contrib/Metis/balance.c b/contrib/Metis/balance.c
new file mode 100644
index 0000000000..2e9bd5fbbb
--- /dev/null
+++ b/contrib/Metis/balance.c
@@ -0,0 +1,278 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * balance.c
+ *
+ * This file contains code that is used to forcefully balance either
+ * bisections or k-sections
+ *
+ * Started 7/29/97
+ * George
+ *
+ * $Id: balance.c,v 1.1 2005-09-21 17:29:37 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+/*************************************************************************
+* This function is the entry point of the bisection balancing algorithms.
+**************************************************************************/
+void Balance2Way(CtrlType *ctrl, GraphType *graph, int *tpwgts, float ubfactor)
+{
+  int i, j, nvtxs, from, imax, gain, mindiff;
+  idxtype *id, *ed;
+
+  /* Return right away if the balance is OK */
+  mindiff = abs(tpwgts[0]-graph->pwgts[0]);
+  if (mindiff < 3*(graph->pwgts[0]+graph->pwgts[1])/graph->nvtxs)
+    return;
+  if (graph->pwgts[0] > tpwgts[0] && graph->pwgts[0] < (int)(ubfactor*tpwgts[0]))
+    return;
+  if (graph->pwgts[1] > tpwgts[1] && graph->pwgts[1] < (int)(ubfactor*tpwgts[1]))
+    return;
+
+  if (graph->nbnd > 0)
+    Bnd2WayBalance(ctrl, graph, tpwgts);
+  else
+    General2WayBalance(ctrl, graph, tpwgts);
+
+}
+
+
+
+/*************************************************************************
+* This function balances two partitions by moving boundary nodes
+* from the domain that is overweight to the one that is underweight.
+**************************************************************************/
+void Bnd2WayBalance(CtrlType *ctrl, GraphType *graph, int *tpwgts)
+{
+  int i, ii, j, k, kwgt, nvtxs, nbnd, nswaps, from, to, pass, me, tmp;
+  idxtype *xadj, *vwgt, *adjncy, *adjwgt, *where, *id, *ed, *bndptr, *bndind, *pwgts;
+  idxtype *moved, *perm;
+  PQueueType parts;
+  int higain, oldgain, mincut, mindiff;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  vwgt = graph->vwgt;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+  where = graph->where;
+  id = graph->id;
+  ed = graph->ed;
+  pwgts = graph->pwgts;
+  bndptr = graph->bndptr;
+  bndind = graph->bndind;
+
+  moved = idxwspacemalloc(ctrl, nvtxs);
+  perm = idxwspacemalloc(ctrl, nvtxs);
+
+  /* Determine from which domain you will be moving data */
+  mindiff = abs(tpwgts[0]-pwgts[0]);
+  from = (pwgts[0] < tpwgts[0] ? 1 : 0);
+  to = (from+1)%2;
+
+  IFSET(ctrl->dbglvl, DBG_REFINE, 
+     printf("Partitions: [%6d %6d] T[%6d %6d], Nv-Nb[%6d %6d]. ICut: %6d [B]\n",
+             pwgts[0], pwgts[1], tpwgts[0], tpwgts[1], graph->nvtxs, graph->nbnd, graph->mincut));
+
+  tmp = graph->adjwgtsum[idxamax(nvtxs, graph->adjwgtsum)];
+  PQueueInit(ctrl, &parts, nvtxs, tmp);
+
+  idxset(nvtxs, -1, moved);
+
+  ASSERT(ComputeCut(graph, where) == graph->mincut);
+  ASSERT(CheckBnd(graph));
+
+  /* Insert the boundary nodes of the proper partition whose size is OK in the priority queue */
+  nbnd = graph->nbnd;
+  RandomPermute(nbnd, perm, 1);
+  for (ii=0; ii<nbnd; ii++) {
+    i = perm[ii];
+    ASSERT(ed[bndind[i]] > 0 || id[bndind[i]] == 0);
+    ASSERT(bndptr[bndind[i]] != -1);
+    if (where[bndind[i]] == from && vwgt[bndind[i]] <= mindiff)
+      PQueueInsert(&parts, bndind[i], ed[bndind[i]]-id[bndind[i]]);
+  }
+
+  mincut = graph->mincut;
+  for (nswaps=0; nswaps<nvtxs; nswaps++) {
+    if ((higain = PQueueGetMax(&parts)) == -1)
+      break;
+    ASSERT(bndptr[higain] != -1);
+
+    if (pwgts[to]+vwgt[higain] > tpwgts[to])
+      break;
+
+    mincut -= (ed[higain]-id[higain]);
+    INC_DEC(pwgts[to], pwgts[from], vwgt[higain]);
+
+    where[higain] = to;
+    moved[higain] = nswaps;
+
+    IFSET(ctrl->dbglvl, DBG_MOVEINFO, 
+      printf("Moved %6d from %d. [%3d %3d] %5d [%4d %4d]\n", higain, from, ed[higain]-id[higain], vwgt[higain], mincut, pwgts[0], pwgts[1]));
+
+    /**************************************************************
+    * Update the id[i]/ed[i] values of the affected nodes
+    ***************************************************************/
+    SWAP(id[higain], ed[higain], tmp);
+    if (ed[higain] == 0 && xadj[higain] < xadj[higain+1]) 
+      BNDDelete(nbnd, bndind,  bndptr, higain);
+
+    for (j=xadj[higain]; j<xadj[higain+1]; j++) {
+      k = adjncy[j];
+      oldgain = ed[k]-id[k];
+
+      kwgt = (to == where[k] ? adjwgt[j] : -adjwgt[j]);
+      INC_DEC(id[k], ed[k], kwgt);
+
+      /* Update its boundary information and queue position */
+      if (bndptr[k] != -1) { /* If k was a boundary vertex */
+        if (ed[k] == 0) { /* Not a boundary vertex any more */
+          BNDDelete(nbnd, bndind, bndptr, k);
+          if (moved[k] == -1 && where[k] == from && vwgt[k] <= mindiff)  /* Remove it if in the queues */
+            PQueueDelete(&parts, k, oldgain);
+        }
+        else { /* If it has not been moved, update its position in the queue */
+          if (moved[k] == -1 && where[k] == from && vwgt[k] <= mindiff)
+            PQueueUpdate(&parts, k, oldgain, ed[k]-id[k]);
+        }
+      }
+      else {
+        if (ed[k] > 0) {  /* It will now become a boundary vertex */
+          BNDInsert(nbnd, bndind, bndptr, k);
+          if (moved[k] == -1 && where[k] == from && vwgt[k] <= mindiff) 
+            PQueueInsert(&parts, k, ed[k]-id[k]);
+        }
+      }
+    }
+  }
+
+  IFSET(ctrl->dbglvl, DBG_REFINE, 
+    printf("\tMinimum cut: %6d, PWGTS: [%6d %6d], NBND: %6d\n", mincut, pwgts[0], pwgts[1], nbnd));
+
+  graph->mincut = mincut;
+  graph->nbnd = nbnd;
+
+  PQueueFree(ctrl, &parts);
+
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+}
+
+
+/*************************************************************************
+* This function balances two partitions by moving the highest gain 
+* (including negative gain) vertices to the other domain.
+* It is used only when tha unbalance is due to non contigous
+* subdomains. That is, the are no boundary vertices.
+* It moves vertices from the domain that is overweight to the one that 
+* is underweight.
+**************************************************************************/
+void General2WayBalance(CtrlType *ctrl, GraphType *graph, int *tpwgts)
+{
+  int i, ii, j, k, kwgt, nvtxs, nbnd, nswaps, from, to, pass, me, tmp;
+  idxtype *xadj, *vwgt, *adjncy, *adjwgt, *where, *id, *ed, *bndptr, *bndind, *pwgts;
+  idxtype *moved, *perm;
+  PQueueType parts;
+  int higain, oldgain, mincut, mindiff;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  vwgt = graph->vwgt;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+  where = graph->where;
+  id = graph->id;
+  ed = graph->ed;
+  pwgts = graph->pwgts;
+  bndptr = graph->bndptr;
+  bndind = graph->bndind;
+
+  moved = idxwspacemalloc(ctrl, nvtxs);
+  perm = idxwspacemalloc(ctrl, nvtxs);
+
+  /* Determine from which domain you will be moving data */
+  mindiff = abs(tpwgts[0]-pwgts[0]);
+  from = (pwgts[0] < tpwgts[0] ? 1 : 0);
+  to = (from+1)%2;
+
+  IFSET(ctrl->dbglvl, DBG_REFINE, 
+     printf("Partitions: [%6d %6d] T[%6d %6d], Nv-Nb[%6d %6d]. ICut: %6d [B]\n",
+             pwgts[0], pwgts[1], tpwgts[0], tpwgts[1], graph->nvtxs, graph->nbnd, graph->mincut));
+
+  tmp = graph->adjwgtsum[idxamax(nvtxs, graph->adjwgtsum)];
+  PQueueInit(ctrl, &parts, nvtxs, tmp);
+
+  idxset(nvtxs, -1, moved);
+
+  ASSERT(ComputeCut(graph, where) == graph->mincut);
+  ASSERT(CheckBnd(graph));
+
+  /* Insert the nodes of the proper partition whose size is OK in the priority queue */
+  RandomPermute(nvtxs, perm, 1);
+  for (ii=0; ii<nvtxs; ii++) {
+    i = perm[ii];
+    if (where[i] == from && vwgt[i] <= mindiff)
+      PQueueInsert(&parts, i, ed[i]-id[i]);
+  }
+
+  mincut = graph->mincut;
+  nbnd = graph->nbnd;
+  for (nswaps=0; nswaps<nvtxs; nswaps++) {
+    if ((higain = PQueueGetMax(&parts)) == -1)
+      break;
+
+    if (pwgts[to]+vwgt[higain] > tpwgts[to])
+      break;
+
+    mincut -= (ed[higain]-id[higain]);
+    INC_DEC(pwgts[to], pwgts[from], vwgt[higain]);
+
+    where[higain] = to;
+    moved[higain] = nswaps;
+
+    IFSET(ctrl->dbglvl, DBG_MOVEINFO, 
+      printf("Moved %6d from %d. [%3d %3d] %5d [%4d %4d]\n", higain, from, ed[higain]-id[higain], vwgt[higain], mincut, pwgts[0], pwgts[1]));
+
+    /**************************************************************
+    * Update the id[i]/ed[i] values of the affected nodes
+    ***************************************************************/
+    SWAP(id[higain], ed[higain], tmp);
+    if (ed[higain] == 0 && bndptr[higain] != -1 && xadj[higain] < xadj[higain+1]) 
+      BNDDelete(nbnd, bndind,  bndptr, higain);
+    if (ed[higain] > 0 && bndptr[higain] == -1)
+      BNDInsert(nbnd, bndind,  bndptr, higain);
+
+    for (j=xadj[higain]; j<xadj[higain+1]; j++) {
+      k = adjncy[j];
+      oldgain = ed[k]-id[k];
+
+      kwgt = (to == where[k] ? adjwgt[j] : -adjwgt[j]);
+      INC_DEC(id[k], ed[k], kwgt);
+
+      /* Update the queue position */
+      if (moved[k] == -1 && where[k] == from && vwgt[k] <= mindiff)
+        PQueueUpdate(&parts, k, oldgain, ed[k]-id[k]);
+
+      /* Update its boundary information */
+      if (ed[k] == 0 && bndptr[k] != -1) 
+        BNDDelete(nbnd, bndind, bndptr, k);
+      else if (ed[k] > 0 && bndptr[k] == -1)  
+        BNDInsert(nbnd, bndind, bndptr, k);
+    }
+  }
+
+  IFSET(ctrl->dbglvl, DBG_REFINE, 
+    printf("\tMinimum cut: %6d, PWGTS: [%6d %6d], NBND: %6d\n", mincut, pwgts[0], pwgts[1], nbnd));
+
+  graph->mincut = mincut;
+  graph->nbnd = nbnd;
+
+  PQueueFree(ctrl, &parts);
+
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+}
diff --git a/contrib/Metis/bucketsort.c b/contrib/Metis/bucketsort.c
new file mode 100644
index 0000000000..316d212179
--- /dev/null
+++ b/contrib/Metis/bucketsort.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * bucketsort.c
+ *
+ * This file contains code that implement a variety of counting sorting
+ * algorithms
+ *
+ * Started 7/25/97
+ * George
+ *
+ * $Id: bucketsort.c,v 1.1 2005-09-21 17:29:37 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+
+
+/*************************************************************************
+* This function uses simple counting sort to return a permutation array
+* corresponding to the sorted order. The keys are assumed to start from
+* 0 and they are positive.  This sorting is used during matching.
+**************************************************************************/
+void BucketSortKeysInc(int n, int max, idxtype *keys, idxtype *tperm, idxtype *perm)
+{
+  int i, ii;
+  idxtype *counts;
+
+  counts = idxsmalloc(max+2, 0, "BucketSortKeysInc: counts");
+
+  for (i=0; i<n; i++)
+    counts[keys[i]]++;
+  MAKECSR(i, max+1, counts);
+
+  for (ii=0; ii<n; ii++) {
+    i = tperm[ii];
+    perm[counts[keys[i]]++] = i;
+  }
+
+  free(counts);
+}
+
diff --git a/contrib/Metis/ccgraph.c b/contrib/Metis/ccgraph.c
new file mode 100644
index 0000000000..549b4267f6
--- /dev/null
+++ b/contrib/Metis/ccgraph.c
@@ -0,0 +1,599 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * ccgraph.c
+ *
+ * This file contains the functions that create the coarse graph
+ *
+ * Started 8/11/97
+ * George
+ *
+ * $Id: ccgraph.c,v 1.1 2005-09-21 17:29:37 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+
+
+/*************************************************************************
+* This function creates the coarser graph
+**************************************************************************/
+void CreateCoarseGraph(CtrlType *ctrl, GraphType *graph, int cnvtxs, idxtype *match, idxtype *perm)
+{
+  int i, j, jj, k, kk, l, m, istart, iend, nvtxs, nedges, ncon, cnedges, v, u, mask, dovsize;
+  idxtype *xadj, *vwgt, *vsize, *adjncy, *adjwgt, *adjwgtsum, *auxadj;
+  idxtype *cmap, *htable;
+  idxtype *cxadj, *cvwgt, *cvsize, *cadjncy, *cadjwgt, *cadjwgtsum;
+  float *nvwgt, *cnvwgt;
+  GraphType *cgraph;
+
+  dovsize = (ctrl->optype == OP_KVMETIS ? 1 : 0);
+
+  mask = HTLENGTH;
+  if (cnvtxs < 8*mask || graph->nedges/graph->nvtxs > 15) { 
+    CreateCoarseGraphNoMask(ctrl, graph, cnvtxs, match, perm);
+    return;
+  }
+
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->ContractTmr));
+
+  nvtxs = graph->nvtxs;
+  ncon = graph->ncon;
+  xadj = graph->xadj;
+  vwgt = graph->vwgt;
+  vsize = graph->vsize;
+  nvwgt = graph->nvwgt;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+  adjwgtsum = graph->adjwgtsum;
+  cmap = graph->cmap;
+
+  /* Initialize the coarser graph */
+  cgraph = SetUpCoarseGraph(graph, cnvtxs, dovsize);
+  cxadj = cgraph->xadj;
+  cvwgt = cgraph->vwgt;
+  cvsize = cgraph->vsize;
+  cnvwgt = cgraph->nvwgt;
+  cadjwgtsum = cgraph->adjwgtsum;
+  cadjncy = cgraph->adjncy;
+  cadjwgt = cgraph->adjwgt;
+
+
+  iend = xadj[nvtxs];
+  auxadj = ctrl->wspace.auxcore; 
+  memcpy(auxadj, adjncy, iend*sizeof(idxtype)); 
+  for (i=0; i<iend; i++)
+    auxadj[i] = cmap[auxadj[i]];
+
+  htable = idxset(mask+1, -1, idxwspacemalloc(ctrl, mask+1)); 
+
+  cxadj[0] = cnvtxs = cnedges = 0;
+  for (i=0; i<nvtxs; i++) {
+    v = perm[i];
+    if (cmap[v] != cnvtxs) 
+      continue;
+
+    u = match[v];
+    if (ncon == 1)
+      cvwgt[cnvtxs] = vwgt[v];
+    else
+      scopy(ncon, nvwgt+v*ncon, cnvwgt+cnvtxs*ncon);
+
+    if (dovsize)
+      cvsize[cnvtxs] = vsize[v];
+
+    cadjwgtsum[cnvtxs] = adjwgtsum[v];
+    nedges = 0;
+
+    istart = xadj[v];
+    iend = xadj[v+1];
+    for (j=istart; j<iend; j++) {
+      k = auxadj[j];
+      kk = k&mask;
+      if ((m = htable[kk]) == -1) {
+        cadjncy[nedges] = k;
+        cadjwgt[nedges] = adjwgt[j];
+        htable[kk] = nedges++;
+      }
+      else if (cadjncy[m] == k) {
+        cadjwgt[m] += adjwgt[j];
+      }
+      else {
+        for (jj=0; jj<nedges; jj++) {
+          if (cadjncy[jj] == k) {
+            cadjwgt[jj] += adjwgt[j];
+            break;
+          }
+        }
+        if (jj == nedges) {
+          cadjncy[nedges] = k;
+          cadjwgt[nedges++] = adjwgt[j];
+        }
+      }
+    }
+
+    if (v != u) { 
+      if (ncon == 1)
+        cvwgt[cnvtxs] += vwgt[u];
+      else
+        saxpy(ncon, 1.0, nvwgt+u*ncon, 1, cnvwgt+cnvtxs*ncon, 1);
+
+      if (dovsize)
+        cvsize[cnvtxs] += vsize[u];
+
+      cadjwgtsum[cnvtxs] += adjwgtsum[u];
+
+      istart = xadj[u];
+      iend = xadj[u+1];
+      for (j=istart; j<iend; j++) {
+        k = auxadj[j];
+        kk = k&mask;
+        if ((m = htable[kk]) == -1) {
+          cadjncy[nedges] = k;
+          cadjwgt[nedges] = adjwgt[j];
+          htable[kk] = nedges++;
+        }
+        else if (cadjncy[m] == k) {
+          cadjwgt[m] += adjwgt[j];
+        }
+        else {
+          for (jj=0; jj<nedges; jj++) {
+            if (cadjncy[jj] == k) {
+              cadjwgt[jj] += adjwgt[j];
+              break;
+            }
+          }
+          if (jj == nedges) {
+            cadjncy[nedges] = k;
+            cadjwgt[nedges++] = adjwgt[j];
+          }
+        }
+      }
+
+      /* Remove the contracted adjacency weight */
+      jj = htable[cnvtxs&mask];
+      if (jj >= 0 && cadjncy[jj] != cnvtxs) {
+        for (jj=0; jj<nedges; jj++) {
+          if (cadjncy[jj] == cnvtxs) 
+            break;
+        }
+      }
+      if (jj >= 0 && cadjncy[jj] == cnvtxs) { /* This 2nd check is needed for non-adjacent matchings */
+        cadjwgtsum[cnvtxs] -= cadjwgt[jj];
+        cadjncy[jj] = cadjncy[--nedges];
+        cadjwgt[jj] = cadjwgt[nedges];
+      }
+    }
+
+    ASSERTP(cadjwgtsum[cnvtxs] == idxsum(nedges, cadjwgt), ("%d %d %d %d %d\n", cnvtxs, cadjwgtsum[cnvtxs], idxsum(nedges, cadjwgt), adjwgtsum[u], adjwgtsum[v]));
+
+    for (j=0; j<nedges; j++)
+      htable[cadjncy[j]&mask] = -1;  /* Zero out the htable */
+    htable[cnvtxs&mask] = -1;
+
+    cnedges += nedges;
+    cxadj[++cnvtxs] = cnedges;
+    cadjncy += nedges;
+    cadjwgt += nedges;
+  }
+
+  cgraph->nedges = cnedges;
+
+  ReAdjustMemory(graph, cgraph, dovsize);
+
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->ContractTmr));
+
+  idxwspacefree(ctrl, mask+1);
+
+}
+
+
+/*************************************************************************
+* This function creates the coarser graph
+**************************************************************************/
+void CreateCoarseGraphNoMask(CtrlType *ctrl, GraphType *graph, int cnvtxs, idxtype *match, idxtype *perm)
+{
+  int i, j, k, m, istart, iend, nvtxs, nedges, ncon, cnedges, v, u, dovsize;
+  idxtype *xadj, *vwgt, *vsize, *adjncy, *adjwgt, *adjwgtsum, *auxadj;
+  idxtype *cmap, *htable;
+  idxtype *cxadj, *cvwgt, *cvsize, *cadjncy, *cadjwgt, *cadjwgtsum;
+  float *nvwgt, *cnvwgt;
+  GraphType *cgraph;
+
+  dovsize = (ctrl->optype == OP_KVMETIS ? 1 : 0);
+
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->ContractTmr));
+
+  nvtxs = graph->nvtxs;
+  ncon = graph->ncon;
+  xadj = graph->xadj;
+  vwgt = graph->vwgt;
+  vsize = graph->vsize;
+  nvwgt = graph->nvwgt;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+  adjwgtsum = graph->adjwgtsum;
+  cmap = graph->cmap;
+
+
+  /* Initialize the coarser graph */
+  cgraph = SetUpCoarseGraph(graph, cnvtxs, dovsize);
+  cxadj = cgraph->xadj;
+  cvwgt = cgraph->vwgt;
+  cvsize = cgraph->vsize;
+  cnvwgt = cgraph->nvwgt;
+  cadjwgtsum = cgraph->adjwgtsum;
+  cadjncy = cgraph->adjncy;
+  cadjwgt = cgraph->adjwgt;
+
+
+  htable = idxset(cnvtxs, -1, idxwspacemalloc(ctrl, cnvtxs));
+
+  iend = xadj[nvtxs];
+  auxadj = ctrl->wspace.auxcore; 
+  memcpy(auxadj, adjncy, iend*sizeof(idxtype)); 
+  for (i=0; i<iend; i++)
+    auxadj[i] = cmap[auxadj[i]];
+
+  cxadj[0] = cnvtxs = cnedges = 0;
+  for (i=0; i<nvtxs; i++) {
+    v = perm[i];
+    if (cmap[v] != cnvtxs) 
+      continue;
+
+    u = match[v];
+    if (ncon == 1)
+      cvwgt[cnvtxs] = vwgt[v];
+    else
+      scopy(ncon, nvwgt+v*ncon, cnvwgt+cnvtxs*ncon);
+
+    if (dovsize)
+      cvsize[cnvtxs] = vsize[v];
+
+    cadjwgtsum[cnvtxs] = adjwgtsum[v];
+    nedges = 0;
+
+    istart = xadj[v];
+    iend = xadj[v+1];
+    for (j=istart; j<iend; j++) {
+      k = auxadj[j];
+      if ((m = htable[k]) == -1) {
+        cadjncy[nedges] = k;
+        cadjwgt[nedges] = adjwgt[j];
+        htable[k] = nedges++;
+      }
+      else {
+        cadjwgt[m] += adjwgt[j];
+      }
+    }
+
+    if (v != u) { 
+      if (ncon == 1)
+        cvwgt[cnvtxs] += vwgt[u];
+      else
+        saxpy(ncon, 1.0, nvwgt+u*ncon, 1, cnvwgt+cnvtxs*ncon, 1);
+
+      if (dovsize)
+        cvsize[cnvtxs] += vsize[u];
+
+      cadjwgtsum[cnvtxs] += adjwgtsum[u];
+
+      istart = xadj[u];
+      iend = xadj[u+1];
+      for (j=istart; j<iend; j++) {
+        k = auxadj[j];
+        if ((m = htable[k]) == -1) {
+          cadjncy[nedges] = k;
+          cadjwgt[nedges] = adjwgt[j];
+          htable[k] = nedges++;
+        }
+        else {
+          cadjwgt[m] += adjwgt[j];
+        }
+      }
+
+      /* Remove the contracted adjacency weight */
+      if ((j = htable[cnvtxs]) != -1) {
+        ASSERT(cadjncy[j] == cnvtxs);
+        cadjwgtsum[cnvtxs] -= cadjwgt[j];
+        cadjncy[j] = cadjncy[--nedges];
+        cadjwgt[j] = cadjwgt[nedges];
+        htable[cnvtxs] = -1;
+      }
+    }
+
+    ASSERTP(cadjwgtsum[cnvtxs] == idxsum(nedges, cadjwgt), ("%d %d\n", cadjwgtsum[cnvtxs], idxsum(nedges, cadjwgt)));
+
+    for (j=0; j<nedges; j++)
+      htable[cadjncy[j]] = -1;  /* Zero out the htable */
+
+    cnedges += nedges;
+    cxadj[++cnvtxs] = cnedges;
+    cadjncy += nedges;
+    cadjwgt += nedges;
+  }
+
+  cgraph->nedges = cnedges;
+
+  ReAdjustMemory(graph, cgraph, dovsize);
+
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->ContractTmr));
+
+  idxwspacefree(ctrl, cnvtxs);
+}
+
+
+/*************************************************************************
+* This function creates the coarser graph
+**************************************************************************/
+void CreateCoarseGraph_NVW(CtrlType *ctrl, GraphType *graph, int cnvtxs, idxtype *match, idxtype *perm)
+{
+  int i, j, jj, k, kk, l, m, istart, iend, nvtxs, nedges, ncon, cnedges, v, u, mask;
+  idxtype *xadj, *adjncy, *adjwgtsum, *auxadj;
+  idxtype *cmap, *htable;
+  idxtype *cxadj, *cvwgt, *cadjncy, *cadjwgt, *cadjwgtsum;
+  float *nvwgt, *cnvwgt;
+  GraphType *cgraph;
+
+
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->ContractTmr));
+
+  nvtxs = graph->nvtxs;
+  ncon = graph->ncon;
+  xadj = graph->xadj;
+  nvwgt = graph->nvwgt;
+  adjncy = graph->adjncy;
+  adjwgtsum = graph->adjwgtsum;
+  cmap = graph->cmap;
+
+  /* Initialize the coarser graph */
+  cgraph = SetUpCoarseGraph(graph, cnvtxs, 0);
+  cxadj = cgraph->xadj;
+  cvwgt = cgraph->vwgt;
+  cnvwgt = cgraph->nvwgt;
+  cadjwgtsum = cgraph->adjwgtsum;
+  cadjncy = cgraph->adjncy;
+  cadjwgt = cgraph->adjwgt;
+
+
+  iend = xadj[nvtxs];
+  auxadj = ctrl->wspace.auxcore; 
+  memcpy(auxadj, adjncy, iend*sizeof(idxtype)); 
+  for (i=0; i<iend; i++)
+    auxadj[i] = cmap[auxadj[i]];
+
+  mask = HTLENGTH;
+  htable = idxset(mask+1, -1, idxwspacemalloc(ctrl, mask+1)); 
+
+  cxadj[0] = cnvtxs = cnedges = 0;
+  for (i=0; i<nvtxs; i++) {
+    v = perm[i];
+    if (cmap[v] != cnvtxs) 
+      continue;
+
+    u = match[v];
+    cvwgt[cnvtxs] = 1;
+    cadjwgtsum[cnvtxs] = adjwgtsum[v];
+    nedges = 0;
+
+    istart = xadj[v];
+    iend = xadj[v+1];
+    for (j=istart; j<iend; j++) {
+      k = auxadj[j];
+      kk = k&mask;
+      if ((m = htable[kk]) == -1) {
+        cadjncy[nedges] = k;
+        cadjwgt[nedges] = 1;
+        htable[kk] = nedges++;
+      }
+      else if (cadjncy[m] == k) {
+        cadjwgt[m]++;
+      }
+      else {
+        for (jj=0; jj<nedges; jj++) {
+          if (cadjncy[jj] == k) {
+            cadjwgt[jj]++;
+            break;
+          }
+        }
+        if (jj == nedges) {
+          cadjncy[nedges] = k;
+          cadjwgt[nedges++] = 1;
+        }
+      }
+    }
+
+    if (v != u) { 
+      cvwgt[cnvtxs]++;
+      cadjwgtsum[cnvtxs] += adjwgtsum[u];
+
+      istart = xadj[u];
+      iend = xadj[u+1];
+      for (j=istart; j<iend; j++) {
+        k = auxadj[j];
+        kk = k&mask;
+        if ((m = htable[kk]) == -1) {
+          cadjncy[nedges] = k;
+          cadjwgt[nedges] = 1;
+          htable[kk] = nedges++;
+        }
+        else if (cadjncy[m] == k) {
+          cadjwgt[m]++;
+        }
+        else {
+          for (jj=0; jj<nedges; jj++) {
+            if (cadjncy[jj] == k) {
+              cadjwgt[jj]++;
+              break;
+            }
+          }
+          if (jj == nedges) {
+            cadjncy[nedges] = k;
+            cadjwgt[nedges++] = 1;
+          }
+        }
+      }
+
+      /* Remove the contracted adjacency weight */
+      jj = htable[cnvtxs&mask];
+      if (jj >= 0 && cadjncy[jj] != cnvtxs) {
+        for (jj=0; jj<nedges; jj++) {
+          if (cadjncy[jj] == cnvtxs) 
+            break;
+        }
+      }
+      if (jj >= 0 && cadjncy[jj] == cnvtxs) { /* This 2nd check is needed for non-adjacent matchings */
+        cadjwgtsum[cnvtxs] -= cadjwgt[jj];
+        cadjncy[jj] = cadjncy[--nedges];
+        cadjwgt[jj] = cadjwgt[nedges];
+      }
+    }
+
+    ASSERTP(cadjwgtsum[cnvtxs] == idxsum(nedges, cadjwgt), ("%d %d %d %d %d\n", cnvtxs, cadjwgtsum[cnvtxs], idxsum(nedges, cadjwgt), adjwgtsum[u], adjwgtsum[v]));
+
+    for (j=0; j<nedges; j++)
+      htable[cadjncy[j]&mask] = -1;  /* Zero out the htable */
+    htable[cnvtxs&mask] = -1;
+
+    cnedges += nedges;
+    cxadj[++cnvtxs] = cnedges;
+    cadjncy += nedges;
+    cadjwgt += nedges;
+  }
+
+  cgraph->nedges = cnedges;
+
+  ReAdjustMemory(graph, cgraph, 0);
+
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->ContractTmr));
+
+  idxwspacefree(ctrl, mask+1);
+
+}
+
+
+/*************************************************************************
+* Setup the various arrays for the coarse graph
+**************************************************************************/
+GraphType *SetUpCoarseGraph(GraphType *graph, int cnvtxs, int dovsize)
+{
+  GraphType *cgraph;
+
+  cgraph = CreateGraph();
+  cgraph->nvtxs = cnvtxs;
+  cgraph->ncon = graph->ncon;
+
+  cgraph->finer = graph;
+  graph->coarser = cgraph;
+
+
+  /* Allocate memory for the coarser graph */
+  if (graph->ncon == 1) {
+    if (dovsize) {
+      cgraph->gdata = idxmalloc(5*cnvtxs+1 + 2*graph->nedges, "SetUpCoarseGraph: gdata");
+      cgraph->xadj 		= cgraph->gdata;
+      cgraph->vwgt 		= cgraph->gdata + cnvtxs+1;
+      cgraph->vsize 		= cgraph->gdata + 2*cnvtxs+1;
+      cgraph->adjwgtsum 	= cgraph->gdata + 3*cnvtxs+1;
+      cgraph->cmap 		= cgraph->gdata + 4*cnvtxs+1;
+      cgraph->adjncy 		= cgraph->gdata + 5*cnvtxs+1;
+      cgraph->adjwgt 		= cgraph->gdata + 5*cnvtxs+1 + graph->nedges;
+    }
+    else {
+      cgraph->gdata = idxmalloc(4*cnvtxs+1 + 2*graph->nedges, "SetUpCoarseGraph: gdata");
+      cgraph->xadj 		= cgraph->gdata;
+      cgraph->vwgt 		= cgraph->gdata + cnvtxs+1;
+      cgraph->adjwgtsum 	= cgraph->gdata + 2*cnvtxs+1;
+      cgraph->cmap 		= cgraph->gdata + 3*cnvtxs+1;
+      cgraph->adjncy 		= cgraph->gdata + 4*cnvtxs+1;
+      cgraph->adjwgt 		= cgraph->gdata + 4*cnvtxs+1 + graph->nedges;
+    }
+  }
+  else {
+    if (dovsize) {
+      cgraph->gdata = idxmalloc(4*cnvtxs+1 + 2*graph->nedges, "SetUpCoarseGraph: gdata");
+      cgraph->xadj 		= cgraph->gdata;
+      cgraph->vsize 		= cgraph->gdata + cnvtxs+1;
+      cgraph->adjwgtsum 	= cgraph->gdata + 2*cnvtxs+1;
+      cgraph->cmap 		= cgraph->gdata + 3*cnvtxs+1;
+      cgraph->adjncy 		= cgraph->gdata + 4*cnvtxs+1;
+      cgraph->adjwgt 		= cgraph->gdata + 4*cnvtxs+1 + graph->nedges;
+    }
+    else {
+      cgraph->gdata = idxmalloc(3*cnvtxs+1 + 2*graph->nedges, "SetUpCoarseGraph: gdata");
+      cgraph->xadj 		= cgraph->gdata;
+      cgraph->adjwgtsum 	= cgraph->gdata + cnvtxs+1;
+      cgraph->cmap 		= cgraph->gdata + 2*cnvtxs+1;
+      cgraph->adjncy 		= cgraph->gdata + 3*cnvtxs+1;
+      cgraph->adjwgt 		= cgraph->gdata + 3*cnvtxs+1 + graph->nedges;
+    }
+
+    cgraph->nvwgt 	= fmalloc(graph->ncon*cnvtxs, "SetUpCoarseGraph: nvwgt");
+  }
+
+  return cgraph;
+}
+
+
+/*************************************************************************
+* This function re-adjusts the amount of memory that was allocated if
+* it will lead to significant savings
+**************************************************************************/
+void ReAdjustMemory(GraphType *graph, GraphType *cgraph, int dovsize) 
+{
+
+  if (cgraph->nedges > 100000 && graph->nedges < 0.7*graph->nedges) {
+    idxcopy(cgraph->nedges, cgraph->adjwgt, cgraph->adjncy+cgraph->nedges);
+
+    if (graph->ncon == 1) {
+      if (dovsize) {
+        cgraph->gdata = realloc(cgraph->gdata, (5*cgraph->nvtxs+1 + 2*cgraph->nedges)*sizeof(idxtype));
+
+        /* Do this, in case everything was copied into new space */
+        cgraph->xadj 		= cgraph->gdata;
+        cgraph->vwgt 		= cgraph->gdata + cgraph->nvtxs+1;
+        cgraph->vsize 		= cgraph->gdata + 2*cgraph->nvtxs+1;
+        cgraph->adjwgtsum	= cgraph->gdata + 3*cgraph->nvtxs+1;
+        cgraph->cmap 		= cgraph->gdata + 4*cgraph->nvtxs+1;
+        cgraph->adjncy 		= cgraph->gdata + 5*cgraph->nvtxs+1;
+        cgraph->adjwgt 		= cgraph->gdata + 5*cgraph->nvtxs+1 + cgraph->nedges;
+      }
+      else {
+        cgraph->gdata = realloc(cgraph->gdata, (4*cgraph->nvtxs+1 + 2*cgraph->nedges)*sizeof(idxtype));
+
+        /* Do this, in case everything was copied into new space */
+        cgraph->xadj 	= cgraph->gdata;
+        cgraph->vwgt 	= cgraph->gdata + cgraph->nvtxs+1;
+        cgraph->adjwgtsum	= cgraph->gdata + 2*cgraph->nvtxs+1;
+        cgraph->cmap 	= cgraph->gdata + 3*cgraph->nvtxs+1;
+        cgraph->adjncy 	= cgraph->gdata + 4*cgraph->nvtxs+1;
+        cgraph->adjwgt 	= cgraph->gdata + 4*cgraph->nvtxs+1 + cgraph->nedges;
+      }
+    }
+    else {
+      if (dovsize) {
+        cgraph->gdata = realloc(cgraph->gdata, (4*cgraph->nvtxs+1 + 2*cgraph->nedges)*sizeof(idxtype));
+
+        /* Do this, in case everything was copied into new space */
+        cgraph->xadj 		= cgraph->gdata;
+        cgraph->vsize		= cgraph->gdata + cgraph->nvtxs+1;
+        cgraph->adjwgtsum	= cgraph->gdata + 2*cgraph->nvtxs+1;
+        cgraph->cmap 		= cgraph->gdata + 3*cgraph->nvtxs+1;
+        cgraph->adjncy 		= cgraph->gdata + 4*cgraph->nvtxs+1;
+        cgraph->adjwgt 		= cgraph->gdata + 4*cgraph->nvtxs+1 + cgraph->nedges;
+      }
+      else {
+        cgraph->gdata = realloc(cgraph->gdata, (3*cgraph->nvtxs+1 + 2*cgraph->nedges)*sizeof(idxtype));
+
+        /* Do this, in case everything was copied into new space */
+        cgraph->xadj 		= cgraph->gdata;
+        cgraph->adjwgtsum	= cgraph->gdata + cgraph->nvtxs+1;
+        cgraph->cmap 		= cgraph->gdata + 2*cgraph->nvtxs+1;
+        cgraph->adjncy 		= cgraph->gdata + 3*cgraph->nvtxs+1;
+        cgraph->adjwgt 		= cgraph->gdata + 3*cgraph->nvtxs+1 + cgraph->nedges;
+      }
+    }
+  }
+
+}
diff --git a/contrib/Metis/coarsen.c b/contrib/Metis/coarsen.c
new file mode 100644
index 0000000000..40c25df00e
--- /dev/null
+++ b/contrib/Metis/coarsen.c
@@ -0,0 +1,83 @@
+/*
+ * coarsen.c
+ *
+ * This file contains the driving routines for the coarsening process 
+ *
+ * Started 7/23/97
+ * George
+ *
+ * $Id: coarsen.c,v 1.1 2005-09-21 17:29:37 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function takes a graph and creates a sequence of coarser graphs
+**************************************************************************/
+GraphType *Coarsen2Way(CtrlType *ctrl, GraphType *graph)
+{
+  int clevel;
+  GraphType *cgraph;
+
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->CoarsenTmr));
+
+  cgraph = graph;
+
+  /* The following is ahack to allow the multiple bisections to go through with correct
+     coarsening */
+  if (ctrl->CType > 20) {
+    clevel = 1;
+    ctrl->CType -= 20;
+  }
+  else
+    clevel = 0;
+
+  do {
+    IFSET(ctrl->dbglvl, DBG_COARSEN, printf("%6d %7d [%d] [%d %d]\n",
+          cgraph->nvtxs, cgraph->nedges, ctrl->CoarsenTo, ctrl->maxvwgt, 
+          (cgraph->vwgt ? idxsum(cgraph->nvtxs, cgraph->vwgt) : cgraph->nvtxs)));
+
+    if (cgraph->adjwgt) {
+      switch (ctrl->CType) {
+        case MATCH_RM:
+          Match_RM(ctrl, cgraph);
+          break;
+        case MATCH_HEM:
+          if (clevel < 1)
+            Match_RM(ctrl, cgraph);
+          else
+            Match_HEM(ctrl, cgraph);
+          break;
+        case MATCH_SHEM:
+          if (clevel < 1)
+            Match_RM(ctrl, cgraph);
+          else
+            Match_SHEM(ctrl, cgraph);
+          break;
+        case MATCH_SHEMKWAY:
+          Match_SHEM(ctrl, cgraph);
+          break;
+        default:
+          errexit("Unknown CType: %d\n", ctrl->CType);
+      }
+    }
+    else {
+      Match_RM_NVW(ctrl, cgraph);
+    }
+
+    cgraph = cgraph->coarser;
+    clevel++;
+
+  } while (cgraph->nvtxs > ctrl->CoarsenTo && cgraph->nvtxs < COARSEN_FRACTION2*cgraph->finer->nvtxs && cgraph->nedges > cgraph->nvtxs/2); 
+
+  IFSET(ctrl->dbglvl, DBG_COARSEN, printf("%6d %7d [%d] [%d %d]\n",
+        cgraph->nvtxs, cgraph->nedges, ctrl->CoarsenTo, ctrl->maxvwgt, 
+        (cgraph->vwgt ? idxsum(cgraph->nvtxs, cgraph->vwgt) : cgraph->nvtxs)));
+
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->CoarsenTmr));
+
+  return cgraph;
+}
+
diff --git a/contrib/Metis/compress.c b/contrib/Metis/compress.c
new file mode 100644
index 0000000000..9798919a6a
--- /dev/null
+++ b/contrib/Metis/compress.c
@@ -0,0 +1,256 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * compress.c
+ *
+ * This file contains code for compressing nodes with identical adjacency
+ * structure and for prunning dense columns
+ *
+ * Started 9/17/97
+ * George
+ *
+ * $Id: compress.c,v 1.1 2005-09-21 17:29:37 geuzaine Exp $
+ */
+
+#include <metis.h>
+
+/*************************************************************************
+* This function compresses a graph by merging identical vertices
+* The compression should lead to at least 10% reduction.
+**************************************************************************/
+void CompressGraph(CtrlType *ctrl, GraphType *graph, int nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *cptr, idxtype *cind)
+{
+  int i, ii, iii, j, jj, k, l, cnvtxs, cnedges;
+  idxtype *cxadj, *cadjncy, *cvwgt, *mark, *map;
+  KeyValueType *keys;
+
+  mark = idxsmalloc(nvtxs, -1, "CompressGraph: mark");
+  map = idxsmalloc(nvtxs, -1, "CompressGraph: map");
+  keys = (KeyValueType *)GKmalloc(nvtxs*sizeof(KeyValueType), "CompressGraph: keys");
+
+  /* Compute a key for each adjacency list */
+  for (i=0; i<nvtxs; i++) {
+    k = 0;
+    for (j=xadj[i]; j<xadj[i+1]; j++)
+      k += adjncy[j];
+    keys[i].key = k+i; /* Add the diagonal entry as well */
+    keys[i].val = i;
+  }
+
+  ikeysort(nvtxs, keys);
+
+  l = cptr[0] = 0;
+  for (cnvtxs=i=0; i<nvtxs; i++) {
+    ii = keys[i].val;
+    if (map[ii] == -1) { 
+      mark[ii] = i;  /* Add the diagonal entry */
+      for (j=xadj[ii]; j<xadj[ii+1]; j++) 
+        mark[adjncy[j]] = i;
+
+      cind[l++] = ii;
+      map[ii] = cnvtxs;
+
+      for (j=i+1; j<nvtxs; j++) {
+        iii = keys[j].val;
+
+        if (keys[i].key != keys[j].key || xadj[ii+1]-xadj[ii] != xadj[iii+1]-xadj[iii])
+          break; /* Break if keys or degrees are different */
+
+        if (map[iii] == -1) { /* Do a comparison if iii has not been mapped */ 
+          for (jj=xadj[iii]; jj<xadj[iii+1]; jj++) {
+            if (mark[adjncy[jj]] != i)
+              break;
+          }
+
+          if (jj == xadj[iii+1]) { /* Identical adjacency structure */
+            map[iii] = cnvtxs;
+            cind[l++] = iii;
+          }
+        }
+      }
+
+      cptr[++cnvtxs] = l;
+    }
+  }
+
+  /* printf("Original: %6d, Compressed: %6d\n", nvtxs, cnvtxs); */
+
+
+  InitGraph(graph);
+
+  if (cnvtxs >= COMPRESSION_FRACTION*nvtxs) {
+    graph->nvtxs = nvtxs;
+    graph->nedges = xadj[nvtxs];
+    graph->ncon = 1;
+    graph->xadj = xadj;
+    graph->adjncy = adjncy;
+
+    graph->gdata = idxmalloc(3*nvtxs+graph->nedges, "CompressGraph: gdata");
+    graph->vwgt    	= graph->gdata;
+    graph->adjwgtsum    = graph->gdata+nvtxs;
+    graph->cmap		= graph->gdata+2*nvtxs;
+    graph->adjwgt	= graph->gdata+3*nvtxs;
+
+    idxset(nvtxs, 1, graph->vwgt);
+    idxset(graph->nedges, 1, graph->adjwgt);
+    for (i=0; i<nvtxs; i++)
+      graph->adjwgtsum[i] = xadj[i+1]-xadj[i];
+
+    graph->label = idxmalloc(nvtxs, "CompressGraph: label");
+    for (i=0; i<nvtxs; i++)
+      graph->label[i] = i;
+  }
+  else { /* Ok, form the compressed graph  */
+    cnedges = 0;
+    for (i=0; i<cnvtxs; i++) {
+      ii = cind[cptr[i]];
+      cnedges += xadj[ii+1]-xadj[ii];
+    }
+
+    /* Allocate memory for the compressed graph*/
+    graph->gdata = idxmalloc(4*cnvtxs+1 + 2*cnedges, "CompressGraph: gdata");
+    cxadj = graph->xadj		= graph->gdata;
+    cvwgt = graph->vwgt         = graph->gdata + cnvtxs+1;
+    graph->adjwgtsum        	= graph->gdata + 2*cnvtxs+1;
+    graph->cmap                 = graph->gdata + 3*cnvtxs+1;
+    cadjncy = graph->adjncy     = graph->gdata + 4*cnvtxs+1;
+    graph->adjwgt            	= graph->gdata + 4*cnvtxs+1 + cnedges;
+
+    /* Now go and compress the graph */
+    idxset(nvtxs, -1, mark);
+    l = cxadj[0] = 0;
+    for (i=0; i<cnvtxs; i++) {
+      cvwgt[i] = cptr[i+1]-cptr[i];
+      mark[i] = i;  /* Remove any dioganal entries in the compressed graph */
+      for (j=cptr[i]; j<cptr[i+1]; j++) {
+        ii = cind[j];
+        for (jj=xadj[ii]; jj<xadj[ii+1]; jj++) {
+          k = map[adjncy[jj]];
+          if (mark[k] != i) 
+            cadjncy[l++] = k;
+          mark[k] = i;
+        }
+      }
+      cxadj[i+1] = l;
+    }
+
+    graph->nvtxs = cnvtxs;
+    graph->nedges = l;
+    graph->ncon = 1;
+
+    idxset(graph->nedges, 1, graph->adjwgt);
+    for (i=0; i<cnvtxs; i++)
+      graph->adjwgtsum[i] = cxadj[i+1]-cxadj[i];
+
+    graph->label = idxmalloc(cnvtxs, "CompressGraph: label");
+    for (i=0; i<cnvtxs; i++)
+      graph->label[i] = i;
+
+  }
+
+  GKfree(&keys, &map, &mark, LTERM);
+}
+
+
+
+/*************************************************************************
+* This function prunes all the vertices in a graph with degree greater 
+* than factor*average
+**************************************************************************/
+void PruneGraph(CtrlType *ctrl, GraphType *graph, int nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *iperm, float factor)
+{
+  int i, j, k, l, nlarge, pnvtxs, pnedges;
+  idxtype *pxadj, *padjncy, *padjwgt, *pvwgt;
+  idxtype *perm;
+
+  perm = idxmalloc(nvtxs, "PruneGraph: perm");
+
+  factor = factor*xadj[nvtxs]/nvtxs;
+
+  pnvtxs = pnedges = nlarge = 0;
+  for (i=0; i<nvtxs; i++) {
+    if (xadj[i+1]-xadj[i] < factor) {
+      perm[i] = pnvtxs;
+      iperm[pnvtxs++] = i;
+      pnedges += xadj[i+1]-xadj[i];
+    }
+    else {
+      perm[i] = nvtxs - ++nlarge;
+      iperm[nvtxs-nlarge] = i;
+    }
+  }
+
+  /* printf("Pruned %d vertices\n", nlarge); */
+
+  InitGraph(graph);
+
+  if (nlarge == 0) { /* No prunning */
+    graph->nvtxs = nvtxs;
+    graph->nedges = xadj[nvtxs];
+    graph->ncon = 1;
+    graph->xadj = xadj;
+    graph->adjncy = adjncy;
+
+    graph->gdata = idxmalloc(3*nvtxs+graph->nedges, "CompressGraph: gdata");
+    graph->vwgt    	= graph->gdata;
+    graph->adjwgtsum    = graph->gdata+nvtxs;
+    graph->cmap		= graph->gdata+2*nvtxs;
+    graph->adjwgt	= graph->gdata+3*nvtxs;
+
+    idxset(nvtxs, 1, graph->vwgt);
+    idxset(graph->nedges, 1, graph->adjwgt);
+    for (i=0; i<nvtxs; i++)
+      graph->adjwgtsum[i] = xadj[i+1]-xadj[i];
+
+    graph->label = idxmalloc(nvtxs, "CompressGraph: label");
+    for (i=0; i<nvtxs; i++)
+      graph->label[i] = i;
+  }
+  else { /* Prune the graph */
+    /* Allocate memory for the compressed graph*/
+    graph->gdata = idxmalloc(4*pnvtxs+1 + 2*pnedges, "PruneGraph: gdata");
+    pxadj = graph->xadj		= graph->gdata;
+    graph->vwgt         	= graph->gdata + pnvtxs+1;
+    graph->adjwgtsum        	= graph->gdata + 2*pnvtxs+1;
+    graph->cmap                 = graph->gdata + 3*pnvtxs+1;
+    padjncy = graph->adjncy     = graph->gdata + 4*pnvtxs+1;
+    graph->adjwgt            	= graph->gdata + 4*pnvtxs+1 + pnedges;
+
+    pxadj[0] = pnedges = l = 0;
+    for (i=0; i<nvtxs; i++) {
+      if (xadj[i+1]-xadj[i] < factor) {
+        for (j=xadj[i]; j<xadj[i+1]; j++) {
+          k = perm[adjncy[j]];
+          if (k < pnvtxs) 
+            padjncy[pnedges++] = k;
+        }
+        pxadj[++l] = pnedges;
+      }
+    }
+
+    graph->nvtxs = pnvtxs;
+    graph->nedges = pnedges;
+    graph->ncon = 1;
+
+    idxset(pnvtxs, 1, graph->vwgt);
+    idxset(pnedges, 1, graph->adjwgt);
+    for (i=0; i<pnvtxs; i++)
+      graph->adjwgtsum[i] = pxadj[i+1]-pxadj[i];
+
+    graph->label = idxmalloc(pnvtxs, "CompressGraph: label");
+    for (i=0; i<pnvtxs; i++)
+      graph->label[i] = i;
+  }
+
+  free(perm);
+
+}
+
+
+
+
+
+
+
+
+
diff --git a/contrib/Metis/debug.c b/contrib/Metis/debug.c
new file mode 100644
index 0000000000..3a30fe9d78
--- /dev/null
+++ b/contrib/Metis/debug.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * debug.c
+ *
+ * This file contains code that performs self debuging
+ *
+ * Started 7/24/97
+ * George
+ *
+ * $Id: debug.c,v 1.1 2005-09-21 17:29:37 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+/*************************************************************************
+* This function computes the cut given the graph and a where vector
+**************************************************************************/
+int ComputeCut(GraphType *graph, idxtype *where)
+{
+  int i, j, cut;
+
+  if (graph->adjwgt == NULL) {
+    for (cut=0, i=0; i<graph->nvtxs; i++) {
+      for (j=graph->xadj[i]; j<graph->xadj[i+1]; j++)
+        if (where[i] != where[graph->adjncy[j]])
+          cut++;
+    }
+  }
+  else {
+    for (cut=0, i=0; i<graph->nvtxs; i++) {
+      for (j=graph->xadj[i]; j<graph->xadj[i+1]; j++)
+        if (where[i] != where[graph->adjncy[j]])
+          cut += graph->adjwgt[j];
+    }
+  }
+
+  return cut/2;
+}
+
+
+/*************************************************************************
+* This function checks whether or not the boundary information is correct
+**************************************************************************/
+int CheckBnd(GraphType *graph) 
+{
+  int i, j, nvtxs, nbnd;
+  idxtype *xadj, *adjncy, *where, *bndptr, *bndind;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  where = graph->where;
+  bndptr = graph->bndptr;
+  bndind = graph->bndind;
+
+  for (nbnd=0, i=0; i<nvtxs; i++) {
+    if (xadj[i+1]-xadj[i] == 0)
+      nbnd++;   /* Islands are considered to be boundary vertices */
+
+    for (j=xadj[i]; j<xadj[i+1]; j++) {
+      if (where[i] != where[adjncy[j]]) {
+        nbnd++;
+        ASSERT(bndptr[i] != -1);
+        ASSERT(bndind[bndptr[i]] == i);
+        break;
+      }
+    }
+  }
+
+  ASSERTP(nbnd == graph->nbnd, ("%d %d\n", nbnd, graph->nbnd));
+
+  return 1;
+}
+
+
+
+/*************************************************************************
+* This function checks whether or not the boundary information is correct
+**************************************************************************/
+int CheckBnd2(GraphType *graph) 
+{
+  int i, j, nvtxs, nbnd, id, ed;
+  idxtype *xadj, *adjncy, *where, *bndptr, *bndind;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  where = graph->where;
+  bndptr = graph->bndptr;
+  bndind = graph->bndind;
+
+  for (nbnd=0, i=0; i<nvtxs; i++) {
+    id = ed = 0;
+    for (j=xadj[i]; j<xadj[i+1]; j++) {
+      if (where[i] != where[adjncy[j]]) 
+        ed += graph->adjwgt[j];
+      else
+        id += graph->adjwgt[j];
+    }
+    if (ed - id >= 0 && xadj[i] < xadj[i+1]) {
+      nbnd++;
+      ASSERTP(bndptr[i] != -1, ("%d %d %d\n", i, id, ed));
+      ASSERT(bndind[bndptr[i]] == i);
+    }
+  }
+
+  ASSERTP(nbnd == graph->nbnd, ("%d %d\n", nbnd, graph->nbnd));
+
+  return 1;
+}
+
+/*************************************************************************
+* This function checks whether or not the boundary information is correct
+**************************************************************************/
+int CheckNodeBnd(GraphType *graph, int onbnd) 
+{
+  int i, j, nvtxs, nbnd;
+  idxtype *xadj, *adjncy, *where, *bndptr, *bndind;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  where = graph->where;
+  bndptr = graph->bndptr;
+  bndind = graph->bndind;
+
+  for (nbnd=0, i=0; i<nvtxs; i++) {
+    if (where[i] == 2) 
+      nbnd++;   
+  }
+
+  ASSERTP(nbnd == onbnd, ("%d %d\n", nbnd, onbnd));
+
+  for (i=0; i<nvtxs; i++) {
+    if (where[i] != 2) {
+      ASSERTP(bndptr[i] == -1, ("%d %d\n", i, bndptr[i]));
+    }
+    else {
+      ASSERTP(bndptr[i] != -1, ("%d %d\n", i, bndptr[i]));
+    }
+  }
+
+  return 1;
+}
+
+
+
+/*************************************************************************
+* This function checks whether or not the rinfo of a vertex is consistent
+**************************************************************************/
+int CheckRInfo(RInfoType *rinfo)
+{
+  int i, j;
+
+  for (i=0; i<rinfo->ndegrees; i++) {
+    for (j=i+1; j<rinfo->ndegrees; j++)
+      ASSERTP(rinfo->edegrees[i].pid != rinfo->edegrees[j].pid, ("%d %d %d %d\n", i, j, rinfo->edegrees[i].pid, rinfo->edegrees[j].pid));
+  }
+
+  return 1;
+}
+
+
+
+/*************************************************************************
+* This function checks the correctness of the NodeFM data structures
+**************************************************************************/
+int CheckNodePartitionParams(GraphType *graph)
+{
+  int i, j, k, l, nvtxs, me, other;
+  idxtype *xadj, *adjncy, *adjwgt, *vwgt, *where;
+  idxtype edegrees[2], pwgts[3];
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  vwgt = graph->vwgt;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  where = graph->where;
+
+  /*------------------------------------------------------------
+  / Compute now the separator external degrees
+  /------------------------------------------------------------*/
+  pwgts[0] = pwgts[1] = pwgts[2] = 0;
+  for (i=0; i<nvtxs; i++) {
+    me = where[i];
+    pwgts[me] += vwgt[i];
+
+    if (me == 2) { /* If it is on the separator do some computations */
+      edegrees[0] = edegrees[1] = 0;
+
+      for (j=xadj[i]; j<xadj[i+1]; j++) {
+        other = where[adjncy[j]];
+        if (other != 2)
+          edegrees[other] += vwgt[adjncy[j]];
+      }
+      if (edegrees[0] != graph->nrinfo[i].edegrees[0] || edegrees[1] != graph->nrinfo[i].edegrees[1]) {
+        printf("Something wrong with edegrees: %d %d %d %d %d\n", i, edegrees[0], edegrees[1], graph->nrinfo[i].edegrees[0], graph->nrinfo[i].edegrees[1]);
+        return 0;
+      }
+    }
+  }
+
+  if (pwgts[0] != graph->pwgts[0] || pwgts[1] != graph->pwgts[1] || pwgts[2] != graph->pwgts[2])
+    printf("Something wrong with part-weights: %d %d %d %d %d %d\n", pwgts[0], pwgts[1], pwgts[2], graph->pwgts[0], graph->pwgts[1], graph->pwgts[2]);
+
+  return 1;
+}
+
+
+/*************************************************************************
+* This function checks if the separator is indeed a separator
+**************************************************************************/
+int IsSeparable(GraphType *graph)
+{
+  int i, j, nvtxs, other;
+  idxtype *xadj, *adjncy, *where;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  where = graph->where;
+
+  for (i=0; i<nvtxs; i++) {
+    if (where[i] == 2)
+      continue;
+    other = (where[i]+1)%2;
+    for (j=xadj[i]; j<xadj[i+1]; j++) {
+      ASSERTP(where[adjncy[j]] != other, ("%d %d %d %d %d %d\n", i, where[i], adjncy[j], where[adjncy[j]], xadj[i+1]-xadj[i], xadj[adjncy[j]+1]-xadj[adjncy[j]]));
+    }
+  }
+
+  return 1;
+}
+
+
diff --git a/contrib/Metis/defs.h b/contrib/Metis/defs.h
new file mode 100644
index 0000000000..472e2db06a
--- /dev/null
+++ b/contrib/Metis/defs.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * defs.h
+ *
+ * This file contains constant definitions
+ *
+ * Started 8/27/94
+ * George
+ *
+ * $Id: defs.h,v 1.1 2005-09-21 17:29:37 geuzaine Exp $
+ *
+ */
+
+#define METISTITLE              "  METIS 4.0.1 Copyright 1998, Regents of the University of Minnesota\n\n"
+#define MAXLINE			1280000
+
+#define LTERM			(void **) 0	/* List terminator for GKfree() */
+
+#define MAXNCON			16		/* The maximum number of constrains */
+#define MAXNOBJ			16		/* The maximum number of objectives */
+
+#define PLUS_GAINSPAN   	500             /* Parameters for FM buckets */
+#define NEG_GAINSPAN    	500
+
+#define HTLENGTH		((1<<11)-1)
+
+/* Meaning of various options[] parameters */
+#define OPTION_PTYPE		0
+#define OPTION_CTYPE		1
+#define OPTION_ITYPE		2
+#define OPTION_RTYPE		3
+#define OPTION_DBGLVL		4
+#define OPTION_OFLAGS		5
+#define OPTION_PFACTOR		6
+#define OPTION_NSEPS		7
+
+#define OFLAG_COMPRESS		1	/* Try to compress the graph */
+#define OFLAG_CCMP		2	/* Find and order connected components */
+
+
+/* Default options for PMETIS */
+#define PMETIS_CTYPE		MATCH_SHEM
+#define PMETIS_ITYPE		IPART_GGPKL
+#define PMETIS_RTYPE		RTYPE_FM
+#define PMETIS_DBGLVL		0
+
+/* Default options for KMETIS */
+#define KMETIS_CTYPE		MATCH_SHEM
+#define KMETIS_ITYPE		IPART_PMETIS
+#define KMETIS_RTYPE		RTYPE_KWAYRANDOM_MCONN
+#define KMETIS_DBGLVL		0
+
+/* Default options for OEMETIS */
+#define OEMETIS_CTYPE		MATCH_SHEM
+#define OEMETIS_ITYPE		IPART_GGPKL
+#define OEMETIS_RTYPE		RTYPE_FM
+#define OEMETIS_DBGLVL		0
+
+/* Default options for ONMETIS */
+#define ONMETIS_CTYPE		MATCH_SHEM
+#define ONMETIS_ITYPE		IPART_GGPKL
+#define ONMETIS_RTYPE		RTYPE_SEP1SIDED
+#define ONMETIS_DBGLVL		0
+#define ONMETIS_OFLAGS		OFLAG_COMPRESS
+#define ONMETIS_PFACTOR		-1
+#define ONMETIS_NSEPS		1
+
+/* Default options for McPMETIS */
+#define McPMETIS_CTYPE		MATCH_SHEBM_ONENORM
+#define McPMETIS_ITYPE		IPART_RANDOM
+#define McPMETIS_RTYPE		RTYPE_FM
+#define McPMETIS_DBGLVL		0
+
+/* Default options for McKMETIS */
+#define McKMETIS_CTYPE		MATCH_SHEBM_ONENORM
+#define McKMETIS_ITYPE		IPART_McHPMETIS
+#define McKMETIS_RTYPE		RTYPE_KWAYRANDOM
+#define McKMETIS_DBGLVL		0
+
+/* Default options for KVMETIS */
+#define KVMETIS_CTYPE		MATCH_SHEM
+#define KVMETIS_ITYPE		IPART_PMETIS
+#define KVMETIS_RTYPE		RTYPE_KWAYRANDOM
+#define KVMETIS_DBGLVL		0
+
+
+/* Operations supported by stand-alone code */
+#define OP_PMETIS		1
+#define OP_KMETIS		2
+#define OP_OEMETIS		3
+#define OP_ONMETIS		4
+#define OP_ONWMETIS		5
+#define OP_KVMETIS		6
+
+
+/* Matching Schemes */
+#define MATCH_RM		1
+#define MATCH_HEM		2
+#define MATCH_SHEM		3
+#define MATCH_SHEMKWAY		4
+#define MATCH_SHEBM_ONENORM	5
+#define MATCH_SHEBM_INFNORM	6
+#define MATCH_SBHEM_ONENORM	7
+#define MATCH_SBHEM_INFNORM	8
+
+/* Initial partitioning schemes for PMETIS and ONMETIS */
+#define IPART_GGPKL		1
+#define IPART_GGPKLNODE		2
+#define IPART_RANDOM		2
+
+/* Refinement schemes for PMETIS */
+#define RTYPE_FM		1
+
+/* Initial partitioning schemes for KMETIS */
+#define IPART_PMETIS		1
+
+/* Refinement schemes for KMETIS */
+#define RTYPE_KWAYRANDOM	1
+#define RTYPE_KWAYGREEDY	2
+#define RTYPE_KWAYRANDOM_MCONN	3
+
+/* Refinement schemes for ONMETIS */
+#define RTYPE_SEP2SIDED		1
+#define RTYPE_SEP1SIDED		2
+
+/* Initial Partitioning Schemes for McKMETIS */
+#define IPART_McPMETIS		1   	/* Simple McPMETIS */
+#define IPART_McHPMETIS		2	/* horizontally relaxed McPMETIS */
+
+#define UNMATCHED		-1
+
+#define HTABLE_EMPTY    	-1
+
+#define NGR_PASSES		4	/* Number of greedy refinement passes */
+#define NLGR_PASSES		5	/* Number of GR refinement during IPartition */
+
+#define LARGENIPARTS		8	/* Number of random initial partitions */
+#define SMALLNIPARTS		3	/* Number of random initial partitions */
+
+#define COARSEN_FRACTION	0.75	/* Node reduction between succesive coarsening levels */
+#define COARSEN_FRACTION2	0.90	/* Node reduction between succesive coarsening levels */
+#define UNBALANCE_FRACTION		1.05
+
+#define COMPRESSION_FRACTION		0.85
+
+#define ORDER_UNBALANCE_FRACTION	1.10
+
+#define MMDSWITCH		200
+
+#define HORIZONTAL_IMBALANCE		1.05
+
+/* Debug Levels */
+#define DBG_TIME	1		/* Perform timing analysis */
+#define DBG_OUTPUT	2
+#define DBG_COARSEN   	4		/* Show the coarsening progress */
+#define DBG_REFINE	8		/* Show info on communication during folding */
+#define DBG_IPART	16		/* Show info on initial partition */
+#define DBG_MOVEINFO	32		/* Show info on communication during folding */
+#define DBG_KWAYPINFO	64		/* Show info on communication during folding */
+#define DBG_SEPINFO	128		/* Show info on communication during folding */
diff --git a/contrib/Metis/estmem.c b/contrib/Metis/estmem.c
new file mode 100644
index 0000000000..852d5ac5e0
--- /dev/null
+++ b/contrib/Metis/estmem.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * estmem.c
+ *
+ * This file contains code for estimating the amount of memory required by
+ * the various routines in METIS
+ *
+ * Started 11/4/97
+ * George
+ *
+ * $Id: estmem.c,v 1.1 2005-09-21 17:29:37 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+/*************************************************************************
+* This function computes how much memory will be required by the various
+* routines in METIS
+**************************************************************************/
+void METIS_EstimateMemory(int *nvtxs, idxtype *xadj, idxtype *adjncy, int *numflag, int *optype, int *nbytes)
+{
+  int i, j, k, nedges, nlevels;
+  float vfraction, efraction, vmult, emult;
+  int coresize, gdata, rdata;
+
+  if (*numflag == 1)
+    Change2CNumbering(*nvtxs, xadj, adjncy);
+
+  nedges = xadj[*nvtxs];
+
+  InitRandom(-1);
+  EstimateCFraction(*nvtxs, xadj, adjncy, &vfraction, &efraction);
+
+  /* Estimate the amount of memory for coresize */
+  if (*optype == 2) 
+    coresize = nedges;
+  else
+    coresize = 0;
+  coresize += nedges + 11*(*nvtxs) + 4*1024 + 2*(NEG_GAINSPAN+PLUS_GAINSPAN+1)*(sizeof(ListNodeType *)/sizeof(idxtype));
+  coresize += 2*(*nvtxs);  /* add some more fore other vectors */
+
+  gdata = nedges;   /* Assume that the user does not pass weights */
+
+  nlevels = (int)(log(100.0/(*nvtxs))/log(vfraction) + .5);
+  vmult = 0.5 + (1.0 - pow(vfraction, nlevels))/(1.0 - vfraction);
+  emult = 1.0 + (1.0 - pow(efraction, nlevels+1))/(1.0 - efraction);
+
+  gdata += vmult*4*(*nvtxs) + emult*2*nedges;
+  if ((vmult-1.0)*4*(*nvtxs) + (emult-1.0)*2*nedges < 5*(*nvtxs))
+    rdata = 0;
+  else
+    rdata = 5*(*nvtxs);
+
+  *nbytes = sizeof(idxtype)*(coresize+gdata+rdata+(*nvtxs));
+
+  if (*numflag == 1)
+    Change2FNumbering2(*nvtxs, xadj, adjncy);
+}
+  
+
+/*************************************************************************
+* This function finds a matching using the HEM heuristic
+**************************************************************************/
+void EstimateCFraction(int nvtxs, idxtype *xadj, idxtype *adjncy, float *vfraction, float *efraction)
+{
+  int i, ii, j, cnvtxs, cnedges, maxidx;
+  idxtype *match, *cmap, *perm;
+
+  cmap = idxmalloc(nvtxs, "cmap");
+  match = idxsmalloc(nvtxs, UNMATCHED, "match");
+  perm = idxmalloc(nvtxs, "perm");
+  RandomPermute(nvtxs, perm, 1);
+
+  cnvtxs = 0;
+  for (ii=0; ii<nvtxs; ii++) {
+    i = perm[ii];
+
+    if (match[i] == UNMATCHED) {  /* Unmatched */
+      maxidx = i;
+
+      /* Find a random matching, subject to maxvwgt constraints */
+      for (j=xadj[i]; j<xadj[i+1]; j++) {
+        if (match[adjncy[j]] == UNMATCHED) {
+          maxidx = adjncy[j];
+          break;
+        }
+      }
+
+      cmap[i] = cmap[maxidx] = cnvtxs++;
+      match[i] = maxidx;
+      match[maxidx] = i;
+    }
+  }
+
+  cnedges = ComputeCoarseGraphSize(nvtxs, xadj, adjncy, cnvtxs, cmap, match, perm);
+
+  *vfraction = (1.0*cnvtxs)/(1.0*nvtxs);
+  *efraction = (1.0*cnedges)/(1.0*xadj[nvtxs]);
+
+  GKfree(&cmap, &match, &perm, LTERM);
+}
+
+
+
+
+/*************************************************************************
+* This function computes the size of the coarse graph
+**************************************************************************/
+int ComputeCoarseGraphSize(int nvtxs, idxtype *xadj, idxtype *adjncy, int cnvtxs, idxtype *cmap, idxtype *match, idxtype *perm)
+{
+  int i, j, k, istart, iend, nedges, cnedges, v, u;
+  idxtype *htable;
+
+  htable = idxsmalloc(cnvtxs, -1, "htable");
+
+  cnvtxs = cnedges = 0;
+  for (i=0; i<nvtxs; i++) {
+    v = perm[i];
+    if (cmap[v] != cnvtxs) 
+      continue;
+
+    htable[cnvtxs] = cnvtxs;
+
+    u = match[v];
+
+    istart = xadj[v];
+    iend = xadj[v+1];
+    for (j=istart; j<iend; j++) {
+      k = cmap[adjncy[j]];
+      if (htable[k] != cnvtxs) {
+        htable[k] = cnvtxs;
+        cnedges++;
+      }
+    }
+
+    if (v != u) { 
+      istart = xadj[u];
+      iend = xadj[u+1];
+      for (j=istart; j<iend; j++) {
+        k = cmap[adjncy[j]];
+        if (htable[k] != cnvtxs) {
+          htable[k] = cnvtxs;
+          cnedges++;
+        }
+      }
+    }
+    cnvtxs++;
+  }
+
+  GKfree(&htable, LTERM);
+
+  return cnedges;
+}
+
+
diff --git a/contrib/Metis/fm.c b/contrib/Metis/fm.c
new file mode 100644
index 0000000000..79cd74fa1a
--- /dev/null
+++ b/contrib/Metis/fm.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * fm.c
+ *
+ * This file contains code that implements the edge-based FM refinement
+ *
+ * Started 7/23/97
+ * George
+ *
+ * $Id: fm.c,v 1.1 2005-09-21 17:29:37 geuzaine Exp $
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function performs an edge-based FM refinement
+**************************************************************************/
+void FM_2WayEdgeRefine(CtrlType *ctrl, GraphType *graph, int *tpwgts, int npasses)
+{
+  int i, ii, j, k, kwgt, nvtxs, nbnd, nswaps, from, to, pass, me, limit, tmp;
+  idxtype *xadj, *vwgt, *adjncy, *adjwgt, *where, *id, *ed, *bndptr, *bndind, *pwgts;
+  idxtype *moved, *swaps, *perm;
+  PQueueType parts[2];
+  int higain, oldgain, mincut, mindiff, origdiff, initcut, newcut, mincutorder, avgvwgt;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  vwgt = graph->vwgt;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+  where = graph->where;
+  id = graph->id;
+  ed = graph->ed;
+  pwgts = graph->pwgts;
+  bndptr = graph->bndptr;
+  bndind = graph->bndind;
+
+  moved = idxwspacemalloc(ctrl, nvtxs);
+  swaps = idxwspacemalloc(ctrl, nvtxs);
+  perm = idxwspacemalloc(ctrl, nvtxs);
+
+  limit = amin(amax(0.01*nvtxs, 15), 100);
+  avgvwgt = amin((pwgts[0]+pwgts[1])/20, 2*(pwgts[0]+pwgts[1])/nvtxs);
+
+  tmp = graph->adjwgtsum[idxamax(nvtxs, graph->adjwgtsum)];
+  PQueueInit(ctrl, &parts[0], nvtxs, tmp);
+  PQueueInit(ctrl, &parts[1], nvtxs, tmp);
+
+  IFSET(ctrl->dbglvl, DBG_REFINE, 
+     printf("Partitions: [%6d %6d] T[%6d %6d], Nv-Nb[%6d %6d]. ICut: %6d\n",
+             pwgts[0], pwgts[1], tpwgts[0], tpwgts[1], graph->nvtxs, graph->nbnd, graph->mincut));
+
+  origdiff = abs(tpwgts[0]-pwgts[0]);
+  idxset(nvtxs, -1, moved);
+  for (pass=0; pass<npasses; pass++) { /* Do a number of passes */
+    PQueueReset(&parts[0]);
+    PQueueReset(&parts[1]);
+
+    mincutorder = -1;
+    newcut = mincut = initcut = graph->mincut;
+    mindiff = abs(tpwgts[0]-pwgts[0]);
+
+    ASSERT(ComputeCut(graph, where) == graph->mincut);
+    ASSERT(CheckBnd(graph));
+
+    /* Insert boundary nodes in the priority queues */
+    nbnd = graph->nbnd;
+    RandomPermute(nbnd, perm, 1);
+    for (ii=0; ii<nbnd; ii++) {
+      i = perm[ii];
+      ASSERT(ed[bndind[i]] > 0 || id[bndind[i]] == 0);
+      ASSERT(bndptr[bndind[i]] != -1);
+      PQueueInsert(&parts[where[bndind[i]]], bndind[i], ed[bndind[i]]-id[bndind[i]]);
+    }
+
+    for (nswaps=0; nswaps<nvtxs; nswaps++) {
+      from = (tpwgts[0]-pwgts[0] < tpwgts[1]-pwgts[1] ? 0 : 1);
+      to = (from+1)%2;
+
+      if ((higain = PQueueGetMax(&parts[from])) == -1)
+        break;
+      ASSERT(bndptr[higain] != -1);
+
+      newcut -= (ed[higain]-id[higain]);
+      INC_DEC(pwgts[to], pwgts[from], vwgt[higain]);
+
+      if ((newcut < mincut && abs(tpwgts[0]-pwgts[0]) <= origdiff+avgvwgt) || 
+          (newcut == mincut && abs(tpwgts[0]-pwgts[0]) < mindiff)) {
+        mincut = newcut;
+        mindiff = abs(tpwgts[0]-pwgts[0]);
+        mincutorder = nswaps;
+      }
+      else if (nswaps-mincutorder > limit) { /* We hit the limit, undo last move */
+        newcut += (ed[higain]-id[higain]);
+        INC_DEC(pwgts[from], pwgts[to], vwgt[higain]);
+        break;
+      }
+
+      where[higain] = to;
+      moved[higain] = nswaps;
+      swaps[nswaps] = higain;
+
+      IFSET(ctrl->dbglvl, DBG_MOVEINFO, 
+        printf("Moved %6d from %d. [%3d %3d] %5d [%4d %4d]\n", higain, from, ed[higain]-id[higain], vwgt[higain], newcut, pwgts[0], pwgts[1]));
+
+      /**************************************************************
+      * Update the id[i]/ed[i] values of the affected nodes
+      ***************************************************************/
+      SWAP(id[higain], ed[higain], tmp);
+      if (ed[higain] == 0 && xadj[higain] < xadj[higain+1]) 
+        BNDDelete(nbnd, bndind,  bndptr, higain);
+
+      for (j=xadj[higain]; j<xadj[higain+1]; j++) {
+        k = adjncy[j];
+        oldgain = ed[k]-id[k];
+
+        kwgt = (to == where[k] ? adjwgt[j] : -adjwgt[j]);
+        INC_DEC(id[k], ed[k], kwgt);
+
+        /* Update its boundary information and queue position */
+        if (bndptr[k] != -1) { /* If k was a boundary vertex */
+          if (ed[k] == 0) { /* Not a boundary vertex any more */
+            BNDDelete(nbnd, bndind, bndptr, k);
+            if (moved[k] == -1)  /* Remove it if in the queues */
+              PQueueDelete(&parts[where[k]], k, oldgain);
+          }
+          else { /* If it has not been moved, update its position in the queue */
+            if (moved[k] == -1)
+              PQueueUpdate(&parts[where[k]], k, oldgain, ed[k]-id[k]);
+          }
+        }
+        else {
+          if (ed[k] > 0) {  /* It will now become a boundary vertex */
+            BNDInsert(nbnd, bndind, bndptr, k);
+            if (moved[k] == -1) 
+              PQueueInsert(&parts[where[k]], k, ed[k]-id[k]);
+          }
+        }
+      }
+
+    }
+
+
+    /****************************************************************
+    * Roll back computations
+    *****************************************************************/
+    for (i=0; i<nswaps; i++)
+      moved[swaps[i]] = -1;  /* reset moved array */
+    for (nswaps--; nswaps>mincutorder; nswaps--) {
+      higain = swaps[nswaps];
+
+      to = where[higain] = (where[higain]+1)%2;
+      SWAP(id[higain], ed[higain], tmp);
+      if (ed[higain] == 0 && bndptr[higain] != -1 && xadj[higain] < xadj[higain+1])
+        BNDDelete(nbnd, bndind,  bndptr, higain);
+      else if (ed[higain] > 0 && bndptr[higain] == -1)
+        BNDInsert(nbnd, bndind,  bndptr, higain);
+
+      INC_DEC(pwgts[to], pwgts[(to+1)%2], vwgt[higain]);
+      for (j=xadj[higain]; j<xadj[higain+1]; j++) {
+        k = adjncy[j];
+
+        kwgt = (to == where[k] ? adjwgt[j] : -adjwgt[j]);
+        INC_DEC(id[k], ed[k], kwgt);
+
+        if (bndptr[k] != -1 && ed[k] == 0)
+          BNDDelete(nbnd, bndind, bndptr, k);
+        if (bndptr[k] == -1 && ed[k] > 0)
+          BNDInsert(nbnd, bndind, bndptr, k);
+      }
+    }
+
+    IFSET(ctrl->dbglvl, DBG_REFINE, 
+      printf("\tMinimum cut: %6d at %5d, PWGTS: [%6d %6d], NBND: %6d\n", mincut, mincutorder, pwgts[0], pwgts[1], nbnd));
+
+    graph->mincut = mincut;
+    graph->nbnd = nbnd;
+
+    if (mincutorder == -1 || mincut == initcut)
+      break;
+  }
+
+  PQueueFree(ctrl, &parts[0]);
+  PQueueFree(ctrl, &parts[1]);
+
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+
+}
+
+
diff --git a/contrib/Metis/fortran.c b/contrib/Metis/fortran.c
new file mode 100644
index 0000000000..a89c65408a
--- /dev/null
+++ b/contrib/Metis/fortran.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * fortran.c
+ *
+ * This file contains code for the fortran to C interface
+ *
+ * Started 8/19/97
+ * George
+ *
+ * $Id: fortran.c,v 1.1 2005-09-21 17:29:37 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function changes the numbering to start from 0 instead of 1
+**************************************************************************/
+void Change2CNumbering(int nvtxs, idxtype *xadj, idxtype *adjncy)
+{
+  int i, nedges;
+
+  for (i=0; i<=nvtxs; i++)
+    xadj[i]--;
+
+  nedges = xadj[nvtxs];
+  for (i=0; i<nedges; i++)
+    adjncy[i]--;
+}
+
+/*************************************************************************
+* This function changes the numbering to start from 1 instead of 0
+**************************************************************************/
+void Change2FNumbering(int nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vector)
+{
+  int i, nedges;
+
+  for (i=0; i<nvtxs; i++)
+    vector[i]++;
+
+  nedges = xadj[nvtxs];
+  for (i=0; i<nedges; i++)
+    adjncy[i]++;
+
+  for (i=0; i<=nvtxs; i++)
+    xadj[i]++;
+}
+
+/*************************************************************************
+* This function changes the numbering to start from 1 instead of 0
+**************************************************************************/
+void Change2FNumbering2(int nvtxs, idxtype *xadj, idxtype *adjncy)
+{
+  int i, nedges;
+
+  nedges = xadj[nvtxs];
+  for (i=0; i<nedges; i++)
+    adjncy[i]++;
+
+  for (i=0; i<=nvtxs; i++)
+    xadj[i]++;
+}
+
+
+
+/*************************************************************************
+* This function changes the numbering to start from 1 instead of 0
+**************************************************************************/
+void Change2FNumberingOrder(int nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *v1, idxtype *v2)
+{
+  int i, nedges;
+
+  for (i=0; i<nvtxs; i++) {
+    v1[i]++;
+    v2[i]++;
+  }
+
+  nedges = xadj[nvtxs];
+  for (i=0; i<nedges; i++)
+    adjncy[i]++;
+
+  for (i=0; i<=nvtxs; i++)
+    xadj[i]++;
+
+}
+
+
+
+/*************************************************************************
+* This function changes the numbering to start from 0 instead of 1
+**************************************************************************/
+void ChangeMesh2CNumbering(int n, idxtype *mesh)
+{
+  int i;
+
+  for (i=0; i<n; i++)
+    mesh[i]--;
+
+}
+
+
+/*************************************************************************
+* This function changes the numbering to start from 1 instead of 0
+**************************************************************************/
+void ChangeMesh2FNumbering(int n, idxtype *mesh, int nvtxs, idxtype *xadj, idxtype *adjncy)
+{
+  int i, nedges;
+
+  for (i=0; i<n; i++)
+    mesh[i]++;
+
+  nedges = xadj[nvtxs];
+  for (i=0; i<nedges; i++)
+    adjncy[i]++;
+
+  for (i=0; i<=nvtxs; i++)
+    xadj[i]++;
+
+}
+
+
+/*************************************************************************
+* This function changes the numbering to start from 1 instead of 0
+**************************************************************************/
+void ChangeMesh2FNumbering2(int n, idxtype *mesh, int ne, int nn, idxtype *epart, idxtype *npart)
+{
+  int i, nedges;
+
+  for (i=0; i<n; i++)
+    mesh[i]++;
+
+  for (i=0; i<ne; i++)
+    epart[i]++;
+
+  for (i=0; i<nn; i++)
+    npart[i]++;
+
+}
+
diff --git a/contrib/Metis/frename.c b/contrib/Metis/frename.c
new file mode 100644
index 0000000000..0ec34403a0
--- /dev/null
+++ b/contrib/Metis/frename.c
@@ -0,0 +1,312 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * frename.c
+ * 
+ * This file contains some renaming routines to deal with different Fortran compilers
+ *
+ * Started 9/15/97
+ * George
+ *
+ * $Id: frename.c,v 1.1 2005-09-21 17:29:37 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+
+void METIS_PARTGRAPHRECURSIVE(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, int *wgtflag, int *numflag, int *nparts, int *options, int *edgecut, idxtype *part)
+{
+  METIS_PartGraphRecursive(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, options, edgecut, part);
+}
+void metis_partgraphrecursive(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, int *wgtflag, int *numflag, int *nparts, int *options, int *edgecut, idxtype *part)
+{ 
+  METIS_PartGraphRecursive(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, options, edgecut, part); 
+}
+void metis_partgraphrecursive_(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, int *wgtflag, int *numflag, int *nparts, int *options, int *edgecut, idxtype *part)
+{
+  METIS_PartGraphRecursive(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, options, edgecut, part);
+}
+void metis_partgraphrecursive__(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, int *wgtflag, int *numflag, int *nparts, int *options, int *edgecut, idxtype *part)
+{
+  METIS_PartGraphRecursive(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, options, edgecut, part);
+}
+
+
+void METIS_WPARTGRAPHRECURSIVE(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, int *wgtflag, int *numflag, int *nparts, float *tpwgts, int *options, int *edgecut, idxtype *part)
+{
+  METIS_WPartGraphRecursive(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, tpwgts, options, edgecut, part);
+}
+void metis_wpartgraphrecursive(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, int *wgtflag, int *numflag, int *nparts, float *tpwgts, int *options, int *edgecut, idxtype *part)
+{
+  METIS_WPartGraphRecursive(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, tpwgts, options, edgecut, part);
+}
+void metis_wpartgraphrecursive_(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, int *wgtflag, int *numflag, int *nparts, float *tpwgts, int *options, int *edgecut, idxtype *part)
+{
+  METIS_WPartGraphRecursive(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, tpwgts, options, edgecut, part);
+}
+void metis_wpartgraphrecursive__(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, int *wgtflag, int *numflag, int *nparts, float *tpwgts, int *options, int *edgecut, idxtype *part)
+{
+  METIS_WPartGraphRecursive(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, tpwgts, options, edgecut, part);
+}
+
+
+
+void METIS_PARTGRAPHKWAY(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, int *wgtflag, int *numflag, int *nparts, int *options, int *edgecut, idxtype *part)
+{
+  METIS_PartGraphKway(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, options, edgecut, part);
+}
+void metis_partgraphkway(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, int *wgtflag, int *numflag, int *nparts, int *options, int *edgecut, idxtype *part)
+{
+  METIS_PartGraphKway(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, options, edgecut, part);
+}
+void metis_partgraphkway_(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, int *wgtflag, int *numflag, int *nparts, int *options, int *edgecut, idxtype *part)
+{
+  METIS_PartGraphKway(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, options, edgecut, part);
+}
+void metis_partgraphkway__(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, int *wgtflag, int *numflag, int *nparts, int *options, int *edgecut, idxtype *part)
+{
+  METIS_PartGraphKway(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, options, edgecut, part);
+}
+
+
+
+void METIS_WPARTGRAPHKWAY(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, int *wgtflag, int *numflag, int *nparts, float *tpwgts, int *options, int *edgecut, idxtype *part)
+{
+  METIS_WPartGraphKway(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, tpwgts, options, edgecut, part);
+}
+void metis_wpartgraphkway(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, int *wgtflag, int *numflag, int *nparts, float *tpwgts, int *options, int *edgecut, idxtype *part)
+{
+  METIS_WPartGraphKway(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, tpwgts, options, edgecut, part);
+}
+void metis_wpartgraphkway_(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, int *wgtflag, int *numflag, int *nparts, float *tpwgts, int *options, int *edgecut, idxtype *part)
+{
+  METIS_WPartGraphKway(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, tpwgts, options, edgecut, part);
+}
+void metis_wpartgraphkway__(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, int *wgtflag, int *numflag, int *nparts, float *tpwgts, int *options, int *edgecut, idxtype *part)
+{
+  METIS_WPartGraphKway(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, tpwgts, options, edgecut, part);
+}
+
+
+
+void METIS_EDGEND(int *nvtxs, idxtype *xadj, idxtype *adjncy, int *numflag, int *options, idxtype *perm, idxtype *iperm)
+{
+  METIS_EdgeND(nvtxs, xadj, adjncy, numflag, options, perm, iperm);
+}
+void metis_edgend(int *nvtxs, idxtype *xadj, idxtype *adjncy, int *numflag, int *options, idxtype *perm, idxtype *iperm)
+{
+  METIS_EdgeND(nvtxs, xadj, adjncy, numflag, options, perm, iperm);
+}
+void metis_edgend_(int *nvtxs, idxtype *xadj, idxtype *adjncy, int *numflag, int *options, idxtype *perm, idxtype *iperm)
+{
+  METIS_EdgeND(nvtxs, xadj, adjncy, numflag, options, perm, iperm);
+}
+void metis_edgend__(int *nvtxs, idxtype *xadj, idxtype *adjncy, int *numflag, int *options, idxtype *perm, idxtype *iperm)
+{
+  METIS_EdgeND(nvtxs, xadj, adjncy, numflag, options, perm, iperm);
+}
+
+
+
+void METIS_NODEND(int *nvtxs, idxtype *xadj, idxtype *adjncy, int *numflag, int *options, idxtype *perm, idxtype *iperm)
+{
+  METIS_NodeND(nvtxs, xadj, adjncy, numflag, options, perm, iperm);
+}
+void metis_nodend(int *nvtxs, idxtype *xadj, idxtype *adjncy, int *numflag, int *options, idxtype *perm, idxtype *iperm)
+{
+  METIS_NodeND(nvtxs, xadj, adjncy, numflag, options, perm, iperm);
+}
+void metis_nodend_(int *nvtxs, idxtype *xadj, idxtype *adjncy, int *numflag, int *options, idxtype *perm, idxtype *iperm)
+{
+  METIS_NodeND(nvtxs, xadj, adjncy, numflag, options, perm, iperm);
+}
+void metis_nodend__(int *nvtxs, idxtype *xadj, idxtype *adjncy, int *numflag, int *options, idxtype *perm, idxtype *iperm)
+{
+  METIS_NodeND(nvtxs, xadj, adjncy, numflag, options, perm, iperm);
+}
+
+
+
+void METIS_NODEWND(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, int *numflag, int *options, idxtype *perm, idxtype *iperm)
+{
+  METIS_NodeWND(nvtxs, xadj, adjncy, vwgt, numflag, options, perm, iperm);
+}
+void metis_nodewnd(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, int *numflag, int *options, idxtype *perm, idxtype *iperm)
+{
+  METIS_NodeWND(nvtxs, xadj, adjncy, vwgt, numflag, options, perm, iperm);
+}
+void metis_nodewnd_(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, int *numflag, int *options, idxtype *perm, idxtype *iperm)
+{
+  METIS_NodeWND(nvtxs, xadj, adjncy, vwgt, numflag, options, perm, iperm);
+}
+void metis_nodewnd__(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, int *numflag, int *options, idxtype *perm, idxtype *iperm)
+{
+  METIS_NodeWND(nvtxs, xadj, adjncy, vwgt, numflag, options, perm, iperm);
+}
+
+
+
+void METIS_PARTMESHNODAL(int *ne, int *nn, idxtype *elmnts, int *etype, int *numflag, int *nparts, int *edgecut, idxtype *epart, idxtype *npart)
+{
+  METIS_PartMeshNodal(ne, nn, elmnts, etype, numflag, nparts, edgecut, epart, npart);
+}
+void metis_partmeshnodal(int *ne, int *nn, idxtype *elmnts, int *etype, int *numflag, int *nparts, int *edgecut, idxtype *epart, idxtype *npart)
+{
+  METIS_PartMeshNodal(ne, nn, elmnts, etype, numflag, nparts, edgecut, epart, npart);
+}
+void metis_partmeshnodal_(int *ne, int *nn, idxtype *elmnts, int *etype, int *numflag, int *nparts, int *edgecut, idxtype *epart, idxtype *npart)
+{
+  METIS_PartMeshNodal(ne, nn, elmnts, etype, numflag, nparts, edgecut, epart, npart);
+}
+void metis_partmeshnodal__(int *ne, int *nn, idxtype *elmnts, int *etype, int *numflag, int *nparts, int *edgecut, idxtype *epart, idxtype *npart)
+{
+  METIS_PartMeshNodal(ne, nn, elmnts, etype, numflag, nparts, edgecut, epart, npart);
+}
+
+
+void METIS_PARTMESHDUAL(int *ne, int *nn, idxtype *elmnts, int *etype, int *numflag, int *nparts, int *edgecut, idxtype *epart, idxtype *npart)
+{
+  METIS_PartMeshDual(ne, nn, elmnts, etype, numflag, nparts, edgecut, epart, npart);
+}
+void metis_partmeshdual(int *ne, int *nn, idxtype *elmnts, int *etype, int *numflag, int *nparts, int *edgecut, idxtype *epart, idxtype *npart)
+{
+  METIS_PartMeshDual(ne, nn, elmnts, etype, numflag, nparts, edgecut, epart, npart);
+}
+void metis_partmeshdual_(int *ne, int *nn, idxtype *elmnts, int *etype, int *numflag, int *nparts, int *edgecut, idxtype *epart, idxtype *npart)
+{
+  METIS_PartMeshDual(ne, nn, elmnts, etype, numflag, nparts, edgecut, epart, npart);
+}
+void metis_partmeshdual__(int *ne, int *nn, idxtype *elmnts, int *etype, int *numflag, int *nparts, int *edgecut, idxtype *epart, idxtype *npart)
+{
+  METIS_PartMeshDual(ne, nn, elmnts, etype, numflag, nparts, edgecut, epart, npart);
+}
+
+
+void METIS_MESHTONODAL(int *ne, int *nn, idxtype *elmnts, int *etype, int *numflag, idxtype *dxadj, idxtype *dadjncy)
+{
+  METIS_MeshToNodal(ne, nn, elmnts, etype, numflag, dxadj, dadjncy);
+}
+void metis_meshtonodal(int *ne, int *nn, idxtype *elmnts, int *etype, int *numflag, idxtype *dxadj, idxtype *dadjncy)
+{
+  METIS_MeshToNodal(ne, nn, elmnts, etype, numflag, dxadj, dadjncy);
+}
+void metis_meshtonodal_(int *ne, int *nn, idxtype *elmnts, int *etype, int *numflag, idxtype *dxadj, idxtype *dadjncy)
+{
+  METIS_MeshToNodal(ne, nn, elmnts, etype, numflag, dxadj, dadjncy);
+}
+void metis_meshtonodal__(int *ne, int *nn, idxtype *elmnts, int *etype, int *numflag, idxtype *dxadj, idxtype *dadjncy)
+{
+  METIS_MeshToNodal(ne, nn, elmnts, etype, numflag, dxadj, dadjncy);
+}
+
+
+void METIS_MESHTODUAL(int *ne, int *nn, idxtype *elmnts, int *etype, int *numflag, idxtype *dxadj, idxtype *dadjncy)
+{
+  METIS_MeshToDual(ne, nn, elmnts, etype, numflag, dxadj, dadjncy);
+}
+void metis_meshtodual(int *ne, int *nn, idxtype *elmnts, int *etype, int *numflag, idxtype *dxadj, idxtype *dadjncy)
+{
+  METIS_MeshToDual(ne, nn, elmnts, etype, numflag, dxadj, dadjncy);
+}
+void metis_meshtodual_(int *ne, int *nn, idxtype *elmnts, int *etype, int *numflag, idxtype *dxadj, idxtype *dadjncy)
+{
+  METIS_MeshToDual(ne, nn, elmnts, etype, numflag, dxadj, dadjncy);
+}
+void metis_meshtodual__(int *ne, int *nn, idxtype *elmnts, int *etype, int *numflag, idxtype *dxadj, idxtype *dadjncy)
+{
+  METIS_MeshToDual(ne, nn, elmnts, etype, numflag, dxadj, dadjncy);
+}
+
+
+void METIS_ESTIMATEMEMORY(int *nvtxs, idxtype *xadj, idxtype *adjncy, int *numflag, int *optype, int *nbytes)
+{
+  METIS_EstimateMemory(nvtxs, xadj, adjncy, numflag, optype, nbytes);
+}
+void metis_estimatememory(int *nvtxs, idxtype *xadj, idxtype *adjncy, int *numflag, int *optype, int *nbytes)
+{
+  METIS_EstimateMemory(nvtxs, xadj, adjncy, numflag, optype, nbytes);
+}
+void metis_estimatememory_(int *nvtxs, idxtype *xadj, idxtype *adjncy, int *numflag, int *optype, int *nbytes)
+{
+  METIS_EstimateMemory(nvtxs, xadj, adjncy, numflag, optype, nbytes);
+}
+void metis_estimatememory__(int *nvtxs, idxtype *xadj, idxtype *adjncy, int *numflag, int *optype, int *nbytes)
+{
+  METIS_EstimateMemory(nvtxs, xadj, adjncy, numflag, optype, nbytes);
+}
+
+
+
+void METIS_MCPARTGRAPHRECURSIVE(int *nvtxs, int *ncon, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, int *wgtflag, int *numflag, int *nparts, int *options, int *edgecut, idxtype *part)
+{
+  METIS_mCPartGraphRecursive(nvtxs, ncon, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, options, edgecut, part);
+}
+void metis_mcpartgraphrecursive(int *nvtxs, int *ncon, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, int *wgtflag, int *numflag, int *nparts, int *options, int *edgecut, idxtype *part)
+{
+  METIS_mCPartGraphRecursive(nvtxs, ncon, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, options, edgecut, part);
+}
+void metis_mcpartgraphrecursive_(int *nvtxs, int *ncon, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, int *wgtflag, int *numflag, int *nparts, int *options, int *edgecut, idxtype *part)
+{
+  METIS_mCPartGraphRecursive(nvtxs, ncon, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, options, edgecut, part);
+}
+void metis_mcpartgraphrecursive__(int *nvtxs, int *ncon, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, int *wgtflag, int *numflag, int *nparts, int *options, int *edgecut, idxtype *part)
+{
+  METIS_mCPartGraphRecursive(nvtxs, ncon, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, options, edgecut, part);
+}
+
+
+void METIS_MCPARTGRAPHKWAY(int *nvtxs, int *ncon, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, int *wgtflag, int *numflag, int *nparts, float *rubvec, int *options, int *edgecut, idxtype *part)
+{
+  METIS_mCPartGraphKway(nvtxs, ncon, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, rubvec, options, edgecut, part);
+}
+void metis_mcpartgraphkway(int *nvtxs, int *ncon, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, int *wgtflag, int *numflag, int *nparts, float *rubvec, int *options, int *edgecut, idxtype *part)
+{
+  METIS_mCPartGraphKway(nvtxs, ncon, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, rubvec, options, edgecut, part);
+}
+void metis_mcpartgraphkway_(int *nvtxs, int *ncon, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, int *wgtflag, int *numflag, int *nparts, float *rubvec, int *options, int *edgecut, idxtype *part)
+{
+  METIS_mCPartGraphKway(nvtxs, ncon, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, rubvec, options, edgecut, part);
+}
+void metis_mcpartgraphkway__(int *nvtxs, int *ncon, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, int *wgtflag, int *numflag, int *nparts, float *rubvec, int *options, int *edgecut, idxtype *part)
+{
+  METIS_mCPartGraphKway(nvtxs, ncon, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, rubvec, options, edgecut, part);
+}
+
+
+void METIS_PARTGRAPHVKWAY(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *vsize, int *wgtflag, int *numflag, int *nparts, int *options, int *volume, idxtype *part)
+{
+  METIS_PartGraphVKway(nvtxs, xadj, adjncy, vwgt, vsize, wgtflag, numflag, nparts, options, volume, part);
+}
+void metis_partgraphvkway(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *vsize, int *wgtflag, int *numflag, int *nparts, int *options, int *volume, idxtype *part)
+{
+  METIS_PartGraphVKway(nvtxs, xadj, adjncy, vwgt, vsize, wgtflag, numflag, nparts, options, volume, part);
+}
+void metis_partgraphvkway_(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *vsize, int *wgtflag, int *numflag, int *nparts, int *options, int *volume, idxtype *part)
+{
+  METIS_PartGraphVKway(nvtxs, xadj, adjncy, vwgt, vsize, wgtflag, numflag, nparts, options, volume, part);
+}
+void metis_partgraphvkway__(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *vsize, int *wgtflag, int *numflag, int *nparts, int *options, int *volume, idxtype *part)
+{
+  METIS_PartGraphVKway(nvtxs, xadj, adjncy, vwgt, vsize, wgtflag, numflag, nparts, options, volume, part);
+}
+
+void METIS_WPARTGRAPHVKWAY(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *vsize, int *wgtflag, int *numflag, int *nparts, float *tpwgts, int *options, int *volume, idxtype *part)
+{
+  METIS_WPartGraphVKway(nvtxs, xadj, adjncy, vwgt, vsize, wgtflag, numflag, nparts, tpwgts, options, volume, part);
+}
+void metis_wpartgraphvkway(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *vsize, int *wgtflag, int *numflag, int *nparts, float *tpwgts, int *options, int *volume, idxtype *part)
+{
+  METIS_WPartGraphVKway(nvtxs, xadj, adjncy, vwgt, vsize, wgtflag, numflag, nparts, tpwgts, options, volume, part);
+}
+void metis_wpartgraphvkway_(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *vsize, int *wgtflag, int *numflag, int *nparts, float *tpwgts, int *options, int *volume, idxtype *part)
+{
+  METIS_WPartGraphVKway(nvtxs, xadj, adjncy, vwgt, vsize, wgtflag, numflag, nparts, tpwgts, options, volume, part);
+}
+void metis_wpartgraphvkway__(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *vsize, int *wgtflag, int *numflag, int *nparts, float *tpwgts, int *options, int *volume, idxtype *part)
+{
+  METIS_WPartGraphVKway(nvtxs, xadj, adjncy, vwgt, vsize, wgtflag, numflag, nparts, tpwgts, options, volume, part);
+}
+
+
+
diff --git a/contrib/Metis/graph.c b/contrib/Metis/graph.c
new file mode 100644
index 0000000000..351ce4578c
--- /dev/null
+++ b/contrib/Metis/graph.c
@@ -0,0 +1,616 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * graph.c
+ *
+ * This file contains functions that deal with setting up the graphs
+ * for METIS.
+ *
+ * Started 7/25/97
+ * George
+ *
+ * $Id: graph.c,v 1.1 2005-09-21 17:29:37 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+/*************************************************************************
+* This function sets up the graph from the user input
+**************************************************************************/
+void SetUpGraph(GraphType *graph, int OpType, int nvtxs, int ncon,
+       idxtype *xadj, idxtype *adjncy, idxtype *vwgt, idxtype *adjwgt, int wgtflag)
+{
+  int i, j, k, sum, gsize;
+  float *nvwgt;
+  idxtype tvwgt[MAXNCON];
+
+  if (OpType == OP_KMETIS && ncon == 1 && (wgtflag&2) == 0 && (wgtflag&1) == 0) {
+    SetUpGraphKway(graph, nvtxs, xadj, adjncy);
+    return;
+  }
+
+  InitGraph(graph);
+
+  graph->nvtxs = nvtxs;
+  graph->nedges = xadj[nvtxs];
+  graph->ncon = ncon;
+  graph->xadj = xadj;
+  graph->adjncy = adjncy;
+
+  if (ncon == 1) { /* We are in the non mC mode */
+    gsize = 0; 
+    if ((wgtflag&2) == 0)
+      gsize += nvtxs;
+    if ((wgtflag&1) == 0)
+      gsize += graph->nedges;
+
+    gsize += 2*nvtxs;
+
+    graph->gdata = idxmalloc(gsize, "SetUpGraph: gdata");
+
+    /* Create the vertex/edge weight vectors if they are not supplied */
+    gsize = 0;
+    if ((wgtflag&2) == 0) {
+      vwgt = graph->vwgt = idxset(nvtxs, 1, graph->gdata);
+      gsize += nvtxs;
+    }
+    else
+      graph->vwgt = vwgt;
+
+    if ((wgtflag&1) == 0) {
+      adjwgt = graph->adjwgt = idxset(graph->nedges, 1, graph->gdata+gsize);
+      gsize += graph->nedges;
+    }
+    else
+      graph->adjwgt = adjwgt;
+
+
+    /* Compute the initial values of the adjwgtsum */
+    graph->adjwgtsum = graph->gdata + gsize;
+    gsize += nvtxs;
+
+    for (i=0; i<nvtxs; i++) {
+      sum = 0;
+      for (j=xadj[i]; j<xadj[i+1]; j++)
+        sum += adjwgt[j];
+      graph->adjwgtsum[i] = sum;
+    }
+
+    graph->cmap = graph->gdata + gsize;
+    gsize += nvtxs;
+
+  }
+  else {  /* Set up the graph in MOC mode */
+    gsize = 0; 
+    if ((wgtflag&1) == 0)
+      gsize += graph->nedges;
+
+    gsize += 2*nvtxs;
+
+    graph->gdata = idxmalloc(gsize, "SetUpGraph: gdata");
+    gsize = 0;
+
+    for (i=0; i<ncon; i++) 
+      tvwgt[i] = idxsum_strd(nvtxs, vwgt+i, ncon);
+    
+    nvwgt = graph->nvwgt = fmalloc(ncon*nvtxs, "SetUpGraph: nvwgt");
+
+    for (i=0; i<nvtxs; i++) {
+      for (j=0; j<ncon; j++) 
+        nvwgt[i*ncon+j] = (1.0*vwgt[i*ncon+j])/(1.0*tvwgt[j]);
+    }
+
+
+    /* Create the edge weight vectors if they are not supplied */
+    if ((wgtflag&1) == 0) {
+      adjwgt = graph->adjwgt = idxset(graph->nedges, 1, graph->gdata+gsize);
+      gsize += graph->nedges;
+    }
+    else
+      graph->adjwgt = adjwgt;
+
+    /* Compute the initial values of the adjwgtsum */
+    graph->adjwgtsum = graph->gdata + gsize;
+    gsize += nvtxs;
+
+    for (i=0; i<nvtxs; i++) {
+      sum = 0;
+      for (j=xadj[i]; j<xadj[i+1]; j++)
+        sum += adjwgt[j];
+      graph->adjwgtsum[i] = sum;
+    }
+
+    graph->cmap = graph->gdata + gsize;
+    gsize += nvtxs;
+
+  }
+
+  if (OpType != OP_KMETIS && OpType != OP_KVMETIS) {
+    graph->label = idxmalloc(nvtxs, "SetUpGraph: label");
+
+    for (i=0; i<nvtxs; i++)
+      graph->label[i] = i;
+  }
+
+}
+
+
+/*************************************************************************
+* This function sets up the graph from the user input
+**************************************************************************/
+void SetUpGraphKway(GraphType *graph, int nvtxs, idxtype *xadj, idxtype *adjncy)
+{
+  int i;
+
+  InitGraph(graph);
+
+  graph->nvtxs = nvtxs;
+  graph->nedges = xadj[nvtxs];
+  graph->ncon = 1;
+  graph->xadj = xadj;
+  graph->vwgt = NULL;
+  graph->adjncy = adjncy;
+  graph->adjwgt = NULL;
+
+  graph->gdata = idxmalloc(2*nvtxs, "SetUpGraph: gdata");
+  graph->adjwgtsum = graph->gdata;
+  graph->cmap = graph->gdata + nvtxs;
+
+  /* Compute the initial values of the adjwgtsum */
+  for (i=0; i<nvtxs; i++) 
+    graph->adjwgtsum[i] = xadj[i+1]-xadj[i];
+
+}
+
+
+
+/*************************************************************************
+* This function sets up the graph from the user input
+**************************************************************************/
+void SetUpGraph2(GraphType *graph, int nvtxs, int ncon, idxtype *xadj, 
+       idxtype *adjncy, float *nvwgt, idxtype *adjwgt)
+{
+  int i, j, sum;
+
+  InitGraph(graph);
+
+  graph->nvtxs = nvtxs;
+  graph->nedges = xadj[nvtxs];
+  graph->ncon = ncon;
+  graph->xadj = xadj;
+  graph->adjncy = adjncy;
+  graph->adjwgt = adjwgt;
+
+  graph->nvwgt = fmalloc(nvtxs*ncon, "SetUpGraph2: graph->nvwgt");
+  scopy(nvtxs*ncon, nvwgt, graph->nvwgt);
+
+  graph->gdata = idxmalloc(2*nvtxs, "SetUpGraph: gdata");
+
+  /* Compute the initial values of the adjwgtsum */
+  graph->adjwgtsum = graph->gdata;
+  for (i=0; i<nvtxs; i++) {
+    sum = 0;
+    for (j=xadj[i]; j<xadj[i+1]; j++)
+      sum += adjwgt[j];
+    graph->adjwgtsum[i] = sum;
+  }
+
+  graph->cmap = graph->gdata+nvtxs;
+
+  graph->label = idxmalloc(nvtxs, "SetUpGraph: label");
+  for (i=0; i<nvtxs; i++)
+    graph->label[i] = i;
+
+}
+
+
+/*************************************************************************
+* This function sets up the graph from the user input
+**************************************************************************/
+void VolSetUpGraph(GraphType *graph, int OpType, int nvtxs, int ncon, idxtype *xadj, 
+                   idxtype *adjncy, idxtype *vwgt, idxtype *vsize, int wgtflag)
+{
+  int i, j, k, sum, gsize;
+  idxtype *adjwgt;
+  float *nvwgt;
+  idxtype tvwgt[MAXNCON];
+
+  InitGraph(graph);
+
+  graph->nvtxs = nvtxs;
+  graph->nedges = xadj[nvtxs];
+  graph->ncon = ncon;
+  graph->xadj = xadj;
+  graph->adjncy = adjncy;
+
+  if (ncon == 1) { /* We are in the non mC mode */
+    gsize = graph->nedges;  /* This is for the edge weights */
+    if ((wgtflag&2) == 0)
+      gsize += nvtxs; /* vwgts */
+    if ((wgtflag&1) == 0)
+      gsize += nvtxs; /* vsize */
+
+    gsize += 2*nvtxs;
+
+    graph->gdata = idxmalloc(gsize, "SetUpGraph: gdata");
+
+    /* Create the vertex/edge weight vectors if they are not supplied */
+    gsize = 0;
+    if ((wgtflag&2) == 0) {
+      vwgt = graph->vwgt = idxset(nvtxs, 1, graph->gdata);
+      gsize += nvtxs;
+    }
+    else
+      graph->vwgt = vwgt;
+
+    if ((wgtflag&1) == 0) {
+      vsize = graph->vsize = idxset(nvtxs, 1, graph->gdata);
+      gsize += nvtxs;
+    }
+    else
+      graph->vsize = vsize;
+
+    /* Allocate memory for edge weights and initialize them to the sum of the vsize */
+    adjwgt = graph->adjwgt = graph->gdata+gsize;
+    gsize += graph->nedges;
+
+    for (i=0; i<nvtxs; i++) {
+      for (j=xadj[i]; j<xadj[i+1]; j++)
+        adjwgt[j] = 1+vsize[i]+vsize[adjncy[j]];
+    }
+
+
+    /* Compute the initial values of the adjwgtsum */
+    graph->adjwgtsum = graph->gdata + gsize;
+    gsize += nvtxs;
+
+    for (i=0; i<nvtxs; i++) {
+      sum = 0;
+      for (j=xadj[i]; j<xadj[i+1]; j++)
+        sum += adjwgt[j];
+      graph->adjwgtsum[i] = sum;
+    }
+
+    graph->cmap = graph->gdata + gsize;
+    gsize += nvtxs;
+
+  }
+  else {  /* Set up the graph in MOC mode */
+    gsize = graph->nedges; 
+    if ((wgtflag&1) == 0)
+      gsize += nvtxs;
+
+    gsize += 2*nvtxs;
+
+    graph->gdata = idxmalloc(gsize, "SetUpGraph: gdata");
+    gsize = 0;
+
+    /* Create the normalized vertex weights along each constrain */
+    if ((wgtflag&2) == 0) 
+      vwgt = idxsmalloc(nvtxs, 1, "SetUpGraph: vwgt");
+
+    for (i=0; i<ncon; i++) 
+      tvwgt[i] = idxsum_strd(nvtxs, vwgt+i, ncon);
+    
+    nvwgt = graph->nvwgt = fmalloc(ncon*nvtxs, "SetUpGraph: nvwgt");
+
+    for (i=0; i<nvtxs; i++) {
+      for (j=0; j<ncon; j++) 
+        nvwgt[i*ncon+j] = (1.0*vwgt[i*ncon+j])/(1.0*tvwgt[j]);
+    }
+    if ((wgtflag&2) == 0) 
+      free(vwgt);
+
+
+    /* Create the vsize vector if it is not supplied */
+    if ((wgtflag&1) == 0) {
+      vsize = graph->vsize = idxset(nvtxs, 1, graph->gdata);
+      gsize += nvtxs;
+    }
+    else
+      graph->vsize = vsize;
+
+    /* Allocate memory for edge weights and initialize them to the sum of the vsize */
+    adjwgt = graph->adjwgt = graph->gdata+gsize;
+    gsize += graph->nedges;
+
+    for (i=0; i<nvtxs; i++) {
+      for (j=xadj[i]; j<xadj[i+1]; j++)
+        adjwgt[j] = 1+vsize[i]+vsize[adjncy[j]];
+    }
+
+    /* Compute the initial values of the adjwgtsum */
+    graph->adjwgtsum = graph->gdata + gsize;
+    gsize += nvtxs;
+
+    for (i=0; i<nvtxs; i++) {
+      sum = 0;
+      for (j=xadj[i]; j<xadj[i+1]; j++)
+        sum += adjwgt[j];
+      graph->adjwgtsum[i] = sum;
+    }
+
+    graph->cmap = graph->gdata + gsize;
+    gsize += nvtxs;
+
+  }
+
+  if (OpType != OP_KVMETIS) {
+    graph->label = idxmalloc(nvtxs, "SetUpGraph: label");
+
+    for (i=0; i<nvtxs; i++)
+      graph->label[i] = i;
+  }
+
+}
+
+
+/*************************************************************************
+* This function randomly permutes the adjacency lists of a graph
+**************************************************************************/
+void RandomizeGraph(GraphType *graph)
+{
+  int i, j, k, l, tmp, nvtxs;
+  idxtype *xadj, *adjncy, *adjwgt;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  for (i=0; i<nvtxs; i++) {
+    l = xadj[i+1]-xadj[i];
+    for (j=xadj[i]; j<xadj[i+1]; j++) {
+      k = xadj[i] + RandomInRange(l);
+      SWAP(adjncy[j], adjncy[k], tmp);
+      SWAP(adjwgt[j], adjwgt[k], tmp);
+    }
+  }
+}
+
+
+/*************************************************************************
+* This function checks whether or not partition pid is contigous
+**************************************************************************/
+int IsConnectedSubdomain(CtrlType *ctrl, GraphType *graph, int pid, int report)
+{
+  int i, j, k, nvtxs, first, last, nleft, ncmps, wgt;
+  idxtype *xadj, *adjncy, *where, *touched, *queue;
+  idxtype *cptr;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  where = graph->where;
+
+  touched = idxsmalloc(nvtxs, 0, "IsConnected: touched");
+  queue = idxmalloc(nvtxs, "IsConnected: queue");
+  cptr = idxmalloc(nvtxs, "IsConnected: cptr");
+
+  nleft = 0;
+  for (i=0; i<nvtxs; i++) {
+    if (where[i] == pid) 
+      nleft++;
+  }
+
+  for (i=0; i<nvtxs; i++) {
+    if (where[i] == pid) 
+      break;
+  }
+
+  touched[i] = 1;
+  queue[0] = i;
+  first = 0; last = 1;
+
+  cptr[0] = 0;  /* This actually points to queue */
+  ncmps = 0;
+  while (first != nleft) {
+    if (first == last) { /* Find another starting vertex */
+      cptr[++ncmps] = first;
+      for (i=0; i<nvtxs; i++) {
+        if (where[i] == pid && !touched[i])
+          break;
+      }
+      queue[last++] = i;
+      touched[i] = 1;
+    }
+
+    i = queue[first++];
+    for (j=xadj[i]; j<xadj[i+1]; j++) {
+      k = adjncy[j];
+      if (where[k] == pid && !touched[k]) {
+        queue[last++] = k;
+        touched[k] = 1;
+      }
+    }
+  }
+  cptr[++ncmps] = first;
+
+  if (ncmps > 1 && report) {
+    printf("The graph has %d connected components in partition %d:\t", ncmps, pid);
+    for (i=0; i<ncmps; i++) {
+      wgt = 0;
+      for (j=cptr[i]; j<cptr[i+1]; j++)
+        wgt += graph->vwgt[queue[j]];
+      printf("[%5d %5d] ", cptr[i+1]-cptr[i], wgt);
+      /*
+      if (cptr[i+1]-cptr[i] == 1)
+        printf("[%d %d] ", queue[cptr[i]], xadj[queue[cptr[i]]+1]-xadj[queue[cptr[i]]]);
+      */
+    }
+    printf("\n");
+  }
+
+  GKfree(&touched, &queue, &cptr, LTERM);
+
+  return (ncmps == 1 ? 1 : 0);
+}
+
+
+/*************************************************************************
+* This function checks whether a graph is contigous or not
+**************************************************************************/
+int IsConnected(CtrlType *ctrl, GraphType *graph, int report)
+{
+  int i, j, k, nvtxs, first, last;
+  idxtype *xadj, *adjncy, *touched, *queue;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+
+  touched = idxsmalloc(nvtxs, 0, "IsConnected: touched");
+  queue = idxmalloc(nvtxs, "IsConnected: queue");
+
+  touched[0] = 1;
+  queue[0] = 0;
+  first = 0; last = 1;
+
+  while (first < last) {
+    i = queue[first++];
+    for (j=xadj[i]; j<xadj[i+1]; j++) {
+      k = adjncy[j];
+      if (!touched[k]) {
+        queue[last++] = k;
+        touched[k] = 1;
+      }
+    }
+  }
+
+  if (first != nvtxs && report)
+    printf("The graph is not connected. It has %d disconnected vertices!\n", nvtxs-first);
+
+  return (first == nvtxs ? 1 : 0);
+}
+
+
+/*************************************************************************
+* This function checks whether or not partition pid is contigous
+**************************************************************************/
+int IsConnected2(GraphType *graph, int report)
+{
+  int i, j, k, nvtxs, first, last, nleft, ncmps, wgt;
+  idxtype *xadj, *adjncy, *where, *touched, *queue;
+  idxtype *cptr;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  where = graph->where;
+
+  touched = idxsmalloc(nvtxs, 0, "IsConnected: touched");
+  queue = idxmalloc(nvtxs, "IsConnected: queue");
+  cptr = idxmalloc(nvtxs, "IsConnected: cptr");
+
+  nleft = nvtxs;
+  touched[0] = 1;
+  queue[0] = 0;
+  first = 0; last = 1;
+
+  cptr[0] = 0;  /* This actually points to queue */
+  ncmps = 0;
+  while (first != nleft) {
+    if (first == last) { /* Find another starting vertex */
+      cptr[++ncmps] = first;
+      for (i=0; i<nvtxs; i++) {
+        if (!touched[i])
+          break;
+      }
+      queue[last++] = i;
+      touched[i] = 1;
+    }
+
+    i = queue[first++];
+    for (j=xadj[i]; j<xadj[i+1]; j++) {
+      k = adjncy[j];
+      if (!touched[k]) {
+        queue[last++] = k;
+        touched[k] = 1;
+      }
+    }
+  }
+  cptr[++ncmps] = first;
+
+  if (ncmps > 1 && report) {
+    printf("%d connected components:\t", ncmps);
+    for (i=0; i<ncmps; i++) {
+      if (cptr[i+1]-cptr[i] > 200)
+        printf("[%5d] ", cptr[i+1]-cptr[i]);
+    }
+    printf("\n");
+  }
+
+  GKfree(&touched, &queue, &cptr, LTERM);
+
+  return (ncmps == 1 ? 1 : 0);
+}
+
+
+/*************************************************************************
+* This function returns the number of connected components in cptr,cind
+* The separator of the graph is used to split it and then find its components.
+**************************************************************************/
+int FindComponents(CtrlType *ctrl, GraphType *graph, idxtype *cptr, idxtype *cind)
+{
+  int i, j, k, nvtxs, first, last, nleft, ncmps, wgt;
+  idxtype *xadj, *adjncy, *where, *touched, *queue;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  where = graph->where;
+
+  touched = idxsmalloc(nvtxs, 0, "IsConnected: queue");
+
+  for (i=0; i<graph->nbnd; i++)
+    touched[graph->bndind[i]] = 1;
+
+  queue = cind;
+
+  nleft = 0;
+  for (i=0; i<nvtxs; i++) {
+    if (where[i] != 2) 
+      nleft++;
+  }
+
+  for (i=0; i<nvtxs; i++) {
+    if (where[i] != 2)
+      break;
+  }
+
+  touched[i] = 1;
+  queue[0] = i;
+  first = 0; last = 1;
+
+  cptr[0] = 0;  /* This actually points to queue */
+  ncmps = 0;
+  while (first != nleft) {
+    if (first == last) { /* Find another starting vertex */
+      cptr[++ncmps] = first;
+      for (i=0; i<nvtxs; i++) {
+        if (!touched[i])
+          break;
+      }
+      queue[last++] = i;
+      touched[i] = 1;
+    }
+
+    i = queue[first++];
+    for (j=xadj[i]; j<xadj[i+1]; j++) {
+      k = adjncy[j];
+      if (!touched[k]) {
+        queue[last++] = k;
+        touched[k] = 1;
+      }
+    }
+  }
+  cptr[++ncmps] = first;
+
+  free(touched);
+
+  return ncmps;
+}
+
+
+
diff --git a/contrib/Metis/initpart.c b/contrib/Metis/initpart.c
new file mode 100644
index 0000000000..72c5b7be07
--- /dev/null
+++ b/contrib/Metis/initpart.c
@@ -0,0 +1,422 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * initpart.c
+ *
+ * This file contains code that performs the initial partition of the
+ * coarsest graph
+ *
+ * Started 7/23/97
+ * George
+ *
+ * $Id: initpart.c,v 1.1 2005-09-21 17:29:37 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+/*************************************************************************
+* This function computes the initial bisection of the coarsest graph
+**************************************************************************/
+void Init2WayPartition(CtrlType *ctrl, GraphType *graph, int *tpwgts, float ubfactor) 
+{
+  int dbglvl;
+
+  dbglvl = ctrl->dbglvl;
+  IFSET(ctrl->dbglvl, DBG_REFINE, ctrl->dbglvl -= DBG_REFINE);
+  IFSET(ctrl->dbglvl, DBG_MOVEINFO, ctrl->dbglvl -= DBG_MOVEINFO);
+
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->InitPartTmr));
+
+  switch (ctrl->IType) {
+    case IPART_GGPKL:
+      GrowBisection(ctrl, graph, tpwgts, ubfactor);
+      break;
+    case 3:
+      RandomBisection(ctrl, graph, tpwgts, ubfactor);
+      break;
+    default:
+      errexit("Unknown initial partition type: %d\n", ctrl->IType);
+  }
+
+  IFSET(ctrl->dbglvl, DBG_IPART, printf("Initial Cut: %d\n", graph->mincut));
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->InitPartTmr));
+  ctrl->dbglvl = dbglvl;
+
+/*
+  IsConnectedSubdomain(ctrl, graph, 0);
+  IsConnectedSubdomain(ctrl, graph, 1);
+*/
+}
+
+/*************************************************************************
+* This function computes the initial bisection of the coarsest graph
+**************************************************************************/
+void InitSeparator(CtrlType *ctrl, GraphType *graph, float ubfactor) 
+{
+  int dbglvl;
+
+  dbglvl = ctrl->dbglvl;
+  IFSET(ctrl->dbglvl, DBG_REFINE, ctrl->dbglvl -= DBG_REFINE);
+  IFSET(ctrl->dbglvl, DBG_MOVEINFO, ctrl->dbglvl -= DBG_MOVEINFO);
+
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->InitPartTmr));
+
+  GrowBisectionNode(ctrl, graph, ubfactor);
+  Compute2WayNodePartitionParams(ctrl, graph);
+
+  IFSET(ctrl->dbglvl, DBG_IPART, printf("Initial Sep: %d\n", graph->mincut));
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->InitPartTmr));
+
+  ctrl->dbglvl = dbglvl;
+
+}
+
+
+
+/*************************************************************************
+* This function takes a graph and produces a bisection by using a region
+* growing algorithm. The resulting partition is returned in
+* graph->where
+**************************************************************************/
+void GrowBisection(CtrlType *ctrl, GraphType *graph, int *tpwgts, float ubfactor)
+{
+  int i, j, k, nvtxs, drain, nleft, first, last, pwgts[2], minpwgt[2], maxpwgt[2], from, bestcut, icut, mincut, me, pass, nbfs;
+  idxtype *xadj, *vwgt, *adjncy, *adjwgt, *where;
+  idxtype *queue, *touched, *gain, *bestwhere;
+
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  vwgt = graph->vwgt;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  Allocate2WayPartitionMemory(ctrl, graph);
+  where = graph->where;
+
+  bestwhere = idxmalloc(nvtxs, "BisectGraph: bestwhere");
+  queue = idxmalloc(nvtxs, "BisectGraph: queue");
+  touched = idxmalloc(nvtxs, "BisectGraph: touched");
+
+  ASSERTP(tpwgts[0]+tpwgts[1] == idxsum(nvtxs, vwgt), ("%d %d\n", tpwgts[0]+tpwgts[1], idxsum(nvtxs, vwgt)));
+
+  maxpwgt[0] = ubfactor*tpwgts[0];
+  maxpwgt[1] = ubfactor*tpwgts[1];
+  minpwgt[0] = (1.0/ubfactor)*tpwgts[0];
+  minpwgt[1] = (1.0/ubfactor)*tpwgts[1];
+
+  nbfs = (nvtxs <= ctrl->CoarsenTo ? SMALLNIPARTS : LARGENIPARTS);
+  bestcut = idxsum(nvtxs, graph->adjwgtsum)+1;  /* The +1 is for the 0 edges case */
+  for (; nbfs>0; nbfs--) {
+    idxset(nvtxs, 0, touched);
+
+    pwgts[1] = tpwgts[0]+tpwgts[1];
+    pwgts[0] = 0;
+
+    idxset(nvtxs, 1, where);
+
+    queue[0] = RandomInRange(nvtxs);
+    touched[queue[0]] = 1;
+    first = 0; last = 1;
+    nleft = nvtxs-1;
+    drain = 0;
+
+    /* Start the BFS from queue to get a partition */
+    for (;;) {
+      if (first == last) { /* Empty. Disconnected graph! */
+        if (nleft == 0 || drain)
+          break;
+
+        k = RandomInRange(nleft);
+        for (i=0; i<nvtxs; i++) {
+          if (touched[i] == 0) {
+            if (k == 0)
+              break;
+            else
+              k--;
+          }
+        }
+
+        queue[0] = i;
+        touched[i] = 1;
+        first = 0; last = 1;;
+        nleft--;
+      }
+
+      i = queue[first++];
+      if (pwgts[0] > 0 && pwgts[1]-vwgt[i] < minpwgt[1]) {
+        drain = 1;
+        continue;
+      }
+
+      where[i] = 0;
+      INC_DEC(pwgts[0], pwgts[1], vwgt[i]);
+      if (pwgts[1] <= maxpwgt[1])
+        break;
+
+      drain = 0;
+      for (j=xadj[i]; j<xadj[i+1]; j++) {
+        k = adjncy[j];
+        if (touched[k] == 0) {
+          queue[last++] = k;
+          touched[k] = 1;
+          nleft--;
+        }
+      }
+    }
+
+    /* Check to see if we hit any bad limiting cases */
+    if (pwgts[1] == 0) { 
+      i = RandomInRange(nvtxs);
+      where[i] = 1;
+      INC_DEC(pwgts[1], pwgts[0], vwgt[i]);
+    }
+
+    /*************************************************************
+    * Do some partition refinement 
+    **************************************************************/
+    Compute2WayPartitionParams(ctrl, graph);
+    /*printf("IPART: %3d [%5d %5d] [%5d %5d] %5d\n", graph->nvtxs, pwgts[0], pwgts[1], graph->pwgts[0], graph->pwgts[1], graph->mincut); */
+
+    Balance2Way(ctrl, graph, tpwgts, ubfactor);
+    /*printf("BPART: [%5d %5d] %5d\n", graph->pwgts[0], graph->pwgts[1], graph->mincut);*/
+
+    FM_2WayEdgeRefine(ctrl, graph, tpwgts, 4);
+    /*printf("RPART: [%5d %5d] %5d\n", graph->pwgts[0], graph->pwgts[1], graph->mincut);*/
+
+    if (bestcut > graph->mincut) {
+      bestcut = graph->mincut;
+      idxcopy(nvtxs, where, bestwhere);
+      if (bestcut == 0)
+        break;
+    }
+  }
+
+  graph->mincut = bestcut;
+  idxcopy(nvtxs, bestwhere, where);
+
+  GKfree(&bestwhere, &queue, &touched, LTERM);
+}
+
+
+
+
+/*************************************************************************
+* This function takes a graph and produces a bisection by using a region
+* growing algorithm. The resulting partition is returned in
+* graph->where
+**************************************************************************/
+void GrowBisectionNode(CtrlType *ctrl, GraphType *graph, float ubfactor)
+{
+  int i, j, k, nvtxs, drain, nleft, first, last, pwgts[2], tpwgts[2], minpwgt[2], maxpwgt[2], from, bestcut, icut, mincut, me, pass, nbfs;
+  idxtype *xadj, *vwgt, *adjncy, *adjwgt, *where, *bndind;
+  idxtype *queue, *touched, *gain, *bestwhere;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  vwgt = graph->vwgt;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  bestwhere = idxmalloc(nvtxs, "BisectGraph: bestwhere");
+  queue = idxmalloc(nvtxs, "BisectGraph: queue");
+  touched = idxmalloc(nvtxs, "BisectGraph: touched");
+
+  tpwgts[0] = idxsum(nvtxs, vwgt);
+  tpwgts[1] = tpwgts[0]/2;
+  tpwgts[0] -= tpwgts[1];
+
+  maxpwgt[0] = ubfactor*tpwgts[0];
+  maxpwgt[1] = ubfactor*tpwgts[1];
+  minpwgt[0] = (1.0/ubfactor)*tpwgts[0];
+  minpwgt[1] = (1.0/ubfactor)*tpwgts[1];
+
+  /* Allocate memory for graph->rdata. Allocate sufficient memory for both edge and node */
+  graph->rdata = idxmalloc(5*nvtxs+3, "GrowBisectionNode: graph->rdata");
+  graph->pwgts    = graph->rdata;
+  graph->where    = graph->rdata + 3;
+  graph->bndptr   = graph->rdata + nvtxs + 3;
+  graph->bndind   = graph->rdata + 2*nvtxs + 3;
+  graph->nrinfo   = (NRInfoType *)(graph->rdata + 3*nvtxs + 3);
+  graph->id       = graph->rdata + 3*nvtxs + 3;
+  graph->ed       = graph->rdata + 4*nvtxs + 3;
+  
+  where = graph->where;
+  bndind = graph->bndind;
+
+  nbfs = (nvtxs <= ctrl->CoarsenTo ? SMALLNIPARTS : LARGENIPARTS);
+  bestcut = tpwgts[0]+tpwgts[1];
+  for (nbfs++; nbfs>0; nbfs--) {
+    idxset(nvtxs, 0, touched);
+
+    pwgts[1] = tpwgts[0]+tpwgts[1];
+    pwgts[0] = 0;
+
+    idxset(nvtxs, 1, where);
+
+    queue[0] = RandomInRange(nvtxs);
+    touched[queue[0]] = 1;
+    first = 0; last = 1;
+    nleft = nvtxs-1;
+    drain = 0;
+
+    /* Start the BFS from queue to get a partition */
+    if (nbfs >= 1) {
+      for (;;) {
+        if (first == last) { /* Empty. Disconnected graph! */
+          if (nleft == 0 || drain)
+            break;
+  
+          k = RandomInRange(nleft);
+          for (i=0; i<nvtxs; i++) {
+            if (touched[i] == 0) {
+              if (k == 0)
+                break;
+              else
+                k--;
+            }
+          }
+
+          queue[0] = i;
+          touched[i] = 1;
+          first = 0; last = 1;;
+          nleft--;
+        }
+
+        i = queue[first++];
+        if (pwgts[1]-vwgt[i] < minpwgt[1]) {
+          drain = 1;
+          continue;
+        }
+
+        where[i] = 0;
+        INC_DEC(pwgts[0], pwgts[1], vwgt[i]);
+        if (pwgts[1] <= maxpwgt[1])
+          break;
+
+        drain = 0;
+        for (j=xadj[i]; j<xadj[i+1]; j++) {
+          k = adjncy[j];
+          if (touched[k] == 0) {
+            queue[last++] = k;
+            touched[k] = 1;
+            nleft--;
+          }
+        }
+      }
+    }
+
+    /*************************************************************
+    * Do some partition refinement 
+    **************************************************************/
+    Compute2WayPartitionParams(ctrl, graph);
+    Balance2Way(ctrl, graph, tpwgts, ubfactor);
+    FM_2WayEdgeRefine(ctrl, graph, tpwgts, 4);
+
+    /* Construct and refine the vertex separator */
+    for (i=0; i<graph->nbnd; i++) 
+      where[bndind[i]] = 2;
+
+    Compute2WayNodePartitionParams(ctrl, graph); 
+    FM_2WayNodeRefine(ctrl, graph, ubfactor, 6);
+
+    /* printf("ISep: [%d %d %d] %d\n", graph->pwgts[0], graph->pwgts[1], graph->pwgts[2], bestcut); */
+
+    if (bestcut > graph->mincut) {
+      bestcut = graph->mincut;
+      idxcopy(nvtxs, where, bestwhere);
+    }
+  }
+
+  graph->mincut = bestcut;
+  idxcopy(nvtxs, bestwhere, where);
+
+  Compute2WayNodePartitionParams(ctrl, graph); 
+
+  GKfree(&bestwhere, &queue, &touched, LTERM);
+}
+
+
+/*************************************************************************
+* This function takes a graph and produces a bisection by using a region
+* growing algorithm. The resulting partition is returned in
+* graph->where
+**************************************************************************/
+void RandomBisection(CtrlType *ctrl, GraphType *graph, int *tpwgts, float ubfactor)
+{
+  int i, ii, j, k, nvtxs, pwgts[2], minpwgt[2], maxpwgt[2], from, bestcut, icut, mincut, me, pass, nbfs;
+  idxtype *xadj, *vwgt, *adjncy, *adjwgt, *where;
+  idxtype *perm, *bestwhere;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  vwgt = graph->vwgt;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  Allocate2WayPartitionMemory(ctrl, graph);
+  where = graph->where;
+
+  bestwhere = idxmalloc(nvtxs, "BisectGraph: bestwhere");
+  perm = idxmalloc(nvtxs, "BisectGraph: queue");
+
+  ASSERTP(tpwgts[0]+tpwgts[1] == idxsum(nvtxs, vwgt), ("%d %d\n", tpwgts[0]+tpwgts[1], idxsum(nvtxs, vwgt)));
+
+  maxpwgt[0] = ubfactor*tpwgts[0];
+  maxpwgt[1] = ubfactor*tpwgts[1];
+  minpwgt[0] = (1.0/ubfactor)*tpwgts[0];
+  minpwgt[1] = (1.0/ubfactor)*tpwgts[1];
+
+  nbfs = (nvtxs <= ctrl->CoarsenTo ? SMALLNIPARTS : LARGENIPARTS);
+  bestcut = idxsum(nvtxs, graph->adjwgtsum)+1;  /* The +1 is for the 0 edges case */
+  for (; nbfs>0; nbfs--) {
+    RandomPermute(nvtxs, perm, 1);
+
+    idxset(nvtxs, 1, where);
+    pwgts[1] = tpwgts[0]+tpwgts[1];
+    pwgts[0] = 0;
+
+
+    if (nbfs != 1) {
+      for (ii=0; ii<nvtxs; ii++) {
+        i = perm[ii];
+        if (pwgts[0]+vwgt[i] < maxpwgt[0]) {
+          where[i] = 0;
+          pwgts[0] += vwgt[i];
+          pwgts[1] -= vwgt[i];
+          if (pwgts[0] > minpwgt[0])
+            break;
+        }
+      }
+    }
+
+    /*************************************************************
+    * Do some partition refinement 
+    **************************************************************/
+    Compute2WayPartitionParams(ctrl, graph);
+    /* printf("IPART: %3d [%5d %5d] [%5d %5d] %5d\n", graph->nvtxs, pwgts[0], pwgts[1], graph->pwgts[0], graph->pwgts[1], graph->mincut); */
+
+    Balance2Way(ctrl, graph, tpwgts, ubfactor);
+    /* printf("BPART: [%5d %5d] %5d\n", graph->pwgts[0], graph->pwgts[1], graph->mincut); */
+
+    FM_2WayEdgeRefine(ctrl, graph, tpwgts, 4);
+    /* printf("RPART: [%5d %5d] %5d\n", graph->pwgts[0], graph->pwgts[1], graph->mincut); */
+
+    if (bestcut > graph->mincut) {
+      bestcut = graph->mincut;
+      idxcopy(nvtxs, where, bestwhere);
+      if (bestcut == 0)
+        break;
+    }
+  }
+
+  graph->mincut = bestcut;
+  idxcopy(nvtxs, bestwhere, where);
+
+  GKfree(&bestwhere, &perm, LTERM);
+}
+
+
+
+
diff --git a/contrib/Metis/kmetis.c b/contrib/Metis/kmetis.c
new file mode 100644
index 0000000000..9a075abde5
--- /dev/null
+++ b/contrib/Metis/kmetis.c
@@ -0,0 +1,129 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * kmetis.c
+ *
+ * This file contains the top level routines for the multilevel k-way partitioning
+ * algorithm KMETIS.
+ *
+ * Started 7/28/97
+ * George
+ *
+ * $Id: kmetis.c,v 1.1 2005-09-21 17:29:37 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function is the entry point for KMETIS
+**************************************************************************/
+void METIS_PartGraphKway(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, 
+                         idxtype *adjwgt, int *wgtflag, int *numflag, int *nparts, 
+                         int *options, int *edgecut, idxtype *part)
+{
+  int i;
+  float *tpwgts;
+
+  tpwgts = fmalloc(*nparts, "KMETIS: tpwgts");
+  for (i=0; i<*nparts; i++) 
+    tpwgts[i] = 1.0/(1.0*(*nparts));
+
+  METIS_WPartGraphKway(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, 
+                       tpwgts, options, edgecut, part);
+
+  free(tpwgts);
+}
+
+
+/*************************************************************************
+* This function is the entry point for KWMETIS
+**************************************************************************/
+void METIS_WPartGraphKway(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, 
+                          idxtype *adjwgt, int *wgtflag, int *numflag, int *nparts, 
+                          float *tpwgts, int *options, int *edgecut, idxtype *part)
+{
+  int i, j;
+  GraphType graph;
+  CtrlType ctrl;
+
+  if (*numflag == 1)
+    Change2CNumbering(*nvtxs, xadj, adjncy);
+
+  SetUpGraph(&graph, OP_KMETIS, *nvtxs, 1, xadj, adjncy, vwgt, adjwgt, *wgtflag);
+
+  if (options[0] == 0) {  /* Use the default parameters */
+    ctrl.CType = KMETIS_CTYPE;
+    ctrl.IType = KMETIS_ITYPE;
+    ctrl.RType = KMETIS_RTYPE;
+    ctrl.dbglvl = KMETIS_DBGLVL;
+  }
+  else {
+    ctrl.CType = options[OPTION_CTYPE];
+    ctrl.IType = options[OPTION_ITYPE];
+    ctrl.RType = options[OPTION_RTYPE];
+    ctrl.dbglvl = options[OPTION_DBGLVL];
+  }
+  ctrl.optype = OP_KMETIS;
+  ctrl.CoarsenTo = amax((*nvtxs)/(40*log2(*nparts)), 20*(*nparts));
+  ctrl.maxvwgt = 1.5*((graph.vwgt ? idxsum(*nvtxs, graph.vwgt) : (*nvtxs))/ctrl.CoarsenTo);
+
+  InitRandom(-1);
+
+  AllocateWorkSpace(&ctrl, &graph, *nparts);
+
+  IFSET(ctrl.dbglvl, DBG_TIME, InitTimers(&ctrl));
+  IFSET(ctrl.dbglvl, DBG_TIME, starttimer(ctrl.TotalTmr));
+
+  *edgecut = MlevelKWayPartitioning(&ctrl, &graph, *nparts, part, tpwgts, 1.03);
+
+  IFSET(ctrl.dbglvl, DBG_TIME, stoptimer(ctrl.TotalTmr));
+  IFSET(ctrl.dbglvl, DBG_TIME, PrintTimers(&ctrl));
+
+  FreeWorkSpace(&ctrl, &graph);
+
+  if (*numflag == 1)
+    Change2FNumbering(*nvtxs, xadj, adjncy, part);
+}
+
+
+/*************************************************************************
+* This function takes a graph and produces a bisection of it
+**************************************************************************/
+int MlevelKWayPartitioning(CtrlType *ctrl, GraphType *graph, int nparts, idxtype *part, float *tpwgts, float ubfactor)
+{
+  int i, j, nvtxs, tvwgt, tpwgts2[2];
+  GraphType *cgraph;
+  int wgtflag=3, numflag=0, options[10], edgecut;
+
+  cgraph = Coarsen2Way(ctrl, graph);
+
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->InitPartTmr));
+  AllocateKWayPartitionMemory(ctrl, cgraph, nparts);
+
+  options[0] = 1; 
+  options[OPTION_CTYPE] = MATCH_SHEMKWAY;
+  options[OPTION_ITYPE] = IPART_GGPKL;
+  options[OPTION_RTYPE] = RTYPE_FM;
+  options[OPTION_DBGLVL] = 0;
+
+  METIS_WPartGraphRecursive(&cgraph->nvtxs, cgraph->xadj, cgraph->adjncy, cgraph->vwgt, 
+                            cgraph->adjwgt, &wgtflag, &numflag, &nparts, tpwgts, options, 
+                            &edgecut, cgraph->where);
+
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->InitPartTmr));
+  IFSET(ctrl->dbglvl, DBG_IPART, printf("Initial %d-way partitioning cut: %d\n", nparts, edgecut));
+
+  IFSET(ctrl->dbglvl, DBG_KWAYPINFO, ComputePartitionInfo(cgraph, nparts, cgraph->where));
+
+  RefineKWay(ctrl, graph, cgraph, nparts, tpwgts, ubfactor);
+
+  idxcopy(graph->nvtxs, graph->where, part);
+
+  GKfree(&graph->gdata, &graph->rdata, LTERM);
+
+  return graph->mincut;
+
+}
+
diff --git a/contrib/Metis/kvmetis.c b/contrib/Metis/kvmetis.c
new file mode 100644
index 0000000000..f1d0b456e1
--- /dev/null
+++ b/contrib/Metis/kvmetis.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * kvmetis.c
+ *
+ * This file contains the top level routines for the multilevel k-way partitioning
+ * algorithm KMETIS.
+ *
+ * Started 7/28/97
+ * George
+ *
+ * $Id: kvmetis.c,v 1.1 2005-09-21 17:29:37 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function is the entry point for KMETIS
+**************************************************************************/
+void METIS_PartGraphVKway(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, 
+                         idxtype *vsize, int *wgtflag, int *numflag, int *nparts, 
+                         int *options, int *volume, idxtype *part)
+{
+  int i;
+  float *tpwgts;
+
+  tpwgts = fmalloc(*nparts, "KMETIS: tpwgts");
+  for (i=0; i<*nparts; i++) 
+    tpwgts[i] = 1.0/(1.0*(*nparts));
+
+  METIS_WPartGraphVKway(nvtxs, xadj, adjncy, vwgt, vsize, wgtflag, numflag, nparts, 
+                       tpwgts, options, volume, part);
+
+  free(tpwgts);
+}
+
+
+/*************************************************************************
+* This function is the entry point for KWMETIS
+**************************************************************************/
+void METIS_WPartGraphVKway(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, 
+                          idxtype *vsize, int *wgtflag, int *numflag, int *nparts, 
+                          float *tpwgts, int *options, int *volume, idxtype *part)
+{
+  int i, j;
+  GraphType graph;
+  CtrlType ctrl;
+
+  if (*numflag == 1)
+    Change2CNumbering(*nvtxs, xadj, adjncy);
+
+  VolSetUpGraph(&graph, OP_KVMETIS, *nvtxs, 1, xadj, adjncy, vwgt, vsize, *wgtflag);
+
+  if (options[0] == 0) {  /* Use the default parameters */
+    ctrl.CType = KVMETIS_CTYPE;
+    ctrl.IType = KVMETIS_ITYPE;
+    ctrl.RType = KVMETIS_RTYPE;
+    ctrl.dbglvl = KVMETIS_DBGLVL;
+  }
+  else {
+    ctrl.CType = options[OPTION_CTYPE];
+    ctrl.IType = options[OPTION_ITYPE];
+    ctrl.RType = options[OPTION_RTYPE];
+    ctrl.dbglvl = options[OPTION_DBGLVL];
+  }
+  ctrl.optype = OP_KVMETIS;
+  ctrl.CoarsenTo = amax((*nvtxs)/(40*log2(*nparts)), 20*(*nparts));
+  ctrl.maxvwgt = 1.5*((graph.vwgt ? idxsum(*nvtxs, graph.vwgt) : (*nvtxs))/ctrl.CoarsenTo);
+
+  InitRandom(-1);
+
+  AllocateWorkSpace(&ctrl, &graph, *nparts);
+
+  IFSET(ctrl.dbglvl, DBG_TIME, InitTimers(&ctrl));
+  IFSET(ctrl.dbglvl, DBG_TIME, starttimer(ctrl.TotalTmr));
+
+  *volume = MlevelVolKWayPartitioning(&ctrl, &graph, *nparts, part, tpwgts, 1.03);
+
+  IFSET(ctrl.dbglvl, DBG_TIME, stoptimer(ctrl.TotalTmr));
+  IFSET(ctrl.dbglvl, DBG_TIME, PrintTimers(&ctrl));
+
+  FreeWorkSpace(&ctrl, &graph);
+
+  if (*numflag == 1)
+    Change2FNumbering(*nvtxs, xadj, adjncy, part);
+}
+
+
+/*************************************************************************
+* This function takes a graph and produces a bisection of it
+**************************************************************************/
+int MlevelVolKWayPartitioning(CtrlType *ctrl, GraphType *graph, int nparts, idxtype *part, 
+                              float *tpwgts, float ubfactor)
+{
+  int i, j, nvtxs, tvwgt, tpwgts2[2];
+  GraphType *cgraph;
+  int wgtflag=3, numflag=0, options[10], edgecut;
+
+  cgraph = Coarsen2Way(ctrl, graph);
+
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->InitPartTmr));
+  AllocateVolKWayPartitionMemory(ctrl, cgraph, nparts);
+
+  options[0] = 1; 
+  options[OPTION_CTYPE] = MATCH_SHEMKWAY;
+  options[OPTION_ITYPE] = IPART_GGPKL;
+  options[OPTION_RTYPE] = RTYPE_FM;
+  options[OPTION_DBGLVL] = 0;
+
+  METIS_WPartGraphRecursive(&cgraph->nvtxs, cgraph->xadj, cgraph->adjncy, cgraph->vwgt, 
+                            cgraph->adjwgt, &wgtflag, &numflag, &nparts, tpwgts, options, 
+                            &edgecut, cgraph->where);
+
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->InitPartTmr));
+  IFSET(ctrl->dbglvl, DBG_IPART, printf("Initial %d-way partitioning cut: %d\n", nparts, edgecut));
+
+  IFSET(ctrl->dbglvl, DBG_KWAYPINFO, ComputePartitionInfo(cgraph, nparts, cgraph->where));
+
+  RefineVolKWay(ctrl, graph, cgraph, nparts, tpwgts, ubfactor);
+
+  idxcopy(graph->nvtxs, graph->where, part);
+
+  GKfree(&graph->gdata, &graph->rdata, LTERM);
+
+  return graph->minvol;
+
+}
+
diff --git a/contrib/Metis/kwayfm.c b/contrib/Metis/kwayfm.c
new file mode 100644
index 0000000000..5772604948
--- /dev/null
+++ b/contrib/Metis/kwayfm.c
@@ -0,0 +1,672 @@
+/*
+ * kwayfm.c
+ *
+ * This file contains code that implements the multilevel k-way refinement
+ *
+ * Started 7/28/97
+ * George
+ *
+ * $Id: kwayfm.c,v 1.1 2005-09-21 17:29:37 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function performs k-way refinement
+**************************************************************************/
+void Random_KWayEdgeRefine(CtrlType *ctrl, GraphType *graph, int nparts, float *tpwgts, float ubfactor, int npasses, int ffactor)
+{
+  int i, ii, iii, j, jj, k, l, pass, nvtxs, nmoves, nbnd, tvwgt, myndegrees; 
+  int from, me, to, oldcut, vwgt, gain;
+  idxtype *xadj, *adjncy, *adjwgt;
+  idxtype *where, *pwgts, *perm, *bndptr, *bndind, *minwgt, *maxwgt, *itpwgts;
+  EDegreeType *myedegrees;
+  RInfoType *myrinfo;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  bndptr = graph->bndptr;
+  bndind = graph->bndind;
+
+  where = graph->where;
+  pwgts = graph->pwgts;
+  
+  /* Setup the weight intervals of the various subdomains */
+  minwgt =  idxwspacemalloc(ctrl, nparts);
+  maxwgt = idxwspacemalloc(ctrl, nparts);
+  itpwgts = idxwspacemalloc(ctrl, nparts);
+  tvwgt = idxsum(nparts, pwgts);
+  ASSERT(tvwgt == idxsum(nvtxs, graph->vwgt));
+
+  for (i=0; i<nparts; i++) {
+    itpwgts[i] = tpwgts[i]*tvwgt;
+    maxwgt[i] = tpwgts[i]*tvwgt*ubfactor;
+    minwgt[i] = tpwgts[i]*tvwgt*(1.0/ubfactor);
+  }
+
+  perm = idxwspacemalloc(ctrl, nvtxs);
+
+  IFSET(ctrl->dbglvl, DBG_REFINE,
+     printf("Partitions: [%6d %6d]-[%6d %6d], Balance: %5.3f, Nv-Nb[%6d %6d]. Cut: %6d\n",
+             pwgts[idxamin(nparts, pwgts)], pwgts[idxamax(nparts, pwgts)], minwgt[0], maxwgt[0], 
+             1.0*nparts*pwgts[idxamax(nparts, pwgts)]/tvwgt, graph->nvtxs, graph->nbnd,
+             graph->mincut));
+
+  for (pass=0; pass<npasses; pass++) {
+    ASSERT(ComputeCut(graph, where) == graph->mincut);
+
+    oldcut = graph->mincut;
+    nbnd = graph->nbnd;
+
+    RandomPermute(nbnd, perm, 1);
+    for (nmoves=iii=0; iii<graph->nbnd; iii++) {
+      ii = perm[iii];
+      if (ii >= nbnd)
+        continue;
+      i = bndind[ii];
+
+      myrinfo = graph->rinfo+i;
+
+      if (myrinfo->ed >= myrinfo->id) { /* Total ED is too high */
+        from = where[i];
+        vwgt = graph->vwgt[i];
+
+        if (myrinfo->id > 0 && pwgts[from]-vwgt < minwgt[from]) 
+          continue;   /* This cannot be moved! */
+
+        myedegrees = myrinfo->edegrees;
+        myndegrees = myrinfo->ndegrees;
+
+        j = myrinfo->id;
+        for (k=0; k<myndegrees; k++) {
+          to = myedegrees[k].pid;
+          gain = myedegrees[k].ed-j; /* j = myrinfo->id. Allow good nodes to move */ 
+          if (pwgts[to]+vwgt <= maxwgt[to]+ffactor*gain && gain >= 0)  
+            break;
+        }
+        if (k == myndegrees)
+          continue;  /* break out if you did not find a candidate */
+
+        for (j=k+1; j<myndegrees; j++) {
+          to = myedegrees[j].pid;
+          if ((myedegrees[j].ed > myedegrees[k].ed && pwgts[to]+vwgt <= maxwgt[to]) ||
+              (myedegrees[j].ed == myedegrees[k].ed && 
+               itpwgts[myedegrees[k].pid]*pwgts[to] < itpwgts[to]*pwgts[myedegrees[k].pid]))
+            k = j;
+        }
+
+        to = myedegrees[k].pid;
+
+        j = 0;
+        if (myedegrees[k].ed-myrinfo->id > 0)
+          j = 1;
+        else if (myedegrees[k].ed-myrinfo->id == 0) {
+          if ((iii&7) == 0 || pwgts[from] >= maxwgt[from] || itpwgts[from]*(pwgts[to]+vwgt) < itpwgts[to]*pwgts[from])
+            j = 1;
+        }
+        if (j == 0)
+          continue;
+          
+        /*=====================================================================
+        * If we got here, we can now move the vertex from 'from' to 'to' 
+        *======================================================================*/
+        graph->mincut -= myedegrees[k].ed-myrinfo->id;
+
+        IFSET(ctrl->dbglvl, DBG_MOVEINFO, printf("\t\tMoving %6d to %3d. Gain: %4d. Cut: %6d\n", i, to, myedegrees[k].ed-myrinfo->id, graph->mincut));
+
+        /* Update where, weight, and ID/ED information of the vertex you moved */
+        where[i] = to;
+        INC_DEC(pwgts[to], pwgts[from], vwgt);
+        myrinfo->ed += myrinfo->id-myedegrees[k].ed;
+        SWAP(myrinfo->id, myedegrees[k].ed, j);
+        if (myedegrees[k].ed == 0) 
+          myedegrees[k] = myedegrees[--myrinfo->ndegrees];
+        else
+          myedegrees[k].pid = from;
+
+        if (myrinfo->ed-myrinfo->id < 0)
+          BNDDelete(nbnd, bndind, bndptr, i);
+
+        /* Update the degrees of adjacent vertices */
+        for (j=xadj[i]; j<xadj[i+1]; j++) {
+          ii = adjncy[j];
+          me = where[ii];
+
+          myrinfo = graph->rinfo+ii;
+          if (myrinfo->edegrees == NULL) {
+            myrinfo->edegrees = ctrl->wspace.edegrees+ctrl->wspace.cdegree;
+            ctrl->wspace.cdegree += xadj[ii+1]-xadj[ii];
+          }
+          myedegrees = myrinfo->edegrees;
+
+          ASSERT(CheckRInfo(myrinfo));
+
+          if (me == from) {
+            INC_DEC(myrinfo->ed, myrinfo->id, adjwgt[j]);
+
+            if (myrinfo->ed-myrinfo->id >= 0 && bndptr[ii] == -1)
+              BNDInsert(nbnd, bndind, bndptr, ii);
+          }
+          else if (me == to) {
+            INC_DEC(myrinfo->id, myrinfo->ed, adjwgt[j]);
+
+            if (myrinfo->ed-myrinfo->id < 0 && bndptr[ii] != -1)
+              BNDDelete(nbnd, bndind, bndptr, ii);
+          }
+
+          /* Remove contribution from the .ed of 'from' */
+          if (me != from) {
+            for (k=0; k<myrinfo->ndegrees; k++) {
+              if (myedegrees[k].pid == from) {
+                if (myedegrees[k].ed == adjwgt[j])
+                  myedegrees[k] = myedegrees[--myrinfo->ndegrees];
+                else
+                  myedegrees[k].ed -= adjwgt[j];
+                break;
+              }
+            }
+          }
+
+          /* Add contribution to the .ed of 'to' */
+          if (me != to) {
+            for (k=0; k<myrinfo->ndegrees; k++) {
+              if (myedegrees[k].pid == to) {
+                myedegrees[k].ed += adjwgt[j];
+                break;
+              }
+            }
+            if (k == myrinfo->ndegrees) {
+              myedegrees[myrinfo->ndegrees].pid = to;
+              myedegrees[myrinfo->ndegrees++].ed = adjwgt[j];
+            }
+          }
+
+          ASSERT(myrinfo->ndegrees <= xadj[ii+1]-xadj[ii]);
+          ASSERT(CheckRInfo(myrinfo));
+
+        }
+        nmoves++;
+      }
+    }
+
+    graph->nbnd = nbnd;
+
+    IFSET(ctrl->dbglvl, DBG_REFINE,
+       printf("\t[%6d %6d], Balance: %5.3f, Nb: %6d. Nmoves: %5d, Cut: %6d, Vol: %6d\n",
+               pwgts[idxamin(nparts, pwgts)], pwgts[idxamax(nparts, pwgts)],
+               1.0*nparts*pwgts[idxamax(nparts, pwgts)]/tvwgt, graph->nbnd, nmoves, graph->mincut, ComputeVolume(graph, where)));
+
+    if (graph->mincut == oldcut)
+      break;
+  }
+
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nvtxs);
+}
+
+
+
+
+
+
+/*************************************************************************
+* This function performs k-way refinement
+**************************************************************************/
+void Greedy_KWayEdgeRefine(CtrlType *ctrl, GraphType *graph, int nparts, float *tpwgts, float ubfactor, int npasses)
+{
+  int i, ii, iii, j, jj, k, l, pass, nvtxs, nbnd, tvwgt, myndegrees, oldgain, gain; 
+  int from, me, to, oldcut, vwgt;
+  idxtype *xadj, *adjncy, *adjwgt;
+  idxtype *where, *pwgts, *perm, *bndptr, *bndind, *minwgt, *maxwgt, *moved, *itpwgts;
+  EDegreeType *myedegrees;
+  RInfoType *myrinfo;
+  PQueueType queue;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  bndind = graph->bndind;
+  bndptr = graph->bndptr;
+
+  where = graph->where;
+  pwgts = graph->pwgts;
+  
+  /* Setup the weight intervals of the various subdomains */
+  minwgt =  idxwspacemalloc(ctrl, nparts);
+  maxwgt = idxwspacemalloc(ctrl, nparts);
+  itpwgts = idxwspacemalloc(ctrl, nparts);
+  tvwgt = idxsum(nparts, pwgts);
+  ASSERT(tvwgt == idxsum(nvtxs, graph->vwgt));
+
+  for (i=0; i<nparts; i++) {
+    itpwgts[i] = tpwgts[i]*tvwgt;
+    maxwgt[i] = tpwgts[i]*tvwgt*ubfactor;
+    minwgt[i] = tpwgts[i]*tvwgt*(1.0/ubfactor);
+  }
+
+  perm = idxwspacemalloc(ctrl, nvtxs);
+  moved = idxwspacemalloc(ctrl, nvtxs);
+
+  PQueueInit(ctrl, &queue, nvtxs, graph->adjwgtsum[idxamax(nvtxs, graph->adjwgtsum)]);
+
+  IFSET(ctrl->dbglvl, DBG_REFINE,
+     printf("Partitions: [%6d %6d]-[%6d %6d], Balance: %5.3f, Nv-Nb[%6d %6d]. Cut: %6d\n",
+             pwgts[idxamin(nparts, pwgts)], pwgts[idxamax(nparts, pwgts)], minwgt[0], maxwgt[0], 
+             1.0*nparts*pwgts[idxamax(nparts, pwgts)]/tvwgt, graph->nvtxs, graph->nbnd,
+             graph->mincut));
+
+  for (pass=0; pass<npasses; pass++) {
+    ASSERT(ComputeCut(graph, where) == graph->mincut);
+
+    PQueueReset(&queue);
+    idxset(nvtxs, -1, moved);
+
+    oldcut = graph->mincut;
+    nbnd = graph->nbnd;
+
+    RandomPermute(nbnd, perm, 1);
+    for (ii=0; ii<nbnd; ii++) {
+      i = bndind[perm[ii]];
+      PQueueInsert(&queue, i, graph->rinfo[i].ed - graph->rinfo[i].id);
+      moved[i] = 2;
+    }
+
+    for (iii=0;;iii++) {
+      if ((i = PQueueGetMax(&queue)) == -1) 
+        break;
+      moved[i] = 1;
+
+      myrinfo = graph->rinfo+i;
+      from = where[i];
+      vwgt = graph->vwgt[i];
+
+      if (pwgts[from]-vwgt < minwgt[from]) 
+        continue;   /* This cannot be moved! */
+
+      myedegrees = myrinfo->edegrees;
+      myndegrees = myrinfo->ndegrees;
+
+      j = myrinfo->id;
+      for (k=0; k<myndegrees; k++) {
+        to = myedegrees[k].pid;
+        gain = myedegrees[k].ed-j; /* j = myrinfo->id. Allow good nodes to move */ 
+        if (pwgts[to]+vwgt <= maxwgt[to]+gain && gain >= 0)  
+          break;
+      }
+      if (k == myndegrees)
+        continue;  /* break out if you did not find a candidate */
+
+      for (j=k+1; j<myndegrees; j++) {
+        to = myedegrees[j].pid;
+        if ((myedegrees[j].ed > myedegrees[k].ed && pwgts[to]+vwgt <= maxwgt[to]) ||
+            (myedegrees[j].ed == myedegrees[k].ed && 
+             itpwgts[myedegrees[k].pid]*pwgts[to] < itpwgts[to]*pwgts[myedegrees[k].pid]))
+          k = j;
+      }
+
+      to = myedegrees[k].pid;
+
+      j = 0;
+      if (myedegrees[k].ed-myrinfo->id > 0)
+        j = 1;
+      else if (myedegrees[k].ed-myrinfo->id == 0) {
+        if ((iii&7) == 0 || pwgts[from] >= maxwgt[from] || itpwgts[from]*(pwgts[to]+vwgt) < itpwgts[to]*pwgts[from])
+          j = 1;
+      }
+      if (j == 0)
+        continue;
+          
+      /*=====================================================================
+      * If we got here, we can now move the vertex from 'from' to 'to' 
+      *======================================================================*/
+      graph->mincut -= myedegrees[k].ed-myrinfo->id;
+
+      IFSET(ctrl->dbglvl, DBG_MOVEINFO, printf("\t\tMoving %6d to %3d. Gain: %4d. Cut: %6d\n", i, to, myedegrees[k].ed-myrinfo->id, graph->mincut));
+
+      /* Update where, weight, and ID/ED information of the vertex you moved */
+      where[i] = to;
+      INC_DEC(pwgts[to], pwgts[from], vwgt);
+      myrinfo->ed += myrinfo->id-myedegrees[k].ed;
+      SWAP(myrinfo->id, myedegrees[k].ed, j);
+      if (myedegrees[k].ed == 0) 
+        myedegrees[k] = myedegrees[--myrinfo->ndegrees];
+      else
+        myedegrees[k].pid = from;
+
+      if (myrinfo->ed < myrinfo->id)
+        BNDDelete(nbnd, bndind, bndptr, i);
+
+      /* Update the degrees of adjacent vertices */
+      for (j=xadj[i]; j<xadj[i+1]; j++) {
+        ii = adjncy[j];
+        me = where[ii];
+
+        myrinfo = graph->rinfo+ii;
+        if (myrinfo->edegrees == NULL) {
+          myrinfo->edegrees = ctrl->wspace.edegrees+ctrl->wspace.cdegree;
+          ctrl->wspace.cdegree += xadj[ii+1]-xadj[ii];
+        }
+        myedegrees = myrinfo->edegrees;
+
+        ASSERT(CheckRInfo(myrinfo));
+
+        oldgain = (myrinfo->ed-myrinfo->id);
+
+        if (me == from) {
+          INC_DEC(myrinfo->ed, myrinfo->id, adjwgt[j]);
+
+          if (myrinfo->ed-myrinfo->id >= 0 && bndptr[ii] == -1)
+            BNDInsert(nbnd, bndind, bndptr, ii);
+        }
+        else if (me == to) {
+          INC_DEC(myrinfo->id, myrinfo->ed, adjwgt[j]);
+
+          if (myrinfo->ed-myrinfo->id < 0 && bndptr[ii] != -1)
+            BNDDelete(nbnd, bndind, bndptr, ii);
+        }
+
+        /* Remove contribution from the .ed of 'from' */
+        if (me != from) {
+          for (k=0; k<myrinfo->ndegrees; k++) {
+            if (myedegrees[k].pid == from) {
+              if (myedegrees[k].ed == adjwgt[j])
+                myedegrees[k] = myedegrees[--myrinfo->ndegrees];
+              else
+                myedegrees[k].ed -= adjwgt[j];
+              break;
+            }
+          }
+        }
+
+        /* Add contribution to the .ed of 'to' */
+        if (me != to) {
+          for (k=0; k<myrinfo->ndegrees; k++) {
+            if (myedegrees[k].pid == to) {
+              myedegrees[k].ed += adjwgt[j];
+              break;
+            }
+          }
+          if (k == myrinfo->ndegrees) {
+            myedegrees[myrinfo->ndegrees].pid = to;
+            myedegrees[myrinfo->ndegrees++].ed = adjwgt[j];
+          }
+        }
+
+        /* Update the queue */
+        if (me == to || me == from) { 
+          gain = myrinfo->ed-myrinfo->id;
+          if (moved[ii] == 2) {
+            if (gain >= 0)
+              PQueueUpdate(&queue, ii, oldgain, gain);
+            else {
+              PQueueDelete(&queue, ii, oldgain);
+              moved[ii] = -1;
+            }
+          }
+          else if (moved[ii] == -1 && gain >= 0) {
+            PQueueInsert(&queue, ii, gain);
+            moved[ii] = 2;
+          }
+        } 
+
+        ASSERT(myrinfo->ndegrees <= xadj[ii+1]-xadj[ii]);
+        ASSERT(CheckRInfo(myrinfo));
+
+      }
+    }
+
+    graph->nbnd = nbnd;
+
+    IFSET(ctrl->dbglvl, DBG_REFINE,
+       printf("\t[%6d %6d], Balance: %5.3f, Nb: %6d. Cut: %6d\n",
+               pwgts[idxamin(nparts, pwgts)], pwgts[idxamax(nparts, pwgts)],
+               1.0*nparts*pwgts[idxamax(nparts, pwgts)]/tvwgt, graph->nbnd, graph->mincut));
+
+    if (graph->mincut == oldcut)
+      break;
+  }
+
+  PQueueFree(ctrl, &queue);
+
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+
+}
+
+
+/*************************************************************************
+* This function performs k-way refinement
+**************************************************************************/
+void Greedy_KWayEdgeBalance(CtrlType *ctrl, GraphType *graph, int nparts, float *tpwgts, float ubfactor, int npasses)
+{
+  int i, ii, iii, j, jj, k, l, pass, nvtxs, nbnd, tvwgt, myndegrees, oldgain, gain, nmoves; 
+  int from, me, to, oldcut, vwgt;
+  idxtype *xadj, *adjncy, *adjwgt;
+  idxtype *where, *pwgts, *perm, *bndptr, *bndind, *minwgt, *maxwgt, *moved, *itpwgts;
+  EDegreeType *myedegrees;
+  RInfoType *myrinfo;
+  PQueueType queue;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  bndind = graph->bndind;
+  bndptr = graph->bndptr;
+
+  where = graph->where;
+  pwgts = graph->pwgts;
+  
+  /* Setup the weight intervals of the various subdomains */
+  minwgt =  idxwspacemalloc(ctrl, nparts);
+  maxwgt = idxwspacemalloc(ctrl, nparts);
+  itpwgts = idxwspacemalloc(ctrl, nparts);
+  tvwgt = idxsum(nparts, pwgts);
+  ASSERT(tvwgt == idxsum(nvtxs, graph->vwgt));
+
+  for (i=0; i<nparts; i++) {
+    itpwgts[i] = tpwgts[i]*tvwgt;
+    maxwgt[i] = tpwgts[i]*tvwgt*ubfactor;
+    minwgt[i] = tpwgts[i]*tvwgt*(1.0/ubfactor);
+  }
+
+  perm = idxwspacemalloc(ctrl, nvtxs);
+  moved = idxwspacemalloc(ctrl, nvtxs);
+
+  PQueueInit(ctrl, &queue, nvtxs, graph->adjwgtsum[idxamax(nvtxs, graph->adjwgtsum)]);
+
+  IFSET(ctrl->dbglvl, DBG_REFINE,
+     printf("Partitions: [%6d %6d]-[%6d %6d], Balance: %5.3f, Nv-Nb[%6d %6d]. Cut: %6d [B]\n",
+             pwgts[idxamin(nparts, pwgts)], pwgts[idxamax(nparts, pwgts)], minwgt[0], maxwgt[0], 
+             1.0*nparts*pwgts[idxamax(nparts, pwgts)]/tvwgt, graph->nvtxs, graph->nbnd,
+             graph->mincut));
+
+  for (pass=0; pass<npasses; pass++) {
+    ASSERT(ComputeCut(graph, where) == graph->mincut);
+
+    /* Check to see if things are out of balance, given the tolerance */
+    for (i=0; i<nparts; i++) {
+      if (pwgts[i] > maxwgt[i])
+        break;
+    }
+    if (i == nparts) /* Things are balanced. Return right away */
+      break;
+
+    PQueueReset(&queue);
+    idxset(nvtxs, -1, moved);
+
+    oldcut = graph->mincut;
+    nbnd = graph->nbnd;
+
+    RandomPermute(nbnd, perm, 1);
+    for (ii=0; ii<nbnd; ii++) {
+      i = bndind[perm[ii]];
+      PQueueInsert(&queue, i, graph->rinfo[i].ed - graph->rinfo[i].id);
+      moved[i] = 2;
+    }
+
+    nmoves = 0;
+    for (;;) {
+      if ((i = PQueueGetMax(&queue)) == -1) 
+        break;
+      moved[i] = 1;
+
+      myrinfo = graph->rinfo+i;
+      from = where[i];
+      vwgt = graph->vwgt[i];
+
+      if (pwgts[from]-vwgt < minwgt[from]) 
+        continue;   /* This cannot be moved! */
+
+      myedegrees = myrinfo->edegrees;
+      myndegrees = myrinfo->ndegrees;
+
+      for (k=0; k<myndegrees; k++) {
+        to = myedegrees[k].pid;
+        if (pwgts[to]+vwgt <= maxwgt[to] || itpwgts[from]*(pwgts[to]+vwgt) <= itpwgts[to]*pwgts[from]) 
+          break;
+      }
+      if (k == myndegrees)
+        continue;  /* break out if you did not find a candidate */
+
+      for (j=k+1; j<myndegrees; j++) {
+        to = myedegrees[j].pid;
+        if (itpwgts[myedegrees[k].pid]*pwgts[to] < itpwgts[to]*pwgts[myedegrees[k].pid]) 
+          k = j;
+      }
+
+      to = myedegrees[k].pid;
+
+      if (pwgts[from] < maxwgt[from] && pwgts[to] > minwgt[to] && myedegrees[k].ed-myrinfo->id < 0) 
+        continue;
+
+      /*=====================================================================
+      * If we got here, we can now move the vertex from 'from' to 'to' 
+      *======================================================================*/
+      graph->mincut -= myedegrees[k].ed-myrinfo->id;
+
+      IFSET(ctrl->dbglvl, DBG_MOVEINFO, printf("\t\tMoving %6d to %3d. Gain: %4d. Cut: %6d\n", i, to, myedegrees[k].ed-myrinfo->id, graph->mincut));
+
+      /* Update where, weight, and ID/ED information of the vertex you moved */
+      where[i] = to;
+      INC_DEC(pwgts[to], pwgts[from], vwgt);
+      myrinfo->ed += myrinfo->id-myedegrees[k].ed;
+      SWAP(myrinfo->id, myedegrees[k].ed, j);
+      if (myedegrees[k].ed == 0) 
+        myedegrees[k] = myedegrees[--myrinfo->ndegrees];
+      else
+        myedegrees[k].pid = from;
+
+      if (myrinfo->ed == 0)
+        BNDDelete(nbnd, bndind, bndptr, i);
+
+      /* Update the degrees of adjacent vertices */
+      for (j=xadj[i]; j<xadj[i+1]; j++) {
+        ii = adjncy[j];
+        me = where[ii];
+
+        myrinfo = graph->rinfo+ii;
+        if (myrinfo->edegrees == NULL) {
+          myrinfo->edegrees = ctrl->wspace.edegrees+ctrl->wspace.cdegree;
+          ctrl->wspace.cdegree += xadj[ii+1]-xadj[ii];
+        }
+        myedegrees = myrinfo->edegrees;
+
+        ASSERT(CheckRInfo(myrinfo));
+
+        oldgain = (myrinfo->ed-myrinfo->id);
+
+        if (me == from) {
+          INC_DEC(myrinfo->ed, myrinfo->id, adjwgt[j]);
+
+          if (myrinfo->ed > 0 && bndptr[ii] == -1)
+            BNDInsert(nbnd, bndind, bndptr, ii);
+        }
+        else if (me == to) {
+          INC_DEC(myrinfo->id, myrinfo->ed, adjwgt[j]);
+
+          if (myrinfo->ed == 0 && bndptr[ii] != -1)
+            BNDDelete(nbnd, bndind, bndptr, ii);
+        }
+
+        /* Remove contribution from the .ed of 'from' */
+        if (me != from) {
+          for (k=0; k<myrinfo->ndegrees; k++) {
+            if (myedegrees[k].pid == from) {
+              if (myedegrees[k].ed == adjwgt[j])
+                myedegrees[k] = myedegrees[--myrinfo->ndegrees];
+              else
+                myedegrees[k].ed -= adjwgt[j];
+              break;
+            }
+          }
+        }
+
+        /* Add contribution to the .ed of 'to' */
+        if (me != to) {
+          for (k=0; k<myrinfo->ndegrees; k++) {
+            if (myedegrees[k].pid == to) {
+              myedegrees[k].ed += adjwgt[j];
+              break;
+            }
+          }
+          if (k == myrinfo->ndegrees) {
+            myedegrees[myrinfo->ndegrees].pid = to;
+            myedegrees[myrinfo->ndegrees++].ed = adjwgt[j];
+          }
+        }
+
+        /* Update the queue */
+        if (me == to || me == from) { 
+          gain = myrinfo->ed-myrinfo->id;
+          if (moved[ii] == 2) {
+            if (myrinfo->ed > 0)
+              PQueueUpdate(&queue, ii, oldgain, gain);
+            else {
+              PQueueDelete(&queue, ii, oldgain);
+              moved[ii] = -1;
+            }
+          }
+          else if (moved[ii] == -1 && myrinfo->ed > 0) {
+            PQueueInsert(&queue, ii, gain);
+            moved[ii] = 2;
+          }
+        } 
+
+        ASSERT(myrinfo->ndegrees <= xadj[ii+1]-xadj[ii]);
+        ASSERT(CheckRInfo(myrinfo));
+      }
+      nmoves++;
+    }
+
+    graph->nbnd = nbnd;
+
+    IFSET(ctrl->dbglvl, DBG_REFINE,
+       printf("\t[%6d %6d], Balance: %5.3f, Nb: %6d. Nmoves: %5d, Cut: %6d\n",
+               pwgts[idxamin(nparts, pwgts)], pwgts[idxamax(nparts, pwgts)],
+               1.0*nparts*pwgts[idxamax(nparts, pwgts)]/tvwgt, graph->nbnd, nmoves, graph->mincut));
+  }
+
+  PQueueFree(ctrl, &queue);
+
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+
+}
+
diff --git a/contrib/Metis/kwayrefine.c b/contrib/Metis/kwayrefine.c
new file mode 100644
index 0000000000..2783e6cad4
--- /dev/null
+++ b/contrib/Metis/kwayrefine.c
@@ -0,0 +1,392 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * kwayrefine.c
+ *
+ * This file contains the driving routines for multilevel k-way refinement
+ *
+ * Started 7/28/97
+ * George
+ *
+ * $Id: kwayrefine.c,v 1.1 2005-09-21 17:29:37 geuzaine Exp $
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function is the entry point of refinement
+**************************************************************************/
+void RefineKWay(CtrlType *ctrl, GraphType *orggraph, GraphType *graph, int nparts, float *tpwgts, float ubfactor)
+{
+  int i, nlevels, mustfree=0;
+  GraphType *ptr;
+
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->UncoarsenTmr));
+
+  /* Compute the parameters of the coarsest graph */
+  ComputeKWayPartitionParams(ctrl, graph, nparts);
+
+  /* Take care any non-contiguity */
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->AuxTmr1));
+  if (ctrl->RType == RTYPE_KWAYRANDOM_MCONN) {
+    EliminateComponents(ctrl, graph, nparts, tpwgts, 1.25);
+    EliminateSubDomainEdges(ctrl, graph, nparts, tpwgts);
+    EliminateComponents(ctrl, graph, nparts, tpwgts, 1.25);
+  }
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->AuxTmr1));
+
+  /* Determine how many levels are there */
+  for (ptr=graph, nlevels=0; ptr!=orggraph; ptr=ptr->finer, nlevels++); 
+
+  for (i=0; ;i++) {
+    /* PrintSubDomainGraph(graph, nparts, graph->where); */
+    if (ctrl->RType == RTYPE_KWAYRANDOM_MCONN && (i == nlevels/2 || i == nlevels/2+1))
+      EliminateSubDomainEdges(ctrl, graph, nparts, tpwgts);
+
+    IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->RefTmr));
+
+    if (2*i >= nlevels && !IsBalanced(graph->pwgts, nparts, tpwgts, 1.04*ubfactor)) {
+      ComputeKWayBalanceBoundary(ctrl, graph, nparts);
+      if (ctrl->RType == RTYPE_KWAYRANDOM_MCONN)
+        Greedy_KWayEdgeBalanceMConn(ctrl, graph, nparts, tpwgts, ubfactor, 1); 
+      else
+        Greedy_KWayEdgeBalance(ctrl, graph, nparts, tpwgts, ubfactor, 1); 
+      ComputeKWayBoundary(ctrl, graph, nparts);
+    }
+
+    switch (ctrl->RType) {
+      case RTYPE_KWAYRANDOM:
+        Random_KWayEdgeRefine(ctrl, graph, nparts, tpwgts, ubfactor, 10, 1); 
+        break;
+      case RTYPE_KWAYGREEDY:
+        Greedy_KWayEdgeRefine(ctrl, graph, nparts, tpwgts, ubfactor, 10); 
+        break;
+      case RTYPE_KWAYRANDOM_MCONN:
+        Random_KWayEdgeRefineMConn(ctrl, graph, nparts, tpwgts, ubfactor, 10, 1); 
+        break;
+    }
+    IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->RefTmr));
+
+    if (graph == orggraph)
+      break;
+
+    GKfree(&graph->gdata, LTERM);  /* Deallocate the graph related arrays */
+
+    graph = graph->finer;
+
+    IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->ProjectTmr));
+    if (graph->vwgt == NULL) {
+      graph->vwgt = idxsmalloc(graph->nvtxs, 1, "RefineKWay: graph->vwgt");
+      graph->adjwgt = idxsmalloc(graph->nedges, 1, "RefineKWay: graph->adjwgt");
+      mustfree = 1;
+    }
+    ProjectKWayPartition(ctrl, graph, nparts);
+    IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->ProjectTmr));
+  }
+
+  if (!IsBalanced(graph->pwgts, nparts, tpwgts, ubfactor)) {
+    ComputeKWayBalanceBoundary(ctrl, graph, nparts);
+    if (ctrl->RType == RTYPE_KWAYRANDOM_MCONN) {
+      Greedy_KWayEdgeBalanceMConn(ctrl, graph, nparts, tpwgts, ubfactor, 8); 
+      Random_KWayEdgeRefineMConn(ctrl, graph, nparts, tpwgts, ubfactor, 10, 0); 
+    }
+    else {
+      Greedy_KWayEdgeBalance(ctrl, graph, nparts, tpwgts, ubfactor, 8); 
+      Random_KWayEdgeRefine(ctrl, graph, nparts, tpwgts, ubfactor, 10, 0); 
+    }
+  }
+
+  /* Take care any trivial non-contiguity */
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->AuxTmr2));
+  EliminateComponents(ctrl, graph, nparts, tpwgts, ubfactor);
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->AuxTmr2));
+
+  if (mustfree) 
+    GKfree(&graph->vwgt, &graph->adjwgt, LTERM);
+
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->UncoarsenTmr));
+}
+
+
+/*************************************************************************
+* This function allocates memory for k-way edge refinement
+**************************************************************************/
+void AllocateKWayPartitionMemory(CtrlType *ctrl, GraphType *graph, int nparts)
+{
+  int nvtxs, pad64;
+
+  nvtxs = graph->nvtxs;
+
+  pad64 = (3*nvtxs+nparts)%2;
+
+  graph->rdata = idxmalloc(3*nvtxs+nparts+(sizeof(RInfoType)/sizeof(idxtype))*nvtxs+pad64, "AllocateKWayPartitionMemory: rdata");
+  graph->pwgts          = graph->rdata;
+  graph->where          = graph->rdata + nparts;
+  graph->bndptr         = graph->rdata + nvtxs + nparts;
+  graph->bndind         = graph->rdata + 2*nvtxs + nparts;
+  graph->rinfo          = (RInfoType *)(graph->rdata + 3*nvtxs+nparts + pad64);
+
+/*
+  if (ctrl->wspace.edegrees != NULL)
+    free(ctrl->wspace.edegrees);
+  ctrl->wspace.edegrees = (EDegreeType *)GKmalloc(graph->nedges*sizeof(EDegreeType), "AllocateKWayPartitionMemory: edegrees");
+*/
+}
+
+
+/*************************************************************************
+* This function computes the initial id/ed 
+**************************************************************************/
+void ComputeKWayPartitionParams(CtrlType *ctrl, GraphType *graph, int nparts)
+{
+  int i, j, k, l, nvtxs, nbnd, mincut, me, other;
+  idxtype *xadj, *vwgt, *adjncy, *adjwgt, *pwgts, *where, *bndind, *bndptr;
+  RInfoType *rinfo, *myrinfo;
+  EDegreeType *myedegrees;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  vwgt = graph->vwgt;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  where = graph->where;
+  pwgts = idxset(nparts, 0, graph->pwgts);
+  bndind = graph->bndind;
+  bndptr = idxset(nvtxs, -1, graph->bndptr);
+  rinfo = graph->rinfo;
+
+
+  /*------------------------------------------------------------
+  / Compute now the id/ed degrees
+  /------------------------------------------------------------*/
+  ctrl->wspace.cdegree = 0;
+  nbnd = mincut = 0;
+  for (i=0; i<nvtxs; i++) {
+    me = where[i];
+    pwgts[me] += vwgt[i];
+
+    myrinfo = rinfo+i;
+    myrinfo->id = myrinfo->ed = myrinfo->ndegrees = 0;
+    myrinfo->edegrees = NULL;
+
+    for (j=xadj[i]; j<xadj[i+1]; j++) {
+      if (me != where[adjncy[j]])
+        myrinfo->ed += adjwgt[j];
+    }
+    myrinfo->id = graph->adjwgtsum[i] - myrinfo->ed;
+
+    if (myrinfo->ed > 0) 
+      mincut += myrinfo->ed;
+
+    if (myrinfo->ed-myrinfo->id >= 0)
+      BNDInsert(nbnd, bndind, bndptr, i);
+
+    /* Time to compute the particular external degrees */
+    if (myrinfo->ed > 0) { 
+      myedegrees = myrinfo->edegrees = ctrl->wspace.edegrees+ctrl->wspace.cdegree;
+      ctrl->wspace.cdegree += xadj[i+1]-xadj[i];
+
+      for (j=xadj[i]; j<xadj[i+1]; j++) {
+        other = where[adjncy[j]];
+        if (me != other) {
+          for (k=0; k<myrinfo->ndegrees; k++) {
+            if (myedegrees[k].pid == other) {
+              myedegrees[k].ed += adjwgt[j];
+              break;
+            }
+          }
+          if (k == myrinfo->ndegrees) {
+            myedegrees[myrinfo->ndegrees].pid = other;
+            myedegrees[myrinfo->ndegrees++].ed = adjwgt[j];
+          }
+        }
+      }
+
+      ASSERT(myrinfo->ndegrees <= xadj[i+1]-xadj[i]);
+    }
+  }
+
+  graph->mincut = mincut/2;
+  graph->nbnd = nbnd;
+
+}
+
+
+
+/*************************************************************************
+* This function projects a partition, and at the same time computes the
+* parameters for refinement.
+**************************************************************************/
+void ProjectKWayPartition(CtrlType *ctrl, GraphType *graph, int nparts)
+{
+  int i, j, k, nvtxs, nbnd, me, other, istart, iend, ndegrees;
+  idxtype *xadj, *adjncy, *adjwgt, *adjwgtsum;
+  idxtype *cmap, *where, *bndptr, *bndind;
+  idxtype *cwhere;
+  GraphType *cgraph;
+  RInfoType *crinfo, *rinfo, *myrinfo;
+  EDegreeType *myedegrees;
+  idxtype *htable;
+
+  cgraph = graph->coarser;
+  cwhere = cgraph->where;
+  crinfo = cgraph->rinfo;
+
+  nvtxs = graph->nvtxs;
+  cmap = graph->cmap;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+  adjwgtsum = graph->adjwgtsum;
+
+  AllocateKWayPartitionMemory(ctrl, graph, nparts);
+  where = graph->where;
+  rinfo = graph->rinfo;
+  bndind = graph->bndind;
+  bndptr = idxset(nvtxs, -1, graph->bndptr);
+
+  /* Go through and project partition and compute id/ed for the nodes */
+  for (i=0; i<nvtxs; i++) {
+    k = cmap[i];
+    where[i] = cwhere[k];
+    cmap[i] = crinfo[k].ed;  /* For optimization */
+  }
+
+  htable = idxset(nparts, -1, idxwspacemalloc(ctrl, nparts));
+
+  ctrl->wspace.cdegree = 0;
+  for (nbnd=0, i=0; i<nvtxs; i++) {
+    me = where[i];
+
+    myrinfo = rinfo+i;
+    myrinfo->id = myrinfo->ed = myrinfo->ndegrees = 0;
+    myrinfo->edegrees = NULL;
+
+    myrinfo->id = adjwgtsum[i];
+
+    if (cmap[i] > 0) { /* If it is an interface node. Note cmap[i] = crinfo[cmap[i]].ed */
+      istart = xadj[i];
+      iend = xadj[i+1];
+
+      myedegrees = myrinfo->edegrees = ctrl->wspace.edegrees+ctrl->wspace.cdegree;
+      ctrl->wspace.cdegree += iend-istart;
+
+      ndegrees = 0;
+      for (j=istart; j<iend; j++) {
+        other = where[adjncy[j]];
+        if (me != other) {
+          myrinfo->ed += adjwgt[j];
+          if ((k = htable[other]) == -1) {
+            htable[other] = ndegrees;
+            myedegrees[ndegrees].pid = other;
+            myedegrees[ndegrees++].ed = adjwgt[j];
+          }
+          else {
+            myedegrees[k].ed += adjwgt[j];
+          }
+        }
+      }
+      myrinfo->id -= myrinfo->ed;
+
+      /* Remove space for edegrees if it was interior */
+      if (myrinfo->ed == 0) { 
+        myrinfo->edegrees = NULL;
+        ctrl->wspace.cdegree -= iend-istart;
+      }
+      else {
+        if (myrinfo->ed-myrinfo->id >= 0) 
+          BNDInsert(nbnd, bndind, bndptr, i); 
+
+        myrinfo->ndegrees = ndegrees;
+
+        for (j=0; j<ndegrees; j++)
+          htable[myedegrees[j].pid] = -1;
+      }
+    }
+  }
+
+  idxcopy(nparts, cgraph->pwgts, graph->pwgts);
+  graph->mincut = cgraph->mincut;
+  graph->nbnd = nbnd;
+
+  FreeGraph(graph->coarser);
+  graph->coarser = NULL;
+
+  idxwspacefree(ctrl, nparts);
+
+  ASSERT(CheckBnd2(graph));
+
+}
+
+
+
+/*************************************************************************
+* This function checks if the partition weights are within the balance
+* contraints
+**************************************************************************/
+int IsBalanced(idxtype *pwgts, int nparts, float *tpwgts, float ubfactor)
+{
+  int i, j, tvwgt;
+
+  tvwgt = idxsum(nparts, pwgts);
+  for (i=0; i<nparts; i++) {
+    if (pwgts[i] > tpwgts[i]*tvwgt*(ubfactor+0.005))
+      return 0;
+  }
+
+  return 1;
+}
+
+
+/*************************************************************************
+* This function computes the boundary definition for balancing
+**************************************************************************/
+void ComputeKWayBoundary(CtrlType *ctrl, GraphType *graph, int nparts)
+{
+  int i, nvtxs, nbnd;
+  idxtype *bndind, *bndptr;
+
+  nvtxs = graph->nvtxs;
+  bndind = graph->bndind;
+  bndptr = idxset(nvtxs, -1, graph->bndptr);
+
+
+  /*------------------------------------------------------------
+  / Compute the new boundary
+  /------------------------------------------------------------*/
+  nbnd = 0;
+  for (i=0; i<nvtxs; i++) {
+    if (graph->rinfo[i].ed-graph->rinfo[i].id >= 0) 
+      BNDInsert(nbnd, bndind, bndptr, i);
+  }
+
+  graph->nbnd = nbnd;
+}
+
+/*************************************************************************
+* This function computes the boundary definition for balancing
+**************************************************************************/
+void ComputeKWayBalanceBoundary(CtrlType *ctrl, GraphType *graph, int nparts)
+{
+  int i, nvtxs, nbnd;
+  idxtype *bndind, *bndptr;
+
+  nvtxs = graph->nvtxs;
+  bndind = graph->bndind;
+  bndptr = idxset(nvtxs, -1, graph->bndptr);
+
+
+  /*------------------------------------------------------------
+  / Compute the new boundary
+  /------------------------------------------------------------*/
+  nbnd = 0;
+  for (i=0; i<nvtxs; i++) {
+    if (graph->rinfo[i].ed > 0) 
+      BNDInsert(nbnd, bndind, bndptr, i);
+  }
+
+  graph->nbnd = nbnd;
+}
+
diff --git a/contrib/Metis/kwayvolfm.c b/contrib/Metis/kwayvolfm.c
new file mode 100644
index 0000000000..3271571ff1
--- /dev/null
+++ b/contrib/Metis/kwayvolfm.c
@@ -0,0 +1,1778 @@
+/*
+ * kwayvolfm.c
+ *
+ * This file contains code that implements the multilevel k-way refinement
+ *
+ * Started 7/8/98
+ * George
+ *
+ * $Id: kwayvolfm.c,v 1.1 2005-09-21 17:29:37 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function performs k-way refinement
+**************************************************************************/
+void Random_KWayVolRefine(CtrlType *ctrl, GraphType *graph, int nparts, float *tpwgts, 
+                          float ubfactor, int npasses, int ffactor)
+{
+  int i, ii, iii, j, jj, k, kk, l, u, pass, nvtxs, nmoves, tvwgt, myndegrees, xgain; 
+  int from, me, to, oldcut, oldvol, vwgt;
+  idxtype *xadj, *adjncy, *adjwgt;
+  idxtype *where, *pwgts, *perm, *bndptr, *bndind, *minwgt, *maxwgt, *itpwgts, *updind, *marker, *phtable;
+  VEDegreeType *myedegrees;
+  VRInfoType *myrinfo; 
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  bndptr = graph->bndptr;
+  bndind = graph->bndind;
+
+  where = graph->where;
+  pwgts = graph->pwgts;
+  
+  /* Setup the weight intervals of the various subdomains */
+  minwgt =  idxwspacemalloc(ctrl, nparts);
+  maxwgt = idxwspacemalloc(ctrl, nparts);
+  itpwgts = idxwspacemalloc(ctrl, nparts);
+  tvwgt = idxsum(nparts, pwgts);
+  ASSERT(tvwgt == idxsum(nvtxs, graph->vwgt));
+
+  updind = idxmalloc(nvtxs, "Random_KWayVolRefine: updind");
+  marker = idxsmalloc(nvtxs, 0, "Random_KWayVolRefine: marker");
+  phtable = idxsmalloc(nparts, -1, "Random_KWayVolRefine: phtable");
+
+  for (i=0; i<nparts; i++) {
+    itpwgts[i] = tpwgts[i]*tvwgt;
+    maxwgt[i] = tpwgts[i]*tvwgt*ubfactor;
+    minwgt[i] = tpwgts[i]*tvwgt*(1.0/ubfactor);
+  }
+
+  perm = idxwspacemalloc(ctrl, nvtxs);
+
+  IFSET(ctrl->dbglvl, DBG_REFINE,
+     printf("VolPart: [%5d %5d]-[%5d %5d], Balance: %3.2f, Nv-Nb[%5d %5d]. Cut: %5d, Vol: %5d\n",
+             pwgts[idxamin(nparts, pwgts)], pwgts[idxamax(nparts, pwgts)], minwgt[0], maxwgt[0], 
+             1.0*nparts*pwgts[idxamax(nparts, pwgts)]/tvwgt, graph->nvtxs, graph->nbnd,
+             graph->mincut, graph->minvol));
+
+  for (pass=0; pass<npasses; pass++) {
+    ASSERT(ComputeCut(graph, where) == graph->mincut);
+
+    oldcut = graph->mincut;
+    oldvol = graph->minvol;
+
+    RandomPermute(graph->nbnd, perm, 1);
+    for (nmoves=iii=0; iii<graph->nbnd; iii++) {
+      ii = perm[iii];
+      if (ii >= graph->nbnd)
+        continue;
+      i = bndind[ii];
+      myrinfo = graph->vrinfo+i;
+
+      if (myrinfo->gv >= 0) { /* Total volume gain is too high */
+        from = where[i];
+        vwgt = graph->vwgt[i];
+
+        if (myrinfo->id > 0 && pwgts[from]-vwgt < minwgt[from]) 
+          continue;   /* This cannot be moved! */
+
+        xgain = (myrinfo->id == 0 && myrinfo->ed > 0 ? graph->vsize[i] : 0);
+
+        myedegrees = myrinfo->edegrees;
+        myndegrees = myrinfo->ndegrees;
+
+        for (k=0; k<myndegrees; k++) {
+          to = myedegrees[k].pid;
+          if (pwgts[to]+vwgt <= maxwgt[to]+ffactor*myedegrees[k].gv && xgain+myedegrees[k].gv >= 0)  
+            break;
+        }
+        if (k == myndegrees)
+          continue;  /* break out if you did not find a candidate */
+
+        for (j=k+1; j<myndegrees; j++) {
+          to = myedegrees[j].pid;
+          if (pwgts[to]+vwgt > maxwgt[to])
+            continue;
+          if (myedegrees[j].gv > myedegrees[k].gv ||
+              (myedegrees[j].gv == myedegrees[k].gv && myedegrees[j].ed > myedegrees[k].ed) ||
+              (myedegrees[j].gv == myedegrees[k].gv && myedegrees[j].ed == myedegrees[k].ed &&
+               itpwgts[myedegrees[k].pid]*pwgts[to] < itpwgts[to]*pwgts[myedegrees[k].pid]))
+            k = j;
+        }
+
+        to = myedegrees[k].pid;
+
+        j = 0;
+        if (xgain+myedegrees[k].gv > 0 || myedegrees[k].ed-myrinfo->id > 0)
+          j = 1;
+        else if (myedegrees[k].ed-myrinfo->id == 0) {
+          if ((iii&5) == 0 || pwgts[from] >= maxwgt[from] || itpwgts[from]*(pwgts[to]+vwgt) < itpwgts[to]*pwgts[from])
+            j = 1;
+        }
+        if (j == 0)
+          continue;
+          
+        /*=====================================================================
+        * If we got here, we can now move the vertex from 'from' to 'to' 
+        *======================================================================*/
+        INC_DEC(pwgts[to], pwgts[from], vwgt);
+        graph->mincut -= myedegrees[k].ed-myrinfo->id;
+        graph->minvol -= (xgain+myedegrees[k].gv);
+        where[i] = to;
+
+        IFSET(ctrl->dbglvl, DBG_MOVEINFO, printf("\t\tMoving %6d from %3d to %3d. Gain: [%4d %4d]. Cut: %6d, Vol: %6d\n", 
+              i, from, to, xgain+myedegrees[k].gv, myedegrees[k].ed-myrinfo->id, graph->mincut, graph->minvol));
+
+        KWayVolUpdate(ctrl, graph, i, from, to, marker, phtable, updind);
+
+        nmoves++;
+
+        /* CheckVolKWayPartitionParams(ctrl, graph, nparts); */
+      }
+    }
+
+    IFSET(ctrl->dbglvl, DBG_REFINE,
+       printf("\t[%6d %6d], Balance: %5.3f, Nb: %6d. Nmoves: %5d, Cut: %6d, Vol: %6d\n",
+               pwgts[idxamin(nparts, pwgts)], pwgts[idxamax(nparts, pwgts)],
+               1.0*nparts*pwgts[idxamax(nparts, pwgts)]/tvwgt, graph->nbnd, nmoves, graph->mincut, 
+               graph->minvol));
+
+    if (graph->minvol == oldvol && graph->mincut == oldcut)
+      break;
+  }
+
+  GKfree(&marker, &updind, &phtable, LTERM);
+
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nvtxs);
+}
+
+
+/*************************************************************************
+* This function performs k-way refinement
+**************************************************************************/
+void Random_KWayVolRefineMConn(CtrlType *ctrl, GraphType *graph, int nparts, float *tpwgts, 
+            float ubfactor, int npasses, int ffactor)
+{
+  int i, ii, iii, j, jj, k, kk, l, u, pass, nvtxs, nmoves, tvwgt, myndegrees, xgain; 
+  int from, me, to, oldcut, oldvol, vwgt, nadd, maxndoms;
+  idxtype *xadj, *adjncy, *adjwgt;
+  idxtype *where, *pwgts, *perm, *bndptr, *bndind, *minwgt, *maxwgt, *itpwgts, *updind, *marker, *phtable;
+  idxtype *pmat, *pmatptr, *ndoms;
+  VEDegreeType *myedegrees;
+  VRInfoType *myrinfo; 
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  bndptr = graph->bndptr;
+  bndind = graph->bndind;
+
+  where = graph->where;
+  pwgts = graph->pwgts;
+  
+  /* Setup the weight intervals of the various subdomains */
+  minwgt =  idxwspacemalloc(ctrl, nparts);
+  maxwgt = idxwspacemalloc(ctrl, nparts);
+  itpwgts = idxwspacemalloc(ctrl, nparts);
+  tvwgt = idxsum(nparts, pwgts);
+  ASSERT(tvwgt == idxsum(nvtxs, graph->vwgt));
+
+  updind = idxmalloc(nvtxs, "Random_KWayVolRefine: updind");
+  marker = idxsmalloc(nvtxs, 0, "Random_KWayVolRefine: marker");
+  phtable = idxsmalloc(nparts, -1, "Random_KWayVolRefine: phtable");
+
+  pmat = ctrl->wspace.pmat;
+  ndoms = idxwspacemalloc(ctrl, nparts);
+
+  ComputeVolSubDomainGraph(graph, nparts, pmat, ndoms);
+
+  for (i=0; i<nparts; i++) {
+    itpwgts[i] = tpwgts[i]*tvwgt;
+    maxwgt[i] = tpwgts[i]*tvwgt*ubfactor;
+    minwgt[i] = tpwgts[i]*tvwgt*(1.0/ubfactor);
+  }
+
+  perm = idxwspacemalloc(ctrl, nvtxs);
+
+  IFSET(ctrl->dbglvl, DBG_REFINE,
+     printf("VolPart: [%5d %5d]-[%5d %5d], Balance: %3.2f, Nv-Nb[%5d %5d]. Cut: %5d, Vol: %5d\n",
+             pwgts[idxamin(nparts, pwgts)], pwgts[idxamax(nparts, pwgts)], minwgt[0], maxwgt[0], 
+             1.0*nparts*pwgts[idxamax(nparts, pwgts)]/tvwgt, graph->nvtxs, graph->nbnd,
+             graph->mincut, graph->minvol));
+
+  for (pass=0; pass<npasses; pass++) {
+    ASSERT(ComputeCut(graph, where) == graph->mincut);
+
+    maxndoms = ndoms[idxamax(nparts, ndoms)];
+
+    oldcut = graph->mincut;
+    oldvol = graph->minvol;
+
+    RandomPermute(graph->nbnd, perm, 1);
+    for (nmoves=iii=0; iii<graph->nbnd; iii++) {
+      ii = perm[iii];
+      if (ii >= graph->nbnd)
+        continue;
+      i = bndind[ii];
+      myrinfo = graph->vrinfo+i;
+
+      if (myrinfo->gv >= 0) { /* Total volume gain is too high */
+        from = where[i];
+        vwgt = graph->vwgt[i];
+
+        if (myrinfo->id > 0 && pwgts[from]-vwgt < minwgt[from]) 
+          continue;   /* This cannot be moved! */
+
+        xgain = (myrinfo->id == 0 && myrinfo->ed > 0 ? graph->vsize[i] : 0);
+
+        myedegrees = myrinfo->edegrees;
+        myndegrees = myrinfo->ndegrees;
+
+        /* Determine the valid domains */
+        for (j=0; j<myndegrees; j++) {
+          to = myedegrees[j].pid;
+          phtable[to] = 1;
+          pmatptr = pmat + to*nparts;
+          for (nadd=0, k=0; k<myndegrees; k++) {
+            if (k == j)
+              continue;
+
+            l = myedegrees[k].pid;
+            if (pmatptr[l] == 0) {
+              if (ndoms[l] > maxndoms-1) {
+                phtable[to] = 0;
+                nadd = maxndoms;
+                break;
+              }
+              nadd++;
+            }
+          }
+          if (ndoms[to]+nadd > maxndoms)
+            phtable[to] = 0;
+          if (nadd == 0)
+            phtable[to] = 2;
+        }
+
+        for (k=0; k<myndegrees; k++) {
+          to = myedegrees[k].pid;
+          if (!phtable[to])
+            continue;
+          if (pwgts[to]+vwgt <= maxwgt[to]+ffactor*myedegrees[k].gv && xgain+myedegrees[k].gv >= 0)  
+            break;
+        }
+        if (k == myndegrees)
+          continue;  /* break out if you did not find a candidate */
+
+        for (j=k+1; j<myndegrees; j++) {
+          to = myedegrees[j].pid;
+          if (!phtable[to] || pwgts[to]+vwgt > maxwgt[to])
+            continue;
+          if (myedegrees[j].gv > myedegrees[k].gv ||
+              (myedegrees[j].gv == myedegrees[k].gv && myedegrees[j].ed > myedegrees[k].ed) ||
+              (myedegrees[j].gv == myedegrees[k].gv && myedegrees[j].ed == myedegrees[k].ed &&
+               itpwgts[myedegrees[k].pid]*pwgts[to] < itpwgts[to]*pwgts[myedegrees[k].pid]))
+            k = j;
+        }
+
+        to = myedegrees[k].pid;
+
+        j = 0;
+        if (xgain+myedegrees[k].gv > 0 || myedegrees[k].ed-myrinfo->id > 0)
+          j = 1;
+        else if (myedegrees[k].ed-myrinfo->id == 0) {
+          if ((iii&5) == 0 || phtable[myedegrees[k].pid] == 2 || pwgts[from] >= maxwgt[from] || itpwgts[from]*(pwgts[to]+vwgt) < itpwgts[to]*pwgts[from])
+            j = 1;
+        }
+
+        if (j == 0)
+          continue;
+
+        for (j=0; j<myndegrees; j++) 
+          phtable[myedegrees[j].pid] = -1;
+
+          
+        /*=====================================================================
+        * If we got here, we can now move the vertex from 'from' to 'to' 
+        *======================================================================*/
+        INC_DEC(pwgts[to], pwgts[from], vwgt);
+        graph->mincut -= myedegrees[k].ed-myrinfo->id;
+        graph->minvol -= (xgain+myedegrees[k].gv);
+        where[i] = to;
+
+        IFSET(ctrl->dbglvl, DBG_MOVEINFO, printf("\t\tMoving %6d from %3d to %3d. Gain: [%4d %4d]. Cut: %6d, Vol: %6d\n", 
+              i, from, to, xgain+myedegrees[k].gv, myedegrees[k].ed-myrinfo->id, graph->mincut, graph->minvol));
+
+        /* Update pmat to reflect the move of 'i' */
+        pmat[from*nparts+to] += (myrinfo->id-myedegrees[k].ed);
+        pmat[to*nparts+from] += (myrinfo->id-myedegrees[k].ed);
+        if (pmat[from*nparts+to] == 0) {
+          ndoms[from]--;
+          if (ndoms[from]+1 == maxndoms)
+            maxndoms = ndoms[idxamax(nparts, ndoms)];
+        }
+        if (pmat[to*nparts+from] == 0) {
+          ndoms[to]--;
+          if (ndoms[to]+1 == maxndoms)
+            maxndoms = ndoms[idxamax(nparts, ndoms)];
+        }
+
+        for (j=xadj[i]; j<xadj[i+1]; j++) {
+          ii = adjncy[j];
+          me = where[ii];
+
+          /* Update pmat to reflect the move of 'i' for domains other than 'from' and 'to' */
+          if (me != from && me != to) {
+            pmat[me*nparts+from] -= adjwgt[j];
+            pmat[from*nparts+me] -= adjwgt[j];
+            if (pmat[me*nparts+from] == 0) {
+              ndoms[me]--;
+              if (ndoms[me]+1 == maxndoms)
+                maxndoms = ndoms[idxamax(nparts, ndoms)];
+            }
+            if (pmat[from*nparts+me] == 0) {
+              ndoms[from]--;
+              if (ndoms[from]+1 == maxndoms)
+                maxndoms = ndoms[idxamax(nparts, ndoms)];
+            }
+
+            if (pmat[me*nparts+to] == 0) {
+              ndoms[me]++;
+              if (ndoms[me] > maxndoms) {
+                printf("You just increased the maxndoms: %d %d\n", ndoms[me], maxndoms);
+                maxndoms = ndoms[me];
+              }
+            }
+            if (pmat[to*nparts+me] == 0) {
+              ndoms[to]++;
+              if (ndoms[to] > maxndoms) {
+                printf("You just increased the maxndoms: %d %d\n", ndoms[to], maxndoms);
+                maxndoms = ndoms[to];
+              }
+            }
+            pmat[me*nparts+to] += adjwgt[j];
+            pmat[to*nparts+me] += adjwgt[j];
+          }
+        }
+
+        KWayVolUpdate(ctrl, graph, i, from, to, marker, phtable, updind);
+
+        nmoves++;
+
+        /* CheckVolKWayPartitionParams(ctrl, graph, nparts); */
+      }
+    }
+
+    IFSET(ctrl->dbglvl, DBG_REFINE,
+       printf("\t[%6d %6d], Balance: %5.3f, Nb: %6d. Nmoves: %5d, Cut: %6d, Vol: %6d\n",
+               pwgts[idxamin(nparts, pwgts)], pwgts[idxamax(nparts, pwgts)],
+               1.0*nparts*pwgts[idxamax(nparts, pwgts)]/tvwgt, graph->nbnd, nmoves, graph->mincut, 
+               graph->minvol));
+
+    if (graph->minvol == oldvol && graph->mincut == oldcut)
+      break;
+  }
+
+  GKfree(&marker, &updind, &phtable, LTERM);
+
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nvtxs);
+}
+
+
+
+
+/*************************************************************************
+* This function performs k-way refinement
+**************************************************************************/
+void Greedy_KWayVolBalance(CtrlType *ctrl, GraphType *graph, int nparts, float *tpwgts, 
+                           float ubfactor, int npasses)
+{
+  int i, ii, iii, j, jj, k, kk, l, u, pass, nvtxs, nmoves, tvwgt, myndegrees, xgain; 
+  int from, me, to, vwgt, gain;
+  idxtype *xadj, *adjncy, *adjwgt;
+  idxtype *where, *pwgts, *perm, *moved, *bndptr, *bndind, *minwgt, *maxwgt, *itpwgts, *updind, *marker, *phtable;
+  VEDegreeType *myedegrees;
+  VRInfoType *myrinfo; 
+  PQueueType queue;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  bndptr = graph->bndptr;
+  bndind = graph->bndind;
+
+  where = graph->where;
+  pwgts = graph->pwgts;
+  
+  /* Setup the weight intervals of the various subdomains */
+  minwgt =  idxwspacemalloc(ctrl, nparts);
+  maxwgt = idxwspacemalloc(ctrl, nparts);
+  itpwgts = idxwspacemalloc(ctrl, nparts);
+  tvwgt = idxsum(nparts, pwgts);
+  ASSERT(tvwgt == idxsum(nvtxs, graph->vwgt));
+
+  updind = idxmalloc(nvtxs, "Random_KWayVolRefine: updind");
+  marker = idxsmalloc(nvtxs, 0, "Random_KWayVolRefine: marker");
+  phtable = idxsmalloc(nparts, -1, "Random_KWayVolRefine: phtable");
+
+  for (i=0; i<nparts; i++) {
+    itpwgts[i] = tpwgts[i]*tvwgt;
+    maxwgt[i] = tpwgts[i]*tvwgt*ubfactor;
+    minwgt[i] = tpwgts[i]*tvwgt*(1.0/ubfactor);
+  }
+
+  perm = idxwspacemalloc(ctrl, nvtxs);
+  moved = idxwspacemalloc(ctrl, nvtxs);
+
+  PQueueInit(ctrl, &queue, nvtxs, graph->adjwgtsum[idxamax(nvtxs, graph->adjwgtsum)]);
+
+  IFSET(ctrl->dbglvl, DBG_REFINE,
+     printf("VolPart: [%5d %5d]-[%5d %5d], Balance: %3.2f, Nv-Nb[%5d %5d]. Cut: %5d, Vol: %5d [B]\n",
+             pwgts[idxamin(nparts, pwgts)], pwgts[idxamax(nparts, pwgts)], minwgt[0], maxwgt[0], 
+             1.0*nparts*pwgts[idxamax(nparts, pwgts)]/tvwgt, graph->nvtxs, graph->nbnd,
+             graph->mincut, graph->minvol));
+
+
+  for (pass=0; pass<npasses; pass++) {
+    ASSERT(ComputeCut(graph, where) == graph->mincut);
+    /* Check to see if things are out of balance, given the tolerance */
+    for (i=0; i<nparts; i++) {
+      if (pwgts[i] > maxwgt[i])
+        break;
+    }
+    if (i == nparts) /* Things are balanced. Return right away */
+      break;
+
+    PQueueReset(&queue);
+    idxset(nvtxs, -1, moved);
+
+    RandomPermute(graph->nbnd, perm, 1);
+    for (ii=0; ii<graph->nbnd; ii++) {
+      i = bndind[perm[ii]];
+      PQueueInsert(&queue, i, graph->vrinfo[i].gv);
+      moved[i] = 2;
+    }
+
+    for (nmoves=0;;) {
+      if ((i = PQueueGetMax(&queue)) == -1) 
+        break;
+      moved[i] = 1;
+
+      myrinfo = graph->vrinfo+i;
+      from = where[i];
+      vwgt = graph->vwgt[i];
+
+      if (pwgts[from]-vwgt < minwgt[from]) 
+        continue;   /* This cannot be moved! */
+
+      xgain = (myrinfo->id == 0 && myrinfo->ed > 0 ? graph->vsize[i] : 0);
+
+      myedegrees = myrinfo->edegrees;
+      myndegrees = myrinfo->ndegrees;
+
+      for (k=0; k<myndegrees; k++) {
+        to = myedegrees[k].pid;
+        if (pwgts[to]+vwgt <= maxwgt[to] || 
+            itpwgts[from]*(pwgts[to]+vwgt) <= itpwgts[to]*pwgts[from]) 
+          break;
+      }
+      if (k == myndegrees)
+        continue;  /* break out if you did not find a candidate */
+
+      for (j=k+1; j<myndegrees; j++) {
+        to = myedegrees[j].pid;
+        if (itpwgts[myedegrees[k].pid]*pwgts[to] < itpwgts[to]*pwgts[myedegrees[k].pid]) 
+          k = j;
+      }
+
+      to = myedegrees[k].pid;
+
+      if (pwgts[from] < maxwgt[from] && pwgts[to] > minwgt[to] && 
+          (xgain+myedegrees[k].gv < 0 || 
+           (xgain+myedegrees[k].gv == 0 &&  myedegrees[k].ed-myrinfo->id < 0))
+         )
+        continue;
+  
+
+      /*=====================================================================
+      * If we got here, we can now move the vertex from 'from' to 'to' 
+      *======================================================================*/
+      INC_DEC(pwgts[to], pwgts[from], vwgt);
+      graph->mincut -= myedegrees[k].ed-myrinfo->id;
+      graph->minvol -= (xgain+myedegrees[k].gv);
+      where[i] = to;
+
+      IFSET(ctrl->dbglvl, DBG_MOVEINFO, printf("\t\tMoving %6d from %3d to %3d. Gain: [%4d %4d]. Cut: %6d, Vol: %6d\n", 
+            i, from, to, xgain+myedegrees[k].gv, myedegrees[k].ed-myrinfo->id, graph->mincut, graph->minvol));
+
+      KWayVolUpdate(ctrl, graph, i, from, to, marker, phtable, updind);
+
+      nmoves++;
+
+      /*CheckVolKWayPartitionParams(ctrl, graph, nparts); */
+    }
+
+    IFSET(ctrl->dbglvl, DBG_REFINE,
+       printf("\t[%6d %6d], Balance: %5.3f, Nb: %6d. Nmoves: %5d, Cut: %6d, Vol: %6d\n",
+               pwgts[idxamin(nparts, pwgts)], pwgts[idxamax(nparts, pwgts)],
+               1.0*nparts*pwgts[idxamax(nparts, pwgts)]/tvwgt, graph->nbnd, nmoves, graph->mincut, 
+               graph->minvol));
+
+  }
+
+  GKfree(&marker, &updind, &phtable, LTERM);
+
+  PQueueFree(ctrl, &queue);
+
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+}
+
+
+
+/*************************************************************************
+* This function performs k-way refinement
+**************************************************************************/
+void Greedy_KWayVolBalanceMConn(CtrlType *ctrl, GraphType *graph, int nparts, float *tpwgts, 
+                                float ubfactor, int npasses)
+{
+  int i, ii, iii, j, jj, k, kk, l, u, pass, nvtxs, nmoves, tvwgt, myndegrees, xgain; 
+  int from, me, to, vwgt, gain, maxndoms, nadd;
+  idxtype *xadj, *adjncy, *adjwgt;
+  idxtype *where, *pwgts, *perm, *moved, *bndptr, *bndind, *minwgt, *maxwgt, *itpwgts, *updind, *marker, *phtable;
+  idxtype *pmat, *pmatptr, *ndoms;
+  VEDegreeType *myedegrees;
+  VRInfoType *myrinfo; 
+  PQueueType queue;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  bndptr = graph->bndptr;
+  bndind = graph->bndind;
+
+  where = graph->where;
+  pwgts = graph->pwgts;
+  
+  /* Setup the weight intervals of the various subdomains */
+  minwgt =  idxwspacemalloc(ctrl, nparts);
+  maxwgt = idxwspacemalloc(ctrl, nparts);
+  itpwgts = idxwspacemalloc(ctrl, nparts);
+  tvwgt = idxsum(nparts, pwgts);
+  ASSERT(tvwgt == idxsum(nvtxs, graph->vwgt));
+
+  updind = idxmalloc(nvtxs, "Random_KWayVolRefine: updind");
+  marker = idxsmalloc(nvtxs, 0, "Random_KWayVolRefine: marker");
+  phtable = idxsmalloc(nparts, -1, "Random_KWayVolRefine: phtable");
+
+  pmat = ctrl->wspace.pmat;
+  ndoms = idxwspacemalloc(ctrl, nparts);
+
+  ComputeVolSubDomainGraph(graph, nparts, pmat, ndoms);
+
+  for (i=0; i<nparts; i++) {
+    itpwgts[i] = tpwgts[i]*tvwgt;
+    maxwgt[i] = tpwgts[i]*tvwgt*ubfactor;
+    minwgt[i] = tpwgts[i]*tvwgt*(1.0/ubfactor);
+  }
+
+  perm = idxwspacemalloc(ctrl, nvtxs);
+  moved = idxwspacemalloc(ctrl, nvtxs);
+
+  PQueueInit(ctrl, &queue, nvtxs, graph->adjwgtsum[idxamax(nvtxs, graph->adjwgtsum)]);
+
+  IFSET(ctrl->dbglvl, DBG_REFINE,
+     printf("VolPart: [%5d %5d]-[%5d %5d], Balance: %3.2f, Nv-Nb[%5d %5d]. Cut: %5d, Vol: %5d [B]\n",
+             pwgts[idxamin(nparts, pwgts)], pwgts[idxamax(nparts, pwgts)], minwgt[0], maxwgt[0], 
+             1.0*nparts*pwgts[idxamax(nparts, pwgts)]/tvwgt, graph->nvtxs, graph->nbnd,
+             graph->mincut, graph->minvol));
+
+
+  for (pass=0; pass<npasses; pass++) {
+    ASSERT(ComputeCut(graph, where) == graph->mincut);
+    /* Check to see if things are out of balance, given the tolerance */
+    for (i=0; i<nparts; i++) {
+      if (pwgts[i] > maxwgt[i])
+        break;
+    }
+    if (i == nparts) /* Things are balanced. Return right away */
+      break;
+
+    PQueueReset(&queue);
+    idxset(nvtxs, -1, moved);
+
+    RandomPermute(graph->nbnd, perm, 1);
+    for (ii=0; ii<graph->nbnd; ii++) {
+      i = bndind[perm[ii]];
+      PQueueInsert(&queue, i, graph->vrinfo[i].gv);
+      moved[i] = 2;
+    }
+
+    maxndoms = ndoms[idxamax(nparts, ndoms)];
+
+    for (nmoves=0;;) {
+      if ((i = PQueueGetMax(&queue)) == -1) 
+        break;
+      moved[i] = 1;
+
+      myrinfo = graph->vrinfo+i;
+      from = where[i];
+      vwgt = graph->vwgt[i];
+
+      if (pwgts[from]-vwgt < minwgt[from]) 
+        continue;   /* This cannot be moved! */
+
+      xgain = (myrinfo->id == 0 && myrinfo->ed > 0 ? graph->vsize[i] : 0);
+
+      myedegrees = myrinfo->edegrees;
+      myndegrees = myrinfo->ndegrees;
+
+      /* Determine the valid domains */
+      for (j=0; j<myndegrees; j++) {
+        to = myedegrees[j].pid;
+        phtable[to] = 1;
+        pmatptr = pmat + to*nparts;
+        for (nadd=0, k=0; k<myndegrees; k++) {
+          if (k == j)
+            continue;
+
+          l = myedegrees[k].pid;
+          if (pmatptr[l] == 0) {
+            if (ndoms[l] > maxndoms-1) {
+              phtable[to] = 0;
+              nadd = maxndoms;
+              break;
+            }
+            nadd++;
+          }
+        }
+        if (ndoms[to]+nadd > maxndoms)
+          phtable[to] = 0;
+      }
+
+      for (k=0; k<myndegrees; k++) {
+        to = myedegrees[k].pid;
+        if (!phtable[to])
+          continue;
+        if (pwgts[to]+vwgt <= maxwgt[to] || 
+            itpwgts[from]*(pwgts[to]+vwgt) <= itpwgts[to]*pwgts[from]) 
+          break;
+      }
+      if (k == myndegrees)
+        continue;  /* break out if you did not find a candidate */
+
+      for (j=k+1; j<myndegrees; j++) {
+        to = myedegrees[j].pid;
+        if (!phtable[to])
+          continue;
+        if (itpwgts[myedegrees[k].pid]*pwgts[to] < itpwgts[to]*pwgts[myedegrees[k].pid]) 
+          k = j;
+      }
+
+      to = myedegrees[k].pid;
+
+      for (j=0; j<myndegrees; j++) 
+        phtable[myedegrees[j].pid] = -1;
+
+      if (pwgts[from] < maxwgt[from] && pwgts[to] > minwgt[to] && 
+          (xgain+myedegrees[k].gv < 0 || 
+           (xgain+myedegrees[k].gv == 0 &&  myedegrees[k].ed-myrinfo->id < 0))
+         )
+        continue;
+  
+
+      /*=====================================================================
+      * If we got here, we can now move the vertex from 'from' to 'to' 
+      *======================================================================*/
+      INC_DEC(pwgts[to], pwgts[from], vwgt);
+      graph->mincut -= myedegrees[k].ed-myrinfo->id;
+      graph->minvol -= (xgain+myedegrees[k].gv);
+      where[i] = to;
+
+      IFSET(ctrl->dbglvl, DBG_MOVEINFO, printf("\t\tMoving %6d from %3d to %3d. Gain: [%4d %4d]. Cut: %6d, Vol: %6d\n", 
+            i, from, to, xgain+myedegrees[k].gv, myedegrees[k].ed-myrinfo->id, graph->mincut, graph->minvol));
+
+      /* Update pmat to reflect the move of 'i' */
+      pmat[from*nparts+to] += (myrinfo->id-myedegrees[k].ed);
+      pmat[to*nparts+from] += (myrinfo->id-myedegrees[k].ed);
+      if (pmat[from*nparts+to] == 0) {
+        ndoms[from]--;
+        if (ndoms[from]+1 == maxndoms)
+          maxndoms = ndoms[idxamax(nparts, ndoms)];
+      }
+      if (pmat[to*nparts+from] == 0) {
+        ndoms[to]--;
+        if (ndoms[to]+1 == maxndoms)
+          maxndoms = ndoms[idxamax(nparts, ndoms)];
+      }
+
+      for (j=xadj[i]; j<xadj[i+1]; j++) {
+        ii = adjncy[j];
+        me = where[ii];
+
+        /* Update pmat to reflect the move of 'i' for domains other than 'from' and 'to' */
+        if (me != from && me != to) {
+          pmat[me*nparts+from] -= adjwgt[j];
+          pmat[from*nparts+me] -= adjwgt[j];
+          if (pmat[me*nparts+from] == 0) {
+            ndoms[me]--;
+            if (ndoms[me]+1 == maxndoms)
+              maxndoms = ndoms[idxamax(nparts, ndoms)];
+          }
+          if (pmat[from*nparts+me] == 0) {
+            ndoms[from]--;
+            if (ndoms[from]+1 == maxndoms)
+              maxndoms = ndoms[idxamax(nparts, ndoms)];
+          }
+
+          if (pmat[me*nparts+to] == 0) {
+            ndoms[me]++;
+            if (ndoms[me] > maxndoms) {
+              printf("You just increased the maxndoms: %d %d\n", ndoms[me], maxndoms);
+              maxndoms = ndoms[me];
+            }
+          }
+          if (pmat[to*nparts+me] == 0) {
+            ndoms[to]++;
+            if (ndoms[to] > maxndoms) {
+              printf("You just increased the maxndoms: %d %d\n", ndoms[to], maxndoms);
+              maxndoms = ndoms[to];
+            }
+          }
+          pmat[me*nparts+to] += adjwgt[j];
+          pmat[to*nparts+me] += adjwgt[j];
+        }
+      }
+
+      KWayVolUpdate(ctrl, graph, i, from, to, marker, phtable, updind);
+
+      nmoves++;
+
+      /*CheckVolKWayPartitionParams(ctrl, graph, nparts); */
+    }
+
+    IFSET(ctrl->dbglvl, DBG_REFINE,
+       printf("\t[%6d %6d], Balance: %5.3f, Nb: %6d. Nmoves: %5d, Cut: %6d, Vol: %6d\n",
+               pwgts[idxamin(nparts, pwgts)], pwgts[idxamax(nparts, pwgts)],
+               1.0*nparts*pwgts[idxamax(nparts, pwgts)]/tvwgt, graph->nbnd, nmoves, graph->mincut, 
+               graph->minvol));
+
+  }
+
+  GKfree(&marker, &updind, &phtable, LTERM);
+
+  PQueueFree(ctrl, &queue);
+
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+}
+
+
+
+
+/*************************************************************************
+* This function updates the edge and volume gains as a result of moving
+* v from 'from' to 'to'.
+* The working arrays marker and phtable are assumed to be initialized to
+* -1, and they left to -1 upon return
+**************************************************************************/
+void KWayVolUpdate(CtrlType *ctrl, GraphType *graph, int v, int from, int to,
+                   idxtype *marker, idxtype *phtable, idxtype *updind)
+{
+  int ii, iii, j, jj, k, kk, l, u, nupd, other, me, myidx; 
+  idxtype *xadj, *vsize, *adjncy, *adjwgt, *where;
+  VEDegreeType *myedegrees, *oedegrees;
+  VRInfoType *myrinfo, *orinfo;
+
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+  vsize = graph->vsize;
+  where = graph->where;
+
+  myrinfo = graph->vrinfo+v;
+  myedegrees = myrinfo->edegrees;
+
+
+  /*======================================================================
+   * Remove the contributions on the gain made by 'v'. 
+   *=====================================================================*/
+  for (k=0; k<myrinfo->ndegrees; k++)
+    phtable[myedegrees[k].pid] = k;
+  phtable[from] = k;
+
+  myidx = phtable[to];  /* Keep track of the index in myedegrees of the 'to' domain */
+
+  for (j=xadj[v]; j<xadj[v+1]; j++) {
+    ii = adjncy[j];
+    other = where[ii];
+    orinfo = graph->vrinfo+ii;
+    oedegrees = orinfo->edegrees;
+
+    if (other == from) {
+      for (k=0; k<orinfo->ndegrees; k++) {
+        if (phtable[oedegrees[k].pid] == -1) 
+          oedegrees[k].gv += vsize[v];
+      }
+    }
+    else {
+      ASSERT(phtable[other] != -1);
+
+      if (myedegrees[phtable[other]].ned > 1) {
+        for (k=0; k<orinfo->ndegrees; k++) {
+          if (phtable[oedegrees[k].pid] == -1) 
+            oedegrees[k].gv += vsize[v];
+        }
+      }
+      else { /* There is only one connection */
+        for (k=0; k<orinfo->ndegrees; k++) {
+          if (phtable[oedegrees[k].pid] != -1) 
+            oedegrees[k].gv -= vsize[v];
+        }
+      }
+    }
+  }
+
+  for (k=0; k<myrinfo->ndegrees; k++)
+    phtable[myedegrees[k].pid] = -1;
+  phtable[from] = -1;
+
+
+  /*======================================================================
+   * Update the id/ed of vertex 'v'
+   *=====================================================================*/
+  myrinfo->ed += myrinfo->id-myedegrees[myidx].ed;
+  SWAP(myrinfo->id, myedegrees[myidx].ed, j);
+  SWAP(myrinfo->nid, myedegrees[myidx].ned, j);
+  if (myedegrees[myidx].ed == 0) 
+    myedegrees[myidx] = myedegrees[--myrinfo->ndegrees];
+  else
+    myedegrees[myidx].pid = from;
+
+  /*======================================================================
+   * Update the degrees of adjacent vertices and their volume gains
+   *=====================================================================*/
+  marker[v] = 1;
+  updind[0] = v;
+  nupd = 1;
+  for (j=xadj[v]; j<xadj[v+1]; j++) {
+    ii = adjncy[j];
+    me = where[ii];
+
+    if (!marker[ii]) {  /* The marking is done for boundary and max gv calculations */
+      marker[ii] = 2;
+      updind[nupd++] = ii;
+    }
+
+    myrinfo = graph->vrinfo+ii;
+    if (myrinfo->edegrees == NULL) {
+      myrinfo->edegrees = ctrl->wspace.vedegrees+ctrl->wspace.cdegree;
+      ctrl->wspace.cdegree += xadj[ii+1]-xadj[ii];
+    }
+    myedegrees = myrinfo->edegrees;
+
+    if (me == from) {
+      INC_DEC(myrinfo->ed, myrinfo->id, adjwgt[j]);
+      myrinfo->nid--;
+    } 
+    else if (me == to) {
+      INC_DEC(myrinfo->id, myrinfo->ed, adjwgt[j]);
+      myrinfo->nid++;
+    }
+
+    /* Remove the edgeweight from the 'pid == from' entry of the vertex */
+    if (me != from) {
+      for (k=0; k<myrinfo->ndegrees; k++) {
+        if (myedegrees[k].pid == from) {
+          if (myedegrees[k].ned == 1) {
+            myedegrees[k] = myedegrees[--myrinfo->ndegrees];
+            marker[ii] = 1;  /* You do a complete .gv calculation */
+
+            /* All vertices adjacent to 'ii' need to be updated */
+            for (jj=xadj[ii]; jj<xadj[ii+1]; jj++) {
+              u = adjncy[jj];
+              other = where[u];
+              orinfo = graph->vrinfo+u;
+              oedegrees = orinfo->edegrees;
+
+              for (kk=0; kk<orinfo->ndegrees; kk++) {
+                if (oedegrees[kk].pid == from) {
+                  oedegrees[kk].gv -= vsize[ii];
+                  break;
+                }
+              }
+            }
+          }
+          else {
+            myedegrees[k].ed -= adjwgt[j];
+            myedegrees[k].ned--;
+
+            /* Update the gv due to single 'ii' connection to 'from' */
+            if (myedegrees[k].ned == 1) {
+              /* find the vertex 'u' that 'ii' was connected into 'from' */
+              for (jj=xadj[ii]; jj<xadj[ii+1]; jj++) {
+                u = adjncy[jj];
+                other = where[u];
+                orinfo = graph->vrinfo+u;
+                oedegrees = orinfo->edegrees;
+
+                if (other == from) {
+                  for (kk=0; kk<orinfo->ndegrees; kk++) 
+                    oedegrees[kk].gv += vsize[ii];
+                  break;  
+                }
+              }
+            }
+          }
+
+          break; 
+        }
+      }
+    }
+
+    /* Add the edgeweight to the 'pid == to' entry of the vertex */
+    if (me != to) {
+      for (k=0; k<myrinfo->ndegrees; k++) {
+        if (myedegrees[k].pid == to) {
+          myedegrees[k].ed += adjwgt[j];
+          myedegrees[k].ned++;
+
+          /* Update the gv due to non-single 'ii' connection to 'to' */
+          if (myedegrees[k].ned == 2) {
+            /* find the vertex 'u' that 'ii' was connected into 'to' */
+            for (jj=xadj[ii]; jj<xadj[ii+1]; jj++) {
+              u = adjncy[jj];
+              other = where[u];
+              orinfo = graph->vrinfo+u;
+              oedegrees = orinfo->edegrees;
+
+              if (u != v && other == to) {
+                for (kk=0; kk<orinfo->ndegrees; kk++) 
+                  oedegrees[kk].gv -= vsize[ii];
+                break;  
+              }
+            }
+          }
+          break;
+        }
+      }
+
+      if (k == myrinfo->ndegrees) {
+        myedegrees[myrinfo->ndegrees].pid = to;
+        myedegrees[myrinfo->ndegrees].ed = adjwgt[j];
+        myedegrees[myrinfo->ndegrees++].ned = 1;
+        marker[ii] = 1;  /* You do a complete .gv calculation */
+
+        /* All vertices adjacent to 'ii' need to be updated */
+        for (jj=xadj[ii]; jj<xadj[ii+1]; jj++) {
+          u = adjncy[jj];
+          other = where[u];
+          orinfo = graph->vrinfo+u;
+          oedegrees = orinfo->edegrees;
+
+          for (kk=0; kk<orinfo->ndegrees; kk++) {
+            if (oedegrees[kk].pid == to) {
+              oedegrees[kk].gv += vsize[ii];
+              if (!marker[u]) { /* Need to update boundary etc */
+                marker[u] = 2;
+                updind[nupd++] = u;
+              }
+              break;
+            }
+          }
+        }
+      }
+    }
+
+    ASSERT(myrinfo->ndegrees <= xadj[ii+1]-xadj[ii]);
+  }
+
+  /*======================================================================
+   * Add the contributions on the volume gain due to 'v'
+   *=====================================================================*/
+  myrinfo = graph->vrinfo+v;
+  myedegrees = myrinfo->edegrees;
+  for (k=0; k<myrinfo->ndegrees; k++)
+    phtable[myedegrees[k].pid] = k;
+  phtable[to] = k;
+
+  for (j=xadj[v]; j<xadj[v+1]; j++) {
+    ii = adjncy[j];
+    other = where[ii];
+    orinfo = graph->vrinfo+ii;
+    oedegrees = orinfo->edegrees;
+
+    if (other == to) {
+      for (k=0; k<orinfo->ndegrees; k++) {
+        if (phtable[oedegrees[k].pid] == -1) 
+          oedegrees[k].gv -= vsize[v];
+      }
+    }
+    else {
+      ASSERT(phtable[other] != -1);
+
+      if (myedegrees[phtable[other]].ned > 1) {
+        for (k=0; k<orinfo->ndegrees; k++) {
+          if (phtable[oedegrees[k].pid] == -1) 
+            oedegrees[k].gv -= vsize[v];
+        }
+      }
+      else { /* There is only one connection */
+        for (k=0; k<orinfo->ndegrees; k++) {
+          if (phtable[oedegrees[k].pid] != -1) 
+            oedegrees[k].gv += vsize[v];
+        }
+      }
+    }
+  }
+  for (k=0; k<myrinfo->ndegrees; k++)
+    phtable[myedegrees[k].pid] = -1;
+  phtable[to] = -1;
+
+
+  /*======================================================================
+   * Recompute the volume information of the 'hard' nodes, and update the
+   * max volume gain for all the update vertices
+   *=====================================================================*/
+  ComputeKWayVolume(graph, nupd, updind, marker, phtable);
+
+
+  /*======================================================================
+   * Maintain a consistent boundary
+   *=====================================================================*/
+  for (j=0; j<nupd; j++) {
+    k = updind[j];
+    marker[k] = 0;
+    myrinfo = graph->vrinfo+k;
+
+    if ((myrinfo->gv >= 0 || myrinfo->ed-myrinfo->id >= 0) && graph->bndptr[k] == -1)
+      BNDInsert(graph->nbnd, graph->bndind, graph->bndptr, k);
+
+    if (myrinfo->gv < 0 && myrinfo->ed-myrinfo->id < 0 && graph->bndptr[k] != -1)
+      BNDDelete(graph->nbnd, graph->bndind, graph->bndptr, k);
+  }
+
+}
+
+
+
+
+/*************************************************************************
+* This function computes the initial id/ed 
+**************************************************************************/
+void ComputeKWayVolume(GraphType *graph, int nupd, idxtype *updind, idxtype *marker, idxtype *phtable)
+{
+  int ii, iii, i, j, k, kk, l, nvtxs, me, other, pid;
+  idxtype *xadj, *vsize, *adjncy, *adjwgt, *where;
+  VRInfoType *rinfo, *myrinfo, *orinfo;
+  VEDegreeType *myedegrees, *oedegrees;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  vsize = graph->vsize;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+  where = graph->where;
+  rinfo = graph->vrinfo;
+
+
+  /*------------------------------------------------------------
+  / Compute now the iv/ev degrees
+  /------------------------------------------------------------*/
+  for (iii=0; iii<nupd; iii++) {
+    i = updind[iii];
+    me = where[i];
+
+    myrinfo = rinfo+i;
+    myedegrees = myrinfo->edegrees;
+
+    if (marker[i] == 1) {  /* Only complete gain updates go through */
+      for (k=0; k<myrinfo->ndegrees; k++) 
+        myedegrees[k].gv = 0;
+
+      for (j=xadj[i]; j<xadj[i+1]; j++) {
+        ii = adjncy[j];
+        other = where[ii];
+        orinfo = rinfo+ii;
+        oedegrees = orinfo->edegrees;
+
+        for (kk=0; kk<orinfo->ndegrees; kk++) 
+          phtable[oedegrees[kk].pid] = kk;
+        phtable[other] = 1;
+
+        if (me == other) {
+          /* Find which domains 'i' is connected and 'ii' is not and update their gain */
+          for (k=0; k<myrinfo->ndegrees; k++) {
+            if (phtable[myedegrees[k].pid] == -1)
+              myedegrees[k].gv -= vsize[ii];
+          }
+        }
+        else {
+          ASSERT(phtable[me] != -1);
+
+          /* I'm the only connection of 'ii' in 'me' */
+          if (oedegrees[phtable[me]].ned == 1) { 
+            /* Increase the gains for all the common domains between 'i' and 'ii' */
+            for (k=0; k<myrinfo->ndegrees; k++) {
+              if (phtable[myedegrees[k].pid] != -1) 
+                myedegrees[k].gv += vsize[ii];
+            }
+          }
+          else {
+            /* Find which domains 'i' is connected and 'ii' is not and update their gain */
+            for (k=0; k<myrinfo->ndegrees; k++) {
+              if (phtable[myedegrees[k].pid] == -1) 
+                myedegrees[k].gv -= vsize[ii];
+            }
+          }
+        }
+
+        for (kk=0; kk<orinfo->ndegrees; kk++) 
+          phtable[oedegrees[kk].pid] = -1;
+        phtable[other] = -1;
+  
+      }
+    }
+
+    myrinfo->gv = -MAXIDX;
+    for (k=0; k<myrinfo->ndegrees; k++) {
+      if (myedegrees[k].gv > myrinfo->gv)
+        myrinfo->gv = myedegrees[k].gv;
+    }
+    if (myrinfo->ed > 0 && myrinfo->id == 0)
+      myrinfo->gv += vsize[i];
+
+  }
+
+}
+
+
+
+/*************************************************************************
+* This function computes the total volume
+**************************************************************************/
+int ComputeVolume(GraphType *graph, idxtype *where)
+{
+  int i, j, k, me, nvtxs, nparts, totalv;
+  idxtype *xadj, *adjncy, *vsize, *marker;
+
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  vsize = (graph->vsize == NULL ? graph->vwgt : graph->vsize);
+
+  nparts = where[idxamax(nvtxs, where)]+1;
+  marker = idxsmalloc(nparts, -1, "ComputeVolume: marker");
+
+  totalv = 0;
+
+  for (i=0; i<nvtxs; i++) {
+    marker[where[i]] = i;
+    for (j=xadj[i]; j<xadj[i+1]; j++) {
+      k = where[adjncy[j]];
+      if (marker[k] != i) {
+        marker[k] = i;
+        totalv += vsize[i];
+      }
+    }
+  }
+
+  free(marker);
+
+  return totalv;
+}
+
+
+
+
+
+/*************************************************************************
+* This function computes the initial id/ed 
+**************************************************************************/
+void CheckVolKWayPartitionParams(CtrlType *ctrl, GraphType *graph, int nparts)
+{
+  int i, ii, j, k, kk, l, nvtxs, nbnd, mincut, minvol, me, other, pid;
+  idxtype *xadj, *vsize, *adjncy, *adjwgt, *pwgts, *where, *bndind, *bndptr;
+  VRInfoType *rinfo, *myrinfo, *orinfo, tmprinfo;
+  VEDegreeType *myedegrees, *oedegrees, *tmpdegrees;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  vsize = graph->vsize;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+  where = graph->where;
+  rinfo = graph->vrinfo;
+
+  tmpdegrees = (VEDegreeType *)GKmalloc(nparts*sizeof(VEDegreeType), "CheckVolKWayPartitionParams: tmpdegrees");
+
+  /*------------------------------------------------------------
+  / Compute now the iv/ev degrees
+  /------------------------------------------------------------*/
+  for (i=0; i<nvtxs; i++) {
+    me = where[i];
+
+    myrinfo = rinfo+i;
+    myedegrees = myrinfo->edegrees;
+
+    for (k=0; k<myrinfo->ndegrees; k++)
+      tmpdegrees[k] = myedegrees[k];
+
+    tmprinfo.ndegrees = myrinfo->ndegrees;
+    tmprinfo.id = myrinfo->id;
+    tmprinfo.ed = myrinfo->ed;
+
+    myrinfo = &tmprinfo;
+    myedegrees = tmpdegrees;
+
+
+    for (k=0; k<myrinfo->ndegrees; k++)
+      myedegrees[k].gv = 0;
+
+    for (j=xadj[i]; j<xadj[i+1]; j++) {
+      ii = adjncy[j];
+      other = where[ii];
+      orinfo = rinfo+ii;
+      oedegrees = orinfo->edegrees;
+
+      if (me == other) {
+        /* Find which domains 'i' is connected and 'ii' is not and update their gain */
+        for (k=0; k<myrinfo->ndegrees; k++) {
+          pid = myedegrees[k].pid;
+          for (kk=0; kk<orinfo->ndegrees; kk++) {
+            if (oedegrees[kk].pid == pid)
+              break;
+          }
+          if (kk == orinfo->ndegrees) 
+            myedegrees[k].gv -= vsize[ii];
+        }
+      }
+      else {
+        /* Find the orinfo[me].ed and see if I'm the only connection */
+        for (k=0; k<orinfo->ndegrees; k++) {
+          if (oedegrees[k].pid == me)
+            break;
+        }
+
+        if (oedegrees[k].ned == 1) { /* I'm the only connection of 'ii' in 'me' */
+          for (k=0; k<myrinfo->ndegrees; k++) {
+            if (myedegrees[k].pid == other) {
+              myedegrees[k].gv += vsize[ii];
+              break;
+            }
+          }
+
+          /* Increase the gains for all the common domains between 'i' and 'ii' */
+          for (k=0; k<myrinfo->ndegrees; k++) {
+            if ((pid = myedegrees[k].pid) == other)
+              continue;
+            for (kk=0; kk<orinfo->ndegrees; kk++) {
+              if (oedegrees[kk].pid == pid) {
+                myedegrees[k].gv += vsize[ii];
+                break;
+              }
+            }
+          }
+
+        }
+        else {
+          /* Find which domains 'i' is connected and 'ii' is not and update their gain */
+          for (k=0; k<myrinfo->ndegrees; k++) {
+            if ((pid = myedegrees[k].pid) == other)
+              continue;
+            for (kk=0; kk<orinfo->ndegrees; kk++) {
+              if (oedegrees[kk].pid == pid)
+                break;
+            }
+            if (kk == orinfo->ndegrees) 
+              myedegrees[k].gv -= vsize[ii];
+          }
+        }
+      }
+    }
+
+    myrinfo = rinfo+i;
+    myedegrees = myrinfo->edegrees;
+
+    for (k=0; k<myrinfo->ndegrees; k++) {
+      pid = myedegrees[k].pid;
+      for (kk=0; kk<tmprinfo.ndegrees; kk++) {
+        if (tmpdegrees[kk].pid == pid) {
+          if (tmpdegrees[kk].gv != myedegrees[k].gv)
+            printf("[%d %d %d %d]\n", i, pid, myedegrees[k].gv, tmpdegrees[kk].gv);
+          break;
+        }
+      }
+    }
+
+  }
+
+  free(tmpdegrees);
+
+}
+
+
+/*************************************************************************
+* This function computes the subdomain graph
+**************************************************************************/
+void ComputeVolSubDomainGraph(GraphType *graph, int nparts, idxtype *pmat, idxtype *ndoms)
+{
+  int i, j, k, me, nvtxs, ndegrees;
+  idxtype *xadj, *adjncy, *adjwgt, *where;
+  VRInfoType *rinfo;
+  VEDegreeType *edegrees;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+  where = graph->where;
+  rinfo = graph->vrinfo;
+
+  idxset(nparts*nparts, 0, pmat);
+
+  for (i=0; i<nvtxs; i++) {
+    if (rinfo[i].ed > 0) {
+      me = where[i];
+      ndegrees = rinfo[i].ndegrees;
+      edegrees = rinfo[i].edegrees;
+
+      k = me*nparts;
+      for (j=0; j<ndegrees; j++) 
+        pmat[k+edegrees[j].pid] += edegrees[j].ed;
+    }
+  }
+
+  for (i=0; i<nparts; i++) {
+    ndoms[i] = 0;
+    for (j=0; j<nparts; j++) {
+      if (pmat[i*nparts+j] > 0)
+        ndoms[i]++;
+    }
+  }
+}
+
+
+
+/*************************************************************************
+* This function computes the subdomain graph
+**************************************************************************/
+void EliminateVolSubDomainEdges(CtrlType *ctrl, GraphType *graph, int nparts, float *tpwgts)
+{
+  int i, ii, j, k, me, other, nvtxs, total, max, avg, totalout, nind, ncand, ncand2, target, target2, nadd;
+  int min, move, cpwgt, tvwgt;
+  idxtype *xadj, *adjncy, *vwgt, *adjwgt, *pwgts, *where, *maxpwgt, *pmat, *ndoms, *mypmat, *otherpmat, *ind;
+  KeyValueType *cand, *cand2;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  vwgt = graph->vwgt;
+  adjwgt = graph->adjwgt;
+
+  where = graph->where;
+  pwgts = idxset(nparts, 0, graph->pwgts);
+
+  maxpwgt = idxwspacemalloc(ctrl, nparts);
+  ndoms = idxwspacemalloc(ctrl, nparts);
+  otherpmat = idxwspacemalloc(ctrl, nparts);
+  ind = idxwspacemalloc(ctrl, nvtxs);
+  pmat = idxset(nparts*nparts, 0, ctrl->wspace.pmat);
+
+  cand = (KeyValueType *)GKmalloc(nparts*sizeof(KeyValueType), "EliminateSubDomainEdges: cand");
+  cand2 = (KeyValueType *)GKmalloc(nparts*sizeof(KeyValueType), "EliminateSubDomainEdges: cand");
+
+  /* Compute the pmat matrix */
+  for (i=0; i<nvtxs; i++) {
+    me = where[i];
+    pwgts[me] += vwgt[i];
+    for (j=xadj[i]; j<xadj[i+1]; j++) {
+      k = adjncy[j];
+      if (where[k] != me) 
+        pmat[me*nparts+where[k]] += adjwgt[j];
+    }
+  }
+
+  /* Compute the maximum allowed weight for each domain */
+  tvwgt = idxsum(nparts, pwgts);
+  for (i=0; i<nparts; i++)
+    maxpwgt[i] = 1.25*tpwgts[i]*tvwgt;
+
+  /* Determine the domain connectivity */
+  for (i=0; i<nparts; i++) {
+    for (k=0, j=0; j<nparts; j++) {
+      if (pmat[i*nparts+j] > 0)
+        k++;
+    }
+    ndoms[i] = k;
+  }
+
+  /* Get into the loop eliminating subdomain connections */
+  for (;;) {
+    total = idxsum(nparts, ndoms);
+    avg = total/nparts;
+    max = ndoms[idxamax(nparts, ndoms)];
+
+    /* printf("Adjacent Subdomain Stats: Total: %3d, Max: %3d, Avg: %3d\n", total, max, avg); */
+
+    if (max < 1.5*avg)
+      break;
+
+    me = idxamax(nparts, ndoms);
+    mypmat = pmat + me*nparts;
+    totalout = idxsum(nparts, mypmat);
+
+    /*printf("Me: %d, TotalOut: %d,\n", me, totalout);*/
+
+    /* Sort the connections according to their cut */
+    for (ncand2=0, i=0; i<nparts; i++) {
+      if (mypmat[i] > 0) {
+        cand2[ncand2].key = mypmat[i];
+        cand2[ncand2++].val = i;
+      }
+    }
+    ikeysort(ncand2, cand2);
+
+    move = 0;
+    for (min=0; min<ncand2; min++) {
+      if (cand2[min].key > totalout/(2*ndoms[me])) 
+        break;
+
+      other = cand2[min].val;
+
+      /*printf("\tMinOut: %d to %d\n", mypmat[other], other);*/
+
+      idxset(nparts, 0, otherpmat);
+
+      /* Go and find the vertices in 'other' that are connected in 'me' */
+      for (nind=0, i=0; i<nvtxs; i++) {
+        if (where[i] == other) {
+          for (j=xadj[i]; j<xadj[i+1]; j++) {
+            if (where[adjncy[j]] == me) {
+              ind[nind++] = i;
+              break;
+            }
+          }
+        }
+      }
+
+      /* Go and construct the otherpmat to see where these nind vertices are connected to */
+      for (cpwgt=0, ii=0; ii<nind; ii++) {
+        i = ind[ii];
+        cpwgt += vwgt[i];
+
+        for (j=xadj[i]; j<xadj[i+1]; j++) {
+          k = adjncy[j];
+          if (where[k] != other) 
+            otherpmat[where[k]] += adjwgt[j];
+        }
+      }
+
+      for (ncand=0, i=0; i<nparts; i++) {
+        if (otherpmat[i] > 0) {
+          cand[ncand].key = -otherpmat[i];
+          cand[ncand++].val = i;
+        }
+      }
+      ikeysort(ncand, cand);
+
+      /* 
+       * Go through and the select the first domain that is common with 'me', and
+       * does not increase the ndoms[target] higher than my ndoms, subject to the
+       * maxpwgt constraint. Traversal is done from the mostly connected to the least.
+       */
+      target = target2 = -1;
+      for (i=0; i<ncand; i++) {
+        k = cand[i].val;
+
+        if (mypmat[k] > 0) {
+          if (pwgts[k] + cpwgt > maxpwgt[k])  /* Check if balance will go off */
+            continue;
+
+          for (j=0; j<nparts; j++) {
+            if (otherpmat[j] > 0 && ndoms[j] >= ndoms[me]-1 && pmat[nparts*j+k] == 0)
+              break;
+          }
+          if (j == nparts) { /* No bad second level effects */
+            for (nadd=0, j=0; j<nparts; j++) {
+              if (otherpmat[j] > 0 && pmat[nparts*k+j] == 0)
+                nadd++;
+            }
+
+            /*printf("\t\tto=%d, nadd=%d, %d\n", k, nadd, ndoms[k]);*/
+            if (target2 == -1 && ndoms[k]+nadd < ndoms[me]) {
+              target2 = k;
+            }
+            if (nadd == 0) {
+              target = k;
+              break;
+            }
+          }
+        }
+      }
+      if (target == -1 && target2 != -1)
+        target = target2;
+
+      if (target == -1) {
+        /* printf("\t\tCould not make the move\n");*/
+        continue;
+      }
+
+      /*printf("\t\tMoving to %d\n", target);*/
+
+      /* Update the partition weights */
+      INC_DEC(pwgts[target], pwgts[other], cpwgt);
+
+      /* Set all nind vertices to belong to 'target' */
+      for (ii=0; ii<nind; ii++) {
+        i = ind[ii];
+        where[i] = target;
+
+        /* First remove any contribution that this vertex may have made */
+        for (j=xadj[i]; j<xadj[i+1]; j++) {
+          k = adjncy[j];
+          if (where[k] != other) {
+            if (pmat[nparts*other + where[k]] == 0)
+              printf("Something wrong\n");
+            pmat[nparts*other + where[k]] -= adjwgt[j];
+            if (pmat[nparts*other + where[k]] == 0)
+              ndoms[other]--;
+
+            if (pmat[nparts*where[k] + other] == 0)
+              printf("Something wrong\n");
+            pmat[nparts*where[k] + other] -= adjwgt[j];
+            if (pmat[nparts*where[k] + other] == 0)
+              ndoms[where[k]]--;
+          }
+        }
+
+        /* Next add the new contributions as a result of the move */
+        for (j=xadj[i]; j<xadj[i+1]; j++) {
+          k = adjncy[j];
+          if (where[k] != target) {
+            if (pmat[nparts*target + where[k]] == 0)
+              ndoms[target]++;
+            pmat[nparts*target + where[k]] += adjwgt[j];
+  
+            if (pmat[nparts*where[k] + target] == 0)
+              ndoms[where[k]]++;
+            pmat[nparts*where[k] + target] += adjwgt[j];
+          }
+        }
+      }
+
+      move = 1;
+      break;
+    }
+
+    if (move == 0)
+      break;
+  }
+
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nvtxs);
+
+  GKfree(&cand, &cand2, LTERM);
+}
+
+
+
+/*************************************************************************
+* This function finds all the connected components induced by the 
+* partitioning vector in wgraph->where and tries to push them around to 
+* remove some of them
+**************************************************************************/
+void EliminateVolComponents(CtrlType *ctrl, GraphType *graph, int nparts, float *tpwgts, float ubfactor)
+{
+  int i, ii, j, jj, k, me, nvtxs, tvwgt, first, last, nleft, ncmps, cwgt, ncand, other, target, deltawgt;
+  idxtype *xadj, *adjncy, *vwgt, *adjwgt, *where, *pwgts, *maxpwgt;
+  idxtype *cpvec, *touched, *perm, *todo, *cind, *cptr, *npcmps;
+  KeyValueType *cand;
+  int recompute=0;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  vwgt = graph->vwgt;
+  adjwgt = graph->adjwgt;
+
+  where = graph->where;
+  pwgts = idxset(nparts, 0, graph->pwgts);
+
+  touched = idxset(nvtxs, 0, idxwspacemalloc(ctrl, nvtxs));
+  cptr = idxwspacemalloc(ctrl, nvtxs);
+  cind = idxwspacemalloc(ctrl, nvtxs);
+  perm = idxwspacemalloc(ctrl, nvtxs);
+  todo = idxwspacemalloc(ctrl, nvtxs);
+  maxpwgt = idxwspacemalloc(ctrl, nparts);
+  cpvec = idxwspacemalloc(ctrl, nparts);
+  npcmps = idxset(nparts, 0, idxwspacemalloc(ctrl, nparts));
+
+  for (i=0; i<nvtxs; i++) 
+    perm[i] = todo[i] = i;
+
+  /* Find the connected componends induced by the partition */
+  ncmps = -1;
+  first = last = 0;
+  nleft = nvtxs;
+  while (nleft > 0) {
+    if (first == last) { /* Find another starting vertex */
+      cptr[++ncmps] = first;
+      ASSERT(touched[todo[0]] == 0);
+      i = todo[0];
+      cind[last++] = i;
+      touched[i] = 1;
+      me = where[i];
+      npcmps[me]++;
+    }
+
+    i = cind[first++];
+    k = perm[i];
+    j = todo[k] = todo[--nleft];
+    perm[j] = k;
+
+    for (j=xadj[i]; j<xadj[i+1]; j++) {
+      k = adjncy[j];
+      if (where[k] == me && !touched[k]) {
+        cind[last++] = k;
+        touched[k] = 1;
+      }
+    }
+  }
+  cptr[++ncmps] = first;
+
+  /* printf("I found %d components, for this %d-way partition\n", ncmps, nparts); */
+
+  if (ncmps > nparts) { /* There are more components than processors */
+    cand = (KeyValueType *)GKmalloc(nparts*sizeof(KeyValueType), "EliminateSubDomainEdges: cand");
+
+    /* First determine the partition sizes and max allowed load imbalance */
+    for (i=0; i<nvtxs; i++) 
+      pwgts[where[i]] += vwgt[i];
+    tvwgt = idxsum(nparts, pwgts);
+    for (i=0; i<nparts; i++)
+      maxpwgt[i] = ubfactor*tpwgts[i]*tvwgt;
+
+    deltawgt = tvwgt/(100*nparts);
+    deltawgt = 5;
+
+    for (i=0; i<ncmps; i++) {
+      me = where[cind[cptr[i]]];  /* Get the domain of this component */
+      if (npcmps[me] == 1)
+        continue;  /* Skip it because it is contigous */
+
+      /*printf("Trying to move %d from %d\n", i, me); */
+
+      /* Determine the connectivity */
+      idxset(nparts, 0, cpvec);
+      for (cwgt=0, j=cptr[i]; j<cptr[i+1]; j++) {
+        ii = cind[j];
+        cwgt += vwgt[ii];
+        for (jj=xadj[ii]; jj<xadj[ii+1]; jj++) {
+          other = where[adjncy[jj]];
+          if (me != other)
+            cpvec[other] += adjwgt[jj];
+        }
+      }
+
+      /*printf("\tCmp weight: %d\n", cwgt);*/
+
+      if (cwgt > .30*pwgts[me])
+        continue;  /* Skip the component if it is over 30% of the weight */
+
+      for (ncand=0, j=0; j<nparts; j++) {
+        if (cpvec[j] > 0) {
+          cand[ncand].key = -cpvec[j];
+          cand[ncand++].val = j;
+        }
+      }
+      if (ncand == 0)
+        continue;
+
+      ikeysort(ncand, cand);
+
+      target = -1;
+      for (j=0; j<ncand; j++) {
+        k = cand[j].val;
+        if (cwgt < deltawgt || pwgts[k] + cwgt < maxpwgt[k]) {
+          target = k;
+          break;
+        }
+      }
+
+      /*printf("\tMoving it to %d [%d]\n", target, cpvec[target]);*/
+
+      if (target != -1) {
+        /* Assign all the vertices of 'me' to 'target' and update data structures */
+        pwgts[me] -= cwgt;
+        pwgts[target] += cwgt;
+        npcmps[me]--;
+
+        for (j=cptr[i]; j<cptr[i+1]; j++) 
+          where[cind[j]] = target;
+
+        graph->mincut -= cpvec[target];
+        recompute = 1;
+      }
+    }
+
+    free(cand);
+  }
+
+  if (recompute) {
+    int ttlv;
+    idxtype *marker;
+
+    marker = idxset(nparts, -1, cpvec);
+    for (ttlv=0, i=0; i<nvtxs; i++) {
+      marker[where[i]] = i;
+      for (j=xadj[i]; j<xadj[i+1]; j++) {
+        if (marker[where[adjncy[j]]] != i) {
+          ttlv += graph->vsize[i];
+          marker[where[adjncy[j]]] = i;
+        }
+      }
+    }
+    graph->minvol = ttlv;
+  }
+
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+
+}
+
diff --git a/contrib/Metis/kwayvolrefine.c b/contrib/Metis/kwayvolrefine.c
new file mode 100644
index 0000000000..4677b6bd17
--- /dev/null
+++ b/contrib/Metis/kwayvolrefine.c
@@ -0,0 +1,460 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * kwayvolrefine.c
+ *
+ * This file contains the driving routines for multilevel k-way refinement
+ *
+ * Started 7/28/97
+ * George
+ *
+ * $Id: kwayvolrefine.c,v 1.1 2005-09-21 17:29:37 geuzaine Exp $
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function is the entry point of refinement
+**************************************************************************/
+void RefineVolKWay(CtrlType *ctrl, GraphType *orggraph, GraphType *graph, int nparts, 
+                   float *tpwgts, float ubfactor)
+{
+  int i, nlevels;
+  GraphType *ptr;
+
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->UncoarsenTmr));
+
+  /* Take care any non-contiguity */
+  if (ctrl->RType == RTYPE_KWAYRANDOM_MCONN) {
+    ComputeVolKWayPartitionParams(ctrl, graph, nparts);
+    EliminateVolComponents(ctrl, graph, nparts, tpwgts, 1.25);
+    EliminateVolSubDomainEdges(ctrl, graph, nparts, tpwgts);
+    EliminateVolComponents(ctrl, graph, nparts, tpwgts, 1.25);
+  }
+
+
+  /* Determine how many levels are there */
+  for (ptr=graph, nlevels=0; ptr!=orggraph; ptr=ptr->finer, nlevels++); 
+
+  /* Compute the parameters of the coarsest graph */
+  ComputeVolKWayPartitionParams(ctrl, graph, nparts);
+
+  for (i=0; ;i++) {
+    /*PrintSubDomainGraph(graph, nparts, graph->where);*/
+    MALLOC_CHECK(NULL);
+    IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->RefTmr));
+
+    if (2*i >= nlevels && !IsBalanced(graph->pwgts, nparts, tpwgts, 1.04*ubfactor)) {
+      ComputeVolKWayBalanceBoundary(ctrl, graph, nparts);
+      switch (ctrl->RType) {
+        case RTYPE_KWAYRANDOM:
+          Greedy_KWayVolBalance(ctrl, graph, nparts, tpwgts, ubfactor, 1); 
+          break;
+        case RTYPE_KWAYRANDOM_MCONN:
+          Greedy_KWayVolBalanceMConn(ctrl, graph, nparts, tpwgts, ubfactor, 1); 
+          break;
+      }
+      ComputeVolKWayBoundary(ctrl, graph, nparts);
+    }
+
+    switch (ctrl->RType) {
+      case RTYPE_KWAYRANDOM:
+        Random_KWayVolRefine(ctrl, graph, nparts, tpwgts, ubfactor, 10, 0); 
+        break;
+      case RTYPE_KWAYRANDOM_MCONN:
+        Random_KWayVolRefineMConn(ctrl, graph, nparts, tpwgts, ubfactor, 10, 0); 
+        break;
+    }
+    IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->RefTmr));
+
+    if (graph == orggraph)
+      break;
+
+    GKfree(&graph->gdata, LTERM);  /* Deallocate the graph related arrays */
+
+    graph = graph->finer;
+
+    IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->ProjectTmr));
+    ProjectVolKWayPartition(ctrl, graph, nparts);
+    IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->ProjectTmr));
+  }
+
+  if (!IsBalanced(graph->pwgts, nparts, tpwgts, ubfactor)) {
+    ComputeVolKWayBalanceBoundary(ctrl, graph, nparts);
+    switch (ctrl->RType) {
+      case RTYPE_KWAYRANDOM:
+        Greedy_KWayVolBalance(ctrl, graph, nparts, tpwgts, ubfactor, 8); 
+        Random_KWayVolRefine(ctrl, graph, nparts, tpwgts, ubfactor, 10, 0); 
+        break;
+      case RTYPE_KWAYRANDOM_MCONN:
+        Greedy_KWayVolBalanceMConn(ctrl, graph, nparts, tpwgts, ubfactor, 8); 
+        Random_KWayVolRefineMConn(ctrl, graph, nparts, tpwgts, ubfactor, 10, 0); 
+        break;
+    }
+  }
+
+  EliminateVolComponents(ctrl, graph, nparts, tpwgts, ubfactor); 
+
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->UncoarsenTmr));
+}
+
+
+
+/*************************************************************************
+* This function allocates memory for k-way edge refinement
+**************************************************************************/
+void AllocateVolKWayPartitionMemory(CtrlType *ctrl, GraphType *graph, int nparts)
+{
+  int nvtxs, pad64;
+
+  nvtxs = graph->nvtxs;
+
+  pad64 = (3*nvtxs+nparts)%2;
+
+  graph->rdata = idxmalloc(3*nvtxs+nparts+(sizeof(VRInfoType)/sizeof(idxtype))*nvtxs+pad64, "AllocateVolKWayPartitionMemory: rdata");
+  graph->pwgts          = graph->rdata;
+  graph->where          = graph->rdata + nparts;
+  graph->bndptr         = graph->rdata + nvtxs + nparts;
+  graph->bndind         = graph->rdata + 2*nvtxs + nparts;
+  graph->vrinfo          = (VRInfoType *)(graph->rdata + 3*nvtxs+nparts + pad64);
+
+}
+
+
+
+/*************************************************************************
+* This function computes the initial id/ed 
+**************************************************************************/
+void ComputeVolKWayPartitionParams(CtrlType *ctrl, GraphType *graph, int nparts)
+{
+  int i, ii, j, k, kk, l, nvtxs, nbnd, mincut, minvol, me, other, pid; 
+  idxtype *xadj, *vwgt, *adjncy, *adjwgt, *pwgts, *where;
+  VRInfoType *rinfo, *myrinfo, *orinfo;
+  VEDegreeType *myedegrees, *oedegrees;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  vwgt = graph->vwgt;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  where = graph->where;
+  pwgts = idxset(nparts, 0, graph->pwgts);
+  rinfo = graph->vrinfo;
+
+
+  /*------------------------------------------------------------
+  / Compute now the id/ed degrees
+  /------------------------------------------------------------*/
+  ctrl->wspace.cdegree = 0;
+  mincut = 0;
+  for (i=0; i<nvtxs; i++) {
+    me = where[i];
+    pwgts[me] += vwgt[i];
+
+    myrinfo = rinfo+i;
+    myrinfo->id = myrinfo->ed = myrinfo->nid = myrinfo->ndegrees = 0;
+    myrinfo->edegrees = NULL;
+
+    for (j=xadj[i]; j<xadj[i+1]; j++) {
+      if (me == where[adjncy[j]]) {
+        myrinfo->id += adjwgt[j];
+        myrinfo->nid++;
+      }
+    }
+    myrinfo->ed = graph->adjwgtsum[i] - myrinfo->id;
+
+    mincut += myrinfo->ed;
+
+    /* Time to compute the particular external degrees */
+    if (myrinfo->ed > 0) { 
+      myedegrees = myrinfo->edegrees = ctrl->wspace.vedegrees+ctrl->wspace.cdegree;
+      ctrl->wspace.cdegree += xadj[i+1]-xadj[i];
+
+      for (j=xadj[i]; j<xadj[i+1]; j++) {
+        other = where[adjncy[j]];
+        if (me != other) {
+          for (k=0; k<myrinfo->ndegrees; k++) {
+            if (myedegrees[k].pid == other) {
+              myedegrees[k].ed += adjwgt[j];
+              myedegrees[k].ned++;
+              break;
+            }
+          }
+          if (k == myrinfo->ndegrees) {
+            myedegrees[myrinfo->ndegrees].gv = 0;
+            myedegrees[myrinfo->ndegrees].pid = other;
+            myedegrees[myrinfo->ndegrees].ed = adjwgt[j];
+            myedegrees[myrinfo->ndegrees++].ned = 1;
+          }
+        }
+      }
+
+      ASSERT(myrinfo->ndegrees <= xadj[i+1]-xadj[i]);
+    }
+  }
+  graph->mincut = mincut/2;
+
+
+  ComputeKWayVolGains(ctrl, graph, nparts);
+
+}
+
+
+
+/*************************************************************************
+* This function computes the initial id/ed 
+**************************************************************************/
+void ComputeKWayVolGains(CtrlType *ctrl, GraphType *graph, int nparts)
+{
+  int i, ii, j, k, kk, l, nvtxs, me, other, pid, myndegrees; 
+  idxtype *xadj, *vsize, *adjncy, *adjwgt, *where, *bndind, *bndptr, *ophtable;
+  VRInfoType *rinfo, *myrinfo, *orinfo;
+  VEDegreeType *myedegrees, *oedegrees;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  vsize = graph->vsize;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  where = graph->where;
+  bndind = graph->bndind;
+  bndptr = idxset(nvtxs, -1, graph->bndptr);
+  rinfo = graph->vrinfo;
+
+  ophtable = idxset(nparts, -1, idxwspacemalloc(ctrl, nparts));
+
+  /*------------------------------------------------------------
+  / Compute now the iv/ev degrees
+  /------------------------------------------------------------*/
+  graph->minvol = graph->nbnd = 0;
+  for (i=0; i<nvtxs; i++) {
+    myrinfo = rinfo+i;
+    myrinfo->gv = -MAXIDX;
+
+    if (myrinfo->ndegrees > 0) {
+      me = where[i];
+      myedegrees = myrinfo->edegrees;
+      myndegrees = myrinfo->ndegrees;
+
+      graph->minvol += myndegrees*vsize[i];
+
+      for (j=xadj[i]; j<xadj[i+1]; j++) {
+        ii = adjncy[j];
+        other = where[ii];
+        orinfo = rinfo+ii;
+        oedegrees = orinfo->edegrees;
+
+        for (k=0; k<orinfo->ndegrees; k++) 
+          ophtable[oedegrees[k].pid] = k;
+        ophtable[other] = 1;  /* this is to simplify coding */
+
+        if (me == other) {
+          /* Find which domains 'i' is connected and 'ii' is not and update their gain */
+          for (k=0; k<myndegrees; k++) {
+            if (ophtable[myedegrees[k].pid] == -1)
+              myedegrees[k].gv -= vsize[ii];
+          }
+        }
+        else {
+          ASSERT(ophtable[me] != -1);
+
+          if (oedegrees[ophtable[me]].ned == 1) { /* I'm the only connection of 'ii' in 'me' */
+            /* Increase the gains for all the common domains between 'i' and 'ii' */
+            for (k=0; k<myndegrees; k++) {
+              if (ophtable[myedegrees[k].pid] != -1) 
+                myedegrees[k].gv += vsize[ii];
+            }
+          }
+          else {
+            /* Find which domains 'i' is connected and 'ii' is not and update their gain */
+            for (k=0; k<myndegrees; k++) {
+              if (ophtable[myedegrees[k].pid] == -1) 
+                myedegrees[k].gv -= vsize[ii];
+            }
+          }
+        }
+
+        for (kk=0; kk<orinfo->ndegrees; kk++) 
+          ophtable[oedegrees[kk].pid] = -1;
+        ophtable[other] = -1;
+      }
+
+      /* Compute the max vgain */
+      for (k=0; k<myndegrees; k++) {
+        if (myedegrees[k].gv > myrinfo->gv)
+          myrinfo->gv = myedegrees[k].gv;
+      }
+    }
+
+    if (myrinfo->ed > 0 && myrinfo->id == 0) 
+      myrinfo->gv += vsize[i];
+
+    if (myrinfo->gv >= 0 || myrinfo->ed-myrinfo->id >= 0)
+      BNDInsert(graph->nbnd, bndind, bndptr, i);
+  }
+
+  idxwspacefree(ctrl, nparts);
+
+}
+
+
+
+/*************************************************************************
+* This function projects a partition, and at the same time computes the
+* parameters for refinement.
+**************************************************************************/
+void ProjectVolKWayPartition(CtrlType *ctrl, GraphType *graph, int nparts)
+{
+  int i, j, k, nvtxs, me, other, istart, iend, ndegrees;
+  idxtype *xadj, *adjncy, *adjwgt, *adjwgtsum;
+  idxtype *cmap, *where;
+  idxtype *cwhere;
+  GraphType *cgraph;
+  VRInfoType *crinfo, *rinfo, *myrinfo;
+  VEDegreeType *myedegrees;
+  idxtype *htable;
+
+  cgraph = graph->coarser;
+  cwhere = cgraph->where;
+  crinfo = cgraph->vrinfo;
+
+  nvtxs = graph->nvtxs;
+  cmap = graph->cmap;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+  adjwgtsum = graph->adjwgtsum;
+
+  AllocateVolKWayPartitionMemory(ctrl, graph, nparts);
+  where = graph->where;
+  rinfo = graph->vrinfo;
+
+  /* Go through and project partition and compute id/ed for the nodes */
+  for (i=0; i<nvtxs; i++) {
+    k = cmap[i];
+    where[i] = cwhere[k];
+    cmap[i] = crinfo[k].ed;  /* For optimization */
+  }
+
+  htable = idxset(nparts, -1, idxwspacemalloc(ctrl, nparts));
+
+  ctrl->wspace.cdegree = 0;
+  for (i=0; i<nvtxs; i++) {
+    me = where[i];
+
+    myrinfo = rinfo+i;
+    myrinfo->id = myrinfo->ed = myrinfo->nid = myrinfo->ndegrees = 0;
+    myrinfo->edegrees = NULL;
+
+    myrinfo->id = adjwgtsum[i];
+    myrinfo->nid = xadj[i+1]-xadj[i];
+
+    if (cmap[i] > 0) { /* If it is an interface node. Note cmap[i] = crinfo[cmap[i]].ed */
+      istart = xadj[i];
+      iend = xadj[i+1];
+
+      myedegrees = myrinfo->edegrees = ctrl->wspace.vedegrees+ctrl->wspace.cdegree;
+      ctrl->wspace.cdegree += iend-istart;
+
+      ndegrees = 0;
+      for (j=istart; j<iend; j++) {
+        other = where[adjncy[j]];
+        if (me != other) {
+          myrinfo->ed += adjwgt[j];
+          myrinfo->nid--;
+          if ((k = htable[other]) == -1) {
+            htable[other] = ndegrees;
+            myedegrees[ndegrees].gv = 0;
+            myedegrees[ndegrees].pid = other;
+            myedegrees[ndegrees].ed = adjwgt[j];
+            myedegrees[ndegrees++].ned = 1;
+          }
+          else {
+            myedegrees[k].ed += adjwgt[j];
+            myedegrees[k].ned++;
+          }
+        }
+      }
+      myrinfo->id -= myrinfo->ed;
+
+      /* Remove space for edegrees if it was interior */
+      if (myrinfo->ed == 0) { 
+        myrinfo->edegrees = NULL;
+        ctrl->wspace.cdegree -= iend-istart;
+      }
+      else {
+        myrinfo->ndegrees = ndegrees;
+
+        for (j=0; j<ndegrees; j++)
+          htable[myedegrees[j].pid] = -1;
+      }
+    }
+  }
+
+  ComputeKWayVolGains(ctrl, graph, nparts);
+
+  idxcopy(nparts, cgraph->pwgts, graph->pwgts);
+  graph->mincut = cgraph->mincut;
+
+  FreeGraph(graph->coarser);
+  graph->coarser = NULL;
+
+  idxwspacefree(ctrl, nparts);
+
+}
+
+
+
+/*************************************************************************
+* This function computes the boundary definition for balancing
+**************************************************************************/
+void ComputeVolKWayBoundary(CtrlType *ctrl, GraphType *graph, int nparts)
+{
+  int i, nvtxs, nbnd;
+  idxtype *bndind, *bndptr;
+
+  nvtxs = graph->nvtxs;
+  bndind = graph->bndind;
+  bndptr = idxset(nvtxs, -1, graph->bndptr);
+
+
+  /*------------------------------------------------------------
+  / Compute the new boundary
+  /------------------------------------------------------------*/
+  nbnd = 0;
+  for (i=0; i<nvtxs; i++) {
+    if (graph->vrinfo[i].gv >=0 || graph->vrinfo[i].ed-graph->vrinfo[i].id >= 0) 
+      BNDInsert(nbnd, bndind, bndptr, i);
+  }
+
+  graph->nbnd = nbnd;
+}
+
+/*************************************************************************
+* This function computes the boundary definition for balancing
+**************************************************************************/
+void ComputeVolKWayBalanceBoundary(CtrlType *ctrl, GraphType *graph, int nparts)
+{
+  int i, nvtxs, nbnd;
+  idxtype *bndind, *bndptr;
+
+  nvtxs = graph->nvtxs;
+  bndind = graph->bndind;
+  bndptr = idxset(nvtxs, -1, graph->bndptr);
+
+
+  /*------------------------------------------------------------
+  / Compute the new boundary
+  /------------------------------------------------------------*/
+  nbnd = 0;
+  for (i=0; i<nvtxs; i++) {
+    if (graph->vrinfo[i].ed > 0) 
+      BNDInsert(nbnd, bndind, bndptr, i);
+  }
+
+  graph->nbnd = nbnd;
+}
+
diff --git a/contrib/Metis/macros.h b/contrib/Metis/macros.h
new file mode 100644
index 0000000000..8151e10b41
--- /dev/null
+++ b/contrib/Metis/macros.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * macros.h
+ *
+ * This file contains macros used in multilevel
+ *
+ * Started 9/25/94
+ * George
+ *
+ * $Id: macros.h,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ *
+ */
+
+
+/*************************************************************************
+* The following macro returns a random number in the specified range
+**************************************************************************/
+#ifdef __VC__
+#define RandomInRange(u) ((rand()>>3)%(u))
+#define RandomInRangeFast(u) ((rand()>>3)%(u))
+#else
+#define RandomInRange(u) ((int)(drand48()*((double)(u))))
+#define RandomInRangeFast(u) ((rand()>>3)%(u))
+#endif
+
+
+
+#define amax(a, b) ((a) >= (b) ? (a) : (b))
+#define amin(a, b) ((a) >= (b) ? (b) : (a))
+
+#define AND(a, b) ((a) < 0 ? ((-(a))&(b)) : ((a)&(b)))
+#define OR(a, b) ((a) < 0 ? -((-(a))|(b)) : ((a)|(b)))
+#define XOR(a, b) ((a) < 0 ? -((-(a))^(b)) : ((a)^(b)))
+
+#define SWAP(a, b, tmp)  \
+                 do {(tmp) = (a); (a) = (b); (b) = (tmp);} while(0) 
+
+#define INC_DEC(a, b, val) \
+                 do {(a) += (val); (b) -= (val);} while(0)
+
+
+#define scopy(n, a, b) (float *)memcpy((void *)(b), (void *)(a), sizeof(float)*(n))
+#define idxcopy(n, a, b) (idxtype *)memcpy((void *)(b), (void *)(a), sizeof(idxtype)*(n)) 
+
+#define HASHFCT(key, size) ((key)%(size))
+
+
+/*************************************************************************
+* Timer macros
+**************************************************************************/
+#define cleartimer(tmr) (tmr = 0.0)
+#define starttimer(tmr) (tmr -= seconds())
+#define stoptimer(tmr) (tmr += seconds())
+#define gettimer(tmr) (tmr)
+
+
+/*************************************************************************
+* This macro is used to handle dbglvl
+**************************************************************************/
+#define IFSET(a, flag, cmd) if ((a)&(flag)) (cmd);
+
+/*************************************************************************
+* These macros are used for debuging memory leaks
+**************************************************************************/
+#ifdef DMALLOC
+#define imalloc(n, msg) (malloc(sizeof(int)*(n)))
+#define fmalloc(n, msg) (malloc(sizeof(float)*(n)))
+#define idxmalloc(n, msg) (malloc(sizeof(idxtype)*(n)))
+#define ismalloc(n, val, msg) (iset((n), (val), malloc(sizeof(int)*(n))))
+#define idxsmalloc(n, val, msg) (idxset((n), (val), malloc(sizeof(idxtype)*(n))))
+#define GKmalloc(a, b) (malloc((a)))
+#endif
+
+#ifdef DMALLOC
+#   define MALLOC_CHECK(ptr)                                          \
+    if (malloc_verify((ptr)) == DMALLOC_VERIFY_ERROR) {  \
+        printf("***MALLOC_CHECK failed on line %d of file %s: " #ptr "\n", \
+              __LINE__, __FILE__);                               \
+        abort();                                                \
+    }
+#else
+#   define MALLOC_CHECK(ptr) ;
+#endif 
+
+
+
+/*************************************************************************
+* This macro converts a length array in a CSR one
+**************************************************************************/
+#define MAKECSR(i, n, a) \
+   do { \
+     for (i=1; i<n; i++) a[i] += a[i-1]; \
+     for (i=n; i>0; i--) a[i] = a[i-1]; \
+     a[0] = 0; \
+   } while(0) 
+
+
+/*************************************************************************
+* These macros insert and remove nodes from the boundary list
+**************************************************************************/
+#define BNDInsert(nbnd, bndind, bndptr, vtx) \
+   do { \
+     ASSERT(bndptr[vtx] == -1); \
+     bndind[nbnd] = vtx; \
+     bndptr[vtx] = nbnd++;\
+   } while(0) 
+
+#define BNDDelete(nbnd, bndind, bndptr, vtx) \
+   do { \
+     ASSERT(bndptr[vtx] != -1); \
+     bndind[bndptr[vtx]] = bndind[--nbnd]; \
+     bndptr[bndind[nbnd]] = bndptr[vtx]; \
+     bndptr[vtx] = -1; \
+   } while(0) 
+
+
+
+/*************************************************************************
+* These are debugging macros
+**************************************************************************/
+#ifdef DEBUG
+#   define ASSERT(expr)                                          \
+    if (!(expr)) {                                               \
+        printf("***ASSERTION failed on line %d of file %s: " #expr "\n", \
+              __LINE__, __FILE__);                               \
+        abort();                                                \
+    }
+#else
+#   define ASSERT(expr) ;
+#endif 
+
+#ifdef DEBUG
+#   define ASSERTP(expr, msg)                                          \
+    if (!(expr)) {                                               \
+        printf("***ASSERTION failed on line %d of file %s: " #expr "\n", \
+              __LINE__, __FILE__);                               \
+        printf msg ; \
+        abort();                                                \
+    }
+#else
+#   define ASSERTP(expr, msg) ;
+#endif 
diff --git a/contrib/Metis/match.c b/contrib/Metis/match.c
new file mode 100644
index 0000000000..0896fe5cf0
--- /dev/null
+++ b/contrib/Metis/match.c
@@ -0,0 +1,267 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * match.c
+ *
+ * This file contains the code that computes matchings and creates the next
+ * level coarse graph.
+ *
+ * Started 7/23/97
+ * George
+ *
+ * $Id: match.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function finds a matching using the HEM heuristic
+**************************************************************************/
+void Match_RM(CtrlType *ctrl, GraphType *graph)
+{
+  int i, ii, j, nvtxs, cnvtxs, maxidx;
+  idxtype *xadj, *vwgt, *adjncy, *adjwgt;
+  idxtype *match, *cmap, *perm;
+
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->MatchTmr));
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  vwgt = graph->vwgt;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  cmap = graph->cmap;
+  match = idxset(nvtxs, UNMATCHED, idxwspacemalloc(ctrl, nvtxs));
+
+  perm = idxwspacemalloc(ctrl, nvtxs);
+  RandomPermute(nvtxs, perm, 1);
+
+  cnvtxs = 0;
+  for (ii=0; ii<nvtxs; ii++) {
+    i = perm[ii];
+
+    if (match[i] == UNMATCHED) {  /* Unmatched */
+      maxidx = i;
+
+      /* Find a random matching, subject to maxvwgt constraints */
+      for (j=xadj[i]; j<xadj[i+1]; j++) {
+        if (match[adjncy[j]] == UNMATCHED && vwgt[i]+vwgt[adjncy[j]] <= ctrl->maxvwgt) {
+          maxidx = adjncy[j];
+          break;
+        }
+      }
+
+      cmap[i] = cmap[maxidx] = cnvtxs++;
+      match[i] = maxidx;
+      match[maxidx] = i;
+    }
+  }
+
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->MatchTmr));
+
+  CreateCoarseGraph(ctrl, graph, cnvtxs, match, perm);
+
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+}
+
+
+/*************************************************************************
+* This function finds a matching using the HEM heuristic
+**************************************************************************/
+void Match_RM_NVW(CtrlType *ctrl, GraphType *graph)
+{
+  int i, ii, j, nvtxs, cnvtxs, maxidx;
+  idxtype *xadj, *adjncy;
+  idxtype *match, *cmap, *perm;
+
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->MatchTmr));
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+
+  cmap = graph->cmap;
+  match = idxset(nvtxs, UNMATCHED, idxwspacemalloc(ctrl, nvtxs));
+
+  perm = idxwspacemalloc(ctrl, nvtxs);
+  RandomPermute(nvtxs, perm, 1);
+
+  cnvtxs = 0;
+  for (ii=0; ii<nvtxs; ii++) {
+    i = perm[ii];
+
+    if (match[i] == UNMATCHED) {  /* Unmatched */
+      maxidx = i;
+
+      /* Find a random matching, subject to maxvwgt constraints */
+      for (j=xadj[i]; j<xadj[i+1]; j++) {
+        if (match[adjncy[j]] == UNMATCHED) {
+          maxidx = adjncy[j];
+          break;
+        }
+      }
+
+      cmap[i] = cmap[maxidx] = cnvtxs++;
+      match[i] = maxidx;
+      match[maxidx] = i;
+    }
+  }
+
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->MatchTmr));
+
+  CreateCoarseGraph_NVW(ctrl, graph, cnvtxs, match, perm);
+
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+}
+
+
+
+/*************************************************************************
+* This function finds a matching using the HEM heuristic
+**************************************************************************/
+void Match_HEM(CtrlType *ctrl, GraphType *graph)
+{
+  int i, ii, j, k, nvtxs, cnvtxs, maxidx, maxwgt;
+  idxtype *xadj, *vwgt, *adjncy, *adjwgt;
+  idxtype *match, *cmap, *perm;
+
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->MatchTmr));
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  vwgt = graph->vwgt;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  cmap = graph->cmap;
+  match = idxset(nvtxs, UNMATCHED, idxwspacemalloc(ctrl, nvtxs));
+
+  perm = idxwspacemalloc(ctrl, nvtxs);
+  RandomPermute(nvtxs, perm, 1);
+
+  cnvtxs = 0;
+  for (ii=0; ii<nvtxs; ii++) {
+    i = perm[ii];
+
+    if (match[i] == UNMATCHED) {  /* Unmatched */
+      maxidx = i;
+      maxwgt = 0;
+
+      /* Find a heavy-edge matching, subject to maxvwgt constraints */
+      for (j=xadj[i]; j<xadj[i+1]; j++) {
+        k = adjncy[j];
+        if (match[k] == UNMATCHED && maxwgt < adjwgt[j] && vwgt[i]+vwgt[k] <= ctrl->maxvwgt) {
+          maxwgt = adjwgt[j];
+          maxidx = adjncy[j];
+        }
+      }
+
+      cmap[i] = cmap[maxidx] = cnvtxs++;
+      match[i] = maxidx;
+      match[maxidx] = i;
+    }
+  }
+
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->MatchTmr));
+
+  CreateCoarseGraph(ctrl, graph, cnvtxs, match, perm);
+
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+}
+
+
+
+/*************************************************************************
+* This function finds a matching using the HEM heuristic
+**************************************************************************/
+void Match_SHEM(CtrlType *ctrl, GraphType *graph)
+{
+  int i, ii, j, k, nvtxs, cnvtxs, maxidx, maxwgt, avgdegree;
+  idxtype *xadj, *vwgt, *adjncy, *adjwgt;
+  idxtype *match, *cmap, *degrees, *perm, *tperm;
+
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->MatchTmr));
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  vwgt = graph->vwgt;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  cmap = graph->cmap;
+  match = idxset(nvtxs, UNMATCHED, idxwspacemalloc(ctrl, nvtxs));
+
+  perm = idxwspacemalloc(ctrl, nvtxs);
+  tperm = idxwspacemalloc(ctrl, nvtxs);
+  degrees = idxwspacemalloc(ctrl, nvtxs);
+
+  RandomPermute(nvtxs, tperm, 1);
+  avgdegree = 0.7*(xadj[nvtxs]/nvtxs);
+  for (i=0; i<nvtxs; i++) 
+    degrees[i] = (xadj[i+1]-xadj[i] > avgdegree ? avgdegree : xadj[i+1]-xadj[i]);
+  BucketSortKeysInc(nvtxs, avgdegree, degrees, tperm, perm);
+
+  cnvtxs = 0;
+
+  /* Take care any islands. Islands are matched with non-islands due to coarsening */
+  for (ii=0; ii<nvtxs; ii++) {
+    i = perm[ii];
+
+    if (match[i] == UNMATCHED) {  /* Unmatched */
+      if (xadj[i] < xadj[i+1])
+        break;
+
+      maxidx = i;
+      for (j=nvtxs-1; j>ii; j--) {
+        k = perm[j];
+        if (match[k] == UNMATCHED && xadj[k] < xadj[k+1]) {
+          maxidx = k;
+          break;
+        }
+      }
+
+      cmap[i] = cmap[maxidx] = cnvtxs++;
+      match[i] = maxidx;
+      match[maxidx] = i;
+    }
+  }
+
+  /* Continue with normal matching */
+  for (; ii<nvtxs; ii++) {
+    i = perm[ii];
+
+    if (match[i] == UNMATCHED) {  /* Unmatched */
+      maxidx = i;
+      maxwgt = 0;
+
+      /* Find a heavy-edge matching, subject to maxvwgt constraints */
+      for (j=xadj[i]; j<xadj[i+1]; j++) {
+        if (match[adjncy[j]] == UNMATCHED && maxwgt < adjwgt[j] && vwgt[i]+vwgt[adjncy[j]] <= ctrl->maxvwgt) {
+          maxwgt = adjwgt[j];
+          maxidx = adjncy[j];
+        }
+      }
+
+      cmap[i] = cmap[maxidx] = cnvtxs++;
+      match[i] = maxidx;
+      match[maxidx] = i;
+    }
+  }
+
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->MatchTmr));
+
+  idxwspacefree(ctrl, nvtxs);  /* degrees */
+  idxwspacefree(ctrl, nvtxs);  /* tperm */
+
+  CreateCoarseGraph(ctrl, graph, cnvtxs, match, perm);
+
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+}
+
diff --git a/contrib/Metis/mbalance.c b/contrib/Metis/mbalance.c
new file mode 100644
index 0000000000..3fc5a6e2a9
--- /dev/null
+++ b/contrib/Metis/mbalance.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * mbalance.c
+ *
+ * This file contains code that is used to forcefully balance either
+ * bisections or k-sections
+ *
+ * Started 7/29/97
+ * George
+ *
+ * $Id: mbalance.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function is the entry point of the bisection balancing algorithms.
+**************************************************************************/
+void MocBalance2Way(CtrlType *ctrl, GraphType *graph, float *tpwgts, float lbfactor)
+{
+
+  if (Compute2WayHLoadImbalance(graph->ncon, graph->npwgts, tpwgts) < lbfactor)
+    return;
+
+  MocGeneral2WayBalance(ctrl, graph, tpwgts, lbfactor);
+
+}
+
+
+/*************************************************************************
+* This function performs an edge-based FM refinement
+**************************************************************************/
+void MocGeneral2WayBalance(CtrlType *ctrl, GraphType *graph, float *tpwgts, float lbfactor)
+{
+  int i, ii, j, k, l, kwgt, nvtxs, ncon, nbnd, nswaps, from, to, pass, me, limit, tmp, cnum;
+  idxtype *xadj, *adjncy, *adjwgt, *where, *id, *ed, *bndptr, *bndind;
+  idxtype *moved, *swaps, *perm, *qnum;
+  float *nvwgt, *npwgts, mindiff[MAXNCON], origbal, minbal, newbal;
+  PQueueType parts[MAXNCON][2];
+  int higain, oldgain, mincut, newcut, mincutorder;
+  int qsizes[MAXNCON][2];
+
+  nvtxs = graph->nvtxs;
+  ncon = graph->ncon;
+  xadj = graph->xadj;
+  nvwgt = graph->nvwgt;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+  where = graph->where;
+  id = graph->id;
+  ed = graph->ed;
+  npwgts = graph->npwgts;
+  bndptr = graph->bndptr;
+  bndind = graph->bndind;
+
+  moved = idxwspacemalloc(ctrl, nvtxs);
+  swaps = idxwspacemalloc(ctrl, nvtxs);
+  perm = idxwspacemalloc(ctrl, nvtxs);
+  qnum = idxwspacemalloc(ctrl, nvtxs);
+
+  limit = amin(amax(0.01*nvtxs, 15), 100);
+
+  /* Initialize the queues */
+  for (i=0; i<ncon; i++) {
+    PQueueInit(ctrl, &parts[i][0], nvtxs, PLUS_GAINSPAN+1);
+    PQueueInit(ctrl, &parts[i][1], nvtxs, PLUS_GAINSPAN+1);
+    qsizes[i][0] = qsizes[i][1] = 0;
+  }
+
+  for (i=0; i<nvtxs; i++) {
+    qnum[i] = samax(ncon, nvwgt+i*ncon);
+    qsizes[qnum[i]][where[i]]++;
+  }
+
+/*
+  printf("Weight Distribution:    \t");
+  for (i=0; i<ncon; i++) 
+    printf(" [%d %d]", qsizes[i][0], qsizes[i][1]); 
+  printf("\n");
+*/
+
+  for (from=0; from<2; from++) {
+    for (j=0; j<ncon; j++) {
+      if (qsizes[j][from] == 0) {
+        for (i=0; i<nvtxs; i++) {
+          if (where[i] != from)
+            continue;
+
+          k = samax2(ncon, nvwgt+i*ncon);
+          if (k == j && qsizes[qnum[i]][from] > qsizes[j][from] && nvwgt[i*ncon+qnum[i]] < 1.3*nvwgt[i*ncon+j]) {
+            qsizes[qnum[i]][from]--;
+            qsizes[j][from]++;
+            qnum[i] = j;
+          }
+        }
+      }
+    }
+  }
+
+/*
+  printf("Weight Distribution (after):\t ");
+  for (i=0; i<ncon; i++) 
+    printf(" [%d %d]", qsizes[i][0], qsizes[i][1]); 
+  printf("\n");
+*/
+
+
+
+  for (i=0; i<ncon; i++) 
+    mindiff[i] = fabs(tpwgts[0]-npwgts[i]);
+  minbal = origbal = Compute2WayHLoadImbalance(ncon, npwgts, tpwgts);
+  newcut = mincut = graph->mincut;
+  mincutorder = -1;
+
+  if (ctrl->dbglvl&DBG_REFINE) {
+    printf("Parts: [");
+    for (l=0; l<ncon; l++)
+      printf("(%.3f, %.3f) ", npwgts[l], npwgts[ncon+l]);
+    printf("] T[%.3f %.3f], Nv-Nb[%5d, %5d]. ICut: %6d, LB: %.3f [B]\n", tpwgts[0], tpwgts[1], graph->nvtxs, graph->nbnd, graph->mincut, origbal);
+  }
+
+  idxset(nvtxs, -1, moved);
+
+  ASSERT(ComputeCut(graph, where) == graph->mincut);
+  ASSERT(CheckBnd(graph));
+
+  /* Insert all nodes in the priority queues */
+  nbnd = graph->nbnd;
+  RandomPermute(nvtxs, perm, 1);
+  for (ii=0; ii<nvtxs; ii++) {
+    i = perm[ii];
+    PQueueInsert(&parts[qnum[i]][where[i]], i, ed[i]-id[i]);
+  }
+
+  for (nswaps=0; nswaps<nvtxs; nswaps++) {
+    if (minbal < lbfactor)
+      break;
+
+    SelectQueue(ncon, npwgts, tpwgts, &from, &cnum, parts);
+    to = (from+1)%2;
+
+    if (from == -1 || (higain = PQueueGetMax(&parts[cnum][from])) == -1)
+      break;
+
+    saxpy(ncon, 1.0, nvwgt+higain*ncon, 1, npwgts+to*ncon, 1);
+    saxpy(ncon, -1.0, nvwgt+higain*ncon, 1, npwgts+from*ncon, 1);
+    newcut -= (ed[higain]-id[higain]);
+    newbal = Compute2WayHLoadImbalance(ncon, npwgts, tpwgts);
+
+    if (newbal < minbal || (newbal == minbal && 
+        (newcut < mincut || (newcut == mincut && BetterBalance(ncon, npwgts, tpwgts, mindiff))))) {
+      mincut = newcut;
+      minbal = newbal;
+      mincutorder = nswaps;
+      for (i=0; i<ncon; i++)
+        mindiff[i] = fabs(tpwgts[0]-npwgts[i]);
+    }
+    else if (nswaps-mincutorder > limit) { /* We hit the limit, undo last move */
+      newcut += (ed[higain]-id[higain]);
+      saxpy(ncon, 1.0, nvwgt+higain*ncon, 1, npwgts+from*ncon, 1);
+      saxpy(ncon, -1.0, nvwgt+higain*ncon, 1, npwgts+to*ncon, 1);
+      break;
+    }
+
+    where[higain] = to;
+    moved[higain] = nswaps;
+    swaps[nswaps] = higain;
+
+    if (ctrl->dbglvl&DBG_MOVEINFO) {
+      printf("Moved %6d from %d(%d). Gain: %5d, Cut: %5d, NPwgts: ", higain, from, cnum, ed[higain]-id[higain], newcut);
+      for (l=0; l<ncon; l++) 
+        printf("(%.3f, %.3f) ", npwgts[l], npwgts[ncon+l]);
+      printf(", %.3f LB: %.3f\n", minbal, newbal);
+    }
+
+
+    /**************************************************************
+    * Update the id[i]/ed[i] values of the affected nodes
+    ***************************************************************/
+    SWAP(id[higain], ed[higain], tmp);
+    if (ed[higain] == 0 && bndptr[higain] != -1 && xadj[higain] < xadj[higain+1]) 
+      BNDDelete(nbnd, bndind,  bndptr, higain);
+    if (ed[higain] > 0 && bndptr[higain] == -1)
+      BNDInsert(nbnd, bndind,  bndptr, higain);
+
+    for (j=xadj[higain]; j<xadj[higain+1]; j++) {
+      k = adjncy[j];
+      oldgain = ed[k]-id[k];
+
+      kwgt = (to == where[k] ? adjwgt[j] : -adjwgt[j]);
+      INC_DEC(id[k], ed[k], kwgt);
+
+      /* Update the queue position */
+      if (moved[k] == -1)
+        PQueueUpdate(&parts[qnum[k]][where[k]], k, oldgain, ed[k]-id[k]);
+
+      /* Update its boundary information */
+      if (ed[k] == 0 && bndptr[k] != -1) 
+        BNDDelete(nbnd, bndind, bndptr, k);
+      else if (ed[k] > 0 && bndptr[k] == -1)  
+        BNDInsert(nbnd, bndind, bndptr, k);
+    }
+  }
+
+
+
+  /****************************************************************
+  * Roll back computations
+  *****************************************************************/
+  for (nswaps--; nswaps>mincutorder; nswaps--) {
+    higain = swaps[nswaps];
+
+    to = where[higain] = (where[higain]+1)%2;
+    SWAP(id[higain], ed[higain], tmp);
+    if (ed[higain] == 0 && bndptr[higain] != -1 && xadj[higain] < xadj[higain+1])
+      BNDDelete(nbnd, bndind,  bndptr, higain);
+    else if (ed[higain] > 0 && bndptr[higain] == -1)
+      BNDInsert(nbnd, bndind,  bndptr, higain);
+
+    saxpy(ncon, 1.0, nvwgt+higain*ncon, 1, npwgts+to*ncon, 1);
+    saxpy(ncon, -1.0, nvwgt+higain*ncon, 1, npwgts+((to+1)%2)*ncon, 1);
+    for (j=xadj[higain]; j<xadj[higain+1]; j++) {
+      k = adjncy[j];
+
+      kwgt = (to == where[k] ? adjwgt[j] : -adjwgt[j]);
+      INC_DEC(id[k], ed[k], kwgt);
+
+      if (bndptr[k] != -1 && ed[k] == 0)
+        BNDDelete(nbnd, bndind, bndptr, k);
+      if (bndptr[k] == -1 && ed[k] > 0)
+        BNDInsert(nbnd, bndind, bndptr, k);
+    }
+  }
+
+  if (ctrl->dbglvl&DBG_REFINE) {
+    printf("\tMincut: %6d at %5d, NBND: %6d, NPwgts: [", mincut, mincutorder, nbnd);
+    for (l=0; l<ncon; l++)
+      printf("(%.3f, %.3f) ", npwgts[l], npwgts[ncon+l]);
+    printf("], LB: %.3f\n", Compute2WayHLoadImbalance(ncon, npwgts, tpwgts));
+  }
+
+  graph->mincut = mincut;
+  graph->nbnd = nbnd;
+
+
+  for (i=0; i<ncon; i++) {
+    PQueueFree(ctrl, &parts[i][0]);
+    PQueueFree(ctrl, &parts[i][1]);
+  }
+
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+
+}
+
diff --git a/contrib/Metis/mbalance2.c b/contrib/Metis/mbalance2.c
new file mode 100644
index 0000000000..6991dbf523
--- /dev/null
+++ b/contrib/Metis/mbalance2.c
@@ -0,0 +1,328 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * mbalance2.c
+ *
+ * This file contains code that is used to forcefully balance either
+ * bisections or k-sections
+ *
+ * Started 7/29/97
+ * George
+ *
+ * $Id: mbalance2.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function is the entry point of the bisection balancing algorithms.
+**************************************************************************/
+void MocBalance2Way2(CtrlType *ctrl, GraphType *graph, float *tpwgts, float *ubvec)
+{
+  int i;
+  float tvec[MAXNCON];
+
+  Compute2WayHLoadImbalanceVec(graph->ncon, graph->npwgts, tpwgts, tvec);
+  if (!AreAllBelow(graph->ncon, tvec, ubvec))
+    MocGeneral2WayBalance2(ctrl, graph, tpwgts, ubvec);
+}
+
+
+
+/*************************************************************************
+* This function performs an edge-based FM refinement
+**************************************************************************/
+void MocGeneral2WayBalance2(CtrlType *ctrl, GraphType *graph, float *tpwgts, float *ubvec)
+{
+  int i, ii, j, k, l, kwgt, nvtxs, ncon, nbnd, nswaps, from, to, pass, me, limit, tmp, cnum;
+  idxtype *xadj, *adjncy, *adjwgt, *where, *id, *ed, *bndptr, *bndind;
+  idxtype *moved, *swaps, *perm, *qnum;
+  float *nvwgt, *npwgts, origbal[MAXNCON], minbal[MAXNCON], newbal[MAXNCON];
+  PQueueType parts[MAXNCON][2];
+  int higain, oldgain, mincut, newcut, mincutorder;
+  float *maxwgt, *minwgt, tvec[MAXNCON];
+
+
+  nvtxs = graph->nvtxs;
+  ncon = graph->ncon;
+  xadj = graph->xadj;
+  nvwgt = graph->nvwgt;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+  where = graph->where;
+  id = graph->id;
+  ed = graph->ed;
+  npwgts = graph->npwgts;
+  bndptr = graph->bndptr;
+  bndind = graph->bndind;
+
+  moved = idxwspacemalloc(ctrl, nvtxs);
+  swaps = idxwspacemalloc(ctrl, nvtxs);
+  perm = idxwspacemalloc(ctrl, nvtxs);
+  qnum = idxwspacemalloc(ctrl, nvtxs);
+
+  limit = amin(amax(0.01*nvtxs, 15), 100);
+
+  /* Setup the weight intervals of the two subdomains */
+  minwgt = fwspacemalloc(ctrl, 2*ncon);
+  maxwgt = fwspacemalloc(ctrl, 2*ncon);
+
+  for (i=0; i<2; i++) {
+    for (j=0; j<ncon; j++) {
+      maxwgt[i*ncon+j] = tpwgts[i]*ubvec[j];
+      minwgt[i*ncon+j] = tpwgts[i]*(1.0/ubvec[j]);
+    }
+  }
+
+
+  /* Initialize the queues */
+  for (i=0; i<ncon; i++) {
+    PQueueInit(ctrl, &parts[i][0], nvtxs, PLUS_GAINSPAN+1);
+    PQueueInit(ctrl, &parts[i][1], nvtxs, PLUS_GAINSPAN+1);
+  }
+  for (i=0; i<nvtxs; i++)
+    qnum[i] = samax(ncon, nvwgt+i*ncon);
+
+  Compute2WayHLoadImbalanceVec(ncon, npwgts, tpwgts, origbal);
+  for (i=0; i<ncon; i++) 
+    minbal[i] = origbal[i];
+
+  newcut = mincut = graph->mincut;
+  mincutorder = -1;
+
+  if (ctrl->dbglvl&DBG_REFINE) {
+    printf("Parts: [");
+    for (l=0; l<ncon; l++)
+      printf("(%.3f, %.3f) ", npwgts[l], npwgts[ncon+l]);
+    printf("] T[%.3f %.3f], Nv-Nb[%5d, %5d]. ICut: %6d, LB: ", tpwgts[0], tpwgts[1], 
+            graph->nvtxs, graph->nbnd, graph->mincut);
+    for (i=0; i<ncon; i++)
+      printf("%.3f ", origbal[i]);
+    printf("[B]\n");
+  }
+
+  idxset(nvtxs, -1, moved);
+
+  ASSERT(ComputeCut(graph, where) == graph->mincut);
+  ASSERT(CheckBnd(graph));
+
+  /* Insert all nodes in the priority queues */
+  nbnd = graph->nbnd;
+  RandomPermute(nvtxs, perm, 1);
+  for (ii=0; ii<nvtxs; ii++) {
+    i = perm[ii];
+    PQueueInsert(&parts[qnum[i]][where[i]], i, ed[i]-id[i]);
+  }
+
+
+  for (nswaps=0; nswaps<nvtxs; nswaps++) {
+    if (AreAllBelow(ncon, minbal, ubvec))
+      break;
+
+    SelectQueue3(ncon, npwgts, tpwgts, &from, &cnum, parts, maxwgt);
+    to = (from+1)%2;
+
+    if (from == -1 || (higain = PQueueGetMax(&parts[cnum][from])) == -1)
+      break;
+
+    saxpy(ncon, 1.0, nvwgt+higain*ncon, 1, npwgts+to*ncon, 1);
+    saxpy(ncon, -1.0, nvwgt+higain*ncon, 1, npwgts+from*ncon, 1);
+    newcut -= (ed[higain]-id[higain]);
+    Compute2WayHLoadImbalanceVec(ncon, npwgts, tpwgts, newbal);
+
+    if (IsBetter2wayBalance(ncon, newbal, minbal, ubvec) || 
+        (IsBetter2wayBalance(ncon, newbal, origbal, ubvec) && newcut < mincut)) {
+      mincut = newcut;
+      for (i=0; i<ncon; i++) 
+        minbal[i] = newbal[i];
+      mincutorder = nswaps;
+    }
+    else if (nswaps-mincutorder > limit) { /* We hit the limit, undo last move */
+      newcut += (ed[higain]-id[higain]);
+      saxpy(ncon, 1.0, nvwgt+higain*ncon, 1, npwgts+from*ncon, 1);
+      saxpy(ncon, -1.0, nvwgt+higain*ncon, 1, npwgts+to*ncon, 1);
+      break;
+    }
+
+    where[higain] = to;
+    moved[higain] = nswaps;
+    swaps[nswaps] = higain;
+
+    if (ctrl->dbglvl&DBG_MOVEINFO) {
+      printf("Moved %6d from %d(%d). Gain: %5d, Cut: %5d, NPwgts: ", higain, from, cnum, ed[higain]-id[higain], newcut);
+      for (i=0; i<ncon; i++) 
+        printf("(%.3f, %.3f) ", npwgts[i], npwgts[ncon+i]);
+
+      Compute2WayHLoadImbalanceVec(ncon, npwgts, tpwgts, tvec);
+      printf(", LB: ");
+      for (i=0; i<ncon; i++) 
+        printf("%.3f ", tvec[i]);
+      if (mincutorder == nswaps)
+        printf(" *\n");
+      else
+        printf("\n");
+    }
+
+
+    /**************************************************************
+    * Update the id[i]/ed[i] values of the affected nodes
+    ***************************************************************/
+    SWAP(id[higain], ed[higain], tmp);
+    if (ed[higain] == 0 && bndptr[higain] != -1 && xadj[higain] < xadj[higain+1]) 
+      BNDDelete(nbnd, bndind,  bndptr, higain);
+    if (ed[higain] > 0 && bndptr[higain] == -1)
+      BNDInsert(nbnd, bndind,  bndptr, higain);
+
+    for (j=xadj[higain]; j<xadj[higain+1]; j++) {
+      k = adjncy[j];
+      oldgain = ed[k]-id[k];
+
+      kwgt = (to == where[k] ? adjwgt[j] : -adjwgt[j]);
+      INC_DEC(id[k], ed[k], kwgt);
+
+      /* Update the queue position */
+      if (moved[k] == -1)
+        PQueueUpdate(&parts[qnum[k]][where[k]], k, oldgain, ed[k]-id[k]);
+
+      /* Update its boundary information */
+      if (ed[k] == 0 && bndptr[k] != -1) 
+        BNDDelete(nbnd, bndind, bndptr, k);
+      else if (ed[k] > 0 && bndptr[k] == -1)  
+        BNDInsert(nbnd, bndind, bndptr, k);
+    }
+   
+  }
+
+
+
+  /****************************************************************
+  * Roll back computations
+  *****************************************************************/
+  for (i=0; i<nswaps; i++)
+    moved[swaps[i]] = -1;  /* reset moved array */
+  for (nswaps--; nswaps>mincutorder; nswaps--) {
+    higain = swaps[nswaps];
+
+    to = where[higain] = (where[higain]+1)%2;
+    SWAP(id[higain], ed[higain], tmp);
+    if (ed[higain] == 0 && bndptr[higain] != -1 && xadj[higain] < xadj[higain+1])
+      BNDDelete(nbnd, bndind,  bndptr, higain);
+    else if (ed[higain] > 0 && bndptr[higain] == -1)
+      BNDInsert(nbnd, bndind,  bndptr, higain);
+
+    saxpy(ncon, 1.0, nvwgt+higain*ncon, 1, npwgts+to*ncon, 1);
+    saxpy(ncon, -1.0, nvwgt+higain*ncon, 1, npwgts+((to+1)%2)*ncon, 1);
+    for (j=xadj[higain]; j<xadj[higain+1]; j++) {
+      k = adjncy[j];
+
+      kwgt = (to == where[k] ? adjwgt[j] : -adjwgt[j]);
+      INC_DEC(id[k], ed[k], kwgt);
+
+      if (bndptr[k] != -1 && ed[k] == 0)
+        BNDDelete(nbnd, bndind, bndptr, k);
+      if (bndptr[k] == -1 && ed[k] > 0)
+        BNDInsert(nbnd, bndind, bndptr, k);
+    }
+  }
+
+  if (ctrl->dbglvl&DBG_REFINE) {
+    printf("\tMincut: %6d at %5d, NBND: %6d, NPwgts: [", mincut, mincutorder, nbnd);
+    for (i=0; i<ncon; i++)
+      printf("(%.3f, %.3f) ", npwgts[i], npwgts[ncon+i]);
+    printf("], LB: ");
+    Compute2WayHLoadImbalanceVec(ncon, npwgts, tpwgts, tvec);
+    for (i=0; i<ncon; i++) 
+      printf("%.3f ", tvec[i]);
+    printf("\n");
+  }
+
+  graph->mincut = mincut;
+  graph->nbnd = nbnd;
+
+
+  for (i=0; i<ncon; i++) {
+    PQueueFree(ctrl, &parts[i][0]);
+    PQueueFree(ctrl, &parts[i][1]);
+  }
+
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+  fwspacefree(ctrl, 2*ncon);
+  fwspacefree(ctrl, 2*ncon);
+
+}
+
+
+
+
+/*************************************************************************
+* This function selects the partition number and the queue from which
+* we will move vertices out
+**************************************************************************/ 
+void SelectQueue3(int ncon, float *npwgts, float *tpwgts, int *from, int *cnum, 
+       PQueueType queues[MAXNCON][2], float *maxwgt)
+{
+  int i, j, maxgain=0;
+  float maxdiff=0.0, diff;
+
+  *from = -1;
+  *cnum = -1;
+
+  /* First determine the side and the queue, irrespective of the presence of nodes */
+  for (j=0; j<2; j++) {
+    for (i=0; i<ncon; i++) {
+      diff = npwgts[j*ncon+i]-maxwgt[j*ncon+i];
+      if (diff >= maxdiff) {
+        maxdiff = diff;
+        *from = j;
+        *cnum = i;
+      }
+    }
+  }
+
+/* DELETE
+j = *from;
+for (i=0; i<ncon; i++)
+  printf("[%5d %5d %.4f %.4f] ", i, PQueueGetSize(&queues[i][j]), npwgts[j*ncon+i], maxwgt[j*ncon+i]);
+printf("***[%5d %5d]\n", *cnum, *from);
+*/
+
+  /* If the desired queue is empty, select a node from that side anyway */
+  if (*from != -1 && PQueueGetSize(&queues[*cnum][*from]) == 0) {
+    for (i=0; i<ncon; i++) {
+      if (PQueueGetSize(&queues[i][*from]) > 0) {
+        maxdiff = (npwgts[(*from)*ncon+i] - maxwgt[(*from)*ncon+i]);
+        *cnum = i;
+        break;
+      }
+    }
+
+    for (i++; i<ncon; i++) {
+      diff = npwgts[(*from)*ncon+i] - maxwgt[(*from)*ncon+i];
+      if (diff > maxdiff && PQueueGetSize(&queues[i][*from]) > 0) {
+        maxdiff = diff;
+        *cnum = i;
+      }
+    }
+  }
+
+  /* If the constraints ar OK, select a high gain vertex */
+  if (*from == -1) {
+    maxgain = -100000;
+    for (j=0; j<2; j++) {
+      for (i=0; i<ncon; i++) {
+        if (PQueueGetSize(&queues[i][j]) > 0 && PQueueGetKey(&queues[i][j]) > maxgain) {
+          maxgain = PQueueGetKey(&queues[i][0]); 
+          *from = j;
+          *cnum = i;
+        }
+      }
+    }
+
+    /* printf("(%2d %2d) %3d\n", *from, *cnum, maxgain); */
+  }
+}
diff --git a/contrib/Metis/mcoarsen.c b/contrib/Metis/mcoarsen.c
new file mode 100644
index 0000000000..4c4237641b
--- /dev/null
+++ b/contrib/Metis/mcoarsen.c
@@ -0,0 +1,91 @@
+/*
+ * mcoarsen.c
+ *
+ * This file contains the driving routines for the coarsening process 
+ *
+ * Started 7/23/97
+ * George
+ *
+ * $Id: mcoarsen.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function takes a graph and creates a sequence of coarser graphs
+**************************************************************************/
+GraphType *MCCoarsen2Way(CtrlType *ctrl, GraphType *graph)
+{
+  int i, clevel;
+  GraphType *cgraph;
+
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->CoarsenTmr));
+
+  cgraph = graph;
+
+  clevel = 0;
+  do {
+    if (ctrl->dbglvl&DBG_COARSEN) {
+      printf("%6d %7d %10d [%d] [%6.4f", cgraph->nvtxs, cgraph->nedges, 
+              idxsum(cgraph->nvtxs, cgraph->adjwgtsum), ctrl->CoarsenTo, ctrl->nmaxvwgt);
+      for (i=0; i<graph->ncon; i++)
+        printf(" %5.3f", ssum_strd(cgraph->nvtxs, cgraph->nvwgt+i, cgraph->ncon));
+      printf("]\n");
+    }
+
+    switch (ctrl->CType) {
+      case MATCH_RM:
+        MCMatch_RM(ctrl, cgraph);
+        break;
+      case MATCH_HEM:
+        if (clevel < 1)
+          MCMatch_RM(ctrl, cgraph);
+        else
+          MCMatch_HEM(ctrl, cgraph);
+        break;
+      case MATCH_SHEM:
+        if (clevel < 1)
+          MCMatch_RM(ctrl, cgraph);
+        else
+          MCMatch_SHEM(ctrl, cgraph);
+        break;
+      case MATCH_SHEMKWAY:
+        MCMatch_SHEM(ctrl, cgraph);
+        break;
+      case MATCH_SHEBM_ONENORM:
+        MCMatch_SHEBM(ctrl, cgraph, 1);
+        break;
+      case MATCH_SHEBM_INFNORM:
+        MCMatch_SHEBM(ctrl, cgraph, -1);
+        break;
+      case MATCH_SBHEM_ONENORM:
+        MCMatch_SBHEM(ctrl, cgraph, 1);
+        break;
+      case MATCH_SBHEM_INFNORM:
+        MCMatch_SBHEM(ctrl, cgraph, -1);
+        break;
+      default:
+        errexit("Unknown CType: %d\n", ctrl->CType);
+    }
+
+    cgraph = cgraph->coarser;
+    clevel++;
+
+  } while (cgraph->nvtxs > ctrl->CoarsenTo && cgraph->nvtxs < COARSEN_FRACTION2*cgraph->finer->nvtxs && cgraph->nedges > cgraph->nvtxs/2); 
+
+  if (ctrl->dbglvl&DBG_COARSEN) {
+    printf("%6d %7d %10d [%d] [%6.4f", cgraph->nvtxs, cgraph->nedges, 
+            idxsum(cgraph->nvtxs, cgraph->adjwgtsum), ctrl->CoarsenTo, ctrl->nmaxvwgt);
+    for (i=0; i<graph->ncon; i++)
+      printf(" %5.3f", ssum_strd(cgraph->nvtxs, cgraph->nvwgt+i, cgraph->ncon));
+    printf("]\n");
+  }
+
+
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->CoarsenTmr));
+
+  return cgraph;
+}
+
diff --git a/contrib/Metis/memory.c b/contrib/Metis/memory.c
new file mode 100644
index 0000000000..5461105338
--- /dev/null
+++ b/contrib/Metis/memory.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * memory.c
+ *
+ * This file contains routines that deal with memory allocation
+ *
+ * Started 2/24/96
+ * George
+ *
+ * $Id: memory.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function allocates memory for the workspace
+**************************************************************************/
+void AllocateWorkSpace(CtrlType *ctrl, GraphType *graph, int nparts)
+{
+  ctrl->wspace.pmat = NULL;
+
+  if (ctrl->optype == OP_KMETIS) {
+    ctrl->wspace.edegrees = (EDegreeType *)GKmalloc(graph->nedges*sizeof(EDegreeType), "AllocateWorkSpace: edegrees");
+    ctrl->wspace.vedegrees = NULL;
+    ctrl->wspace.auxcore = (idxtype *)ctrl->wspace.edegrees;
+
+    ctrl->wspace.pmat = idxmalloc(nparts*nparts, "AllocateWorkSpace: pmat");
+
+    /* Memory requirements for different phases
+          Coarsening
+                    Matching: 4*nvtxs vectors
+                 Contraction: 2*nvtxs vectors (from the above 4), 1*nparts, 1*Nedges
+            Total = MAX(4*nvtxs, 2*nvtxs+nparts+nedges)
+
+          Refinement
+                Random Refinement/Balance: 5*nparts + 1*nvtxs + 2*nedges
+                Greedy Refinement/Balance: 5*nparts + 2*nvtxs + 2*nedges + 1*PQueue(==Nvtxs)
+            Total = 5*nparts + 3*nvtxs + 2*nedges
+
+         Total = 5*nparts + 3*nvtxs + 2*nedges 
+    */
+    ctrl->wspace.maxcore = 3*(graph->nvtxs+1) +                 /* Match/Refinement vectors */
+                           5*(nparts+1) +                       /* Partition weights etc */
+                           graph->nvtxs*(sizeof(ListNodeType)/sizeof(idxtype)) + /* Greedy k-way balance/refine */
+                           20  /* padding for 64 bit machines */
+                           ;
+  }
+  else if (ctrl->optype == OP_KVMETIS) {
+    ctrl->wspace.edegrees = NULL;
+    ctrl->wspace.vedegrees = (VEDegreeType *)GKmalloc(graph->nedges*sizeof(VEDegreeType), "AllocateWorkSpace: vedegrees");
+    ctrl->wspace.auxcore = (idxtype *)ctrl->wspace.vedegrees;
+
+    ctrl->wspace.pmat = idxmalloc(nparts*nparts, "AllocateWorkSpace: pmat");
+
+    /* Memory requirements for different phases are identical to KMETIS */
+    ctrl->wspace.maxcore = 3*(graph->nvtxs+1) +                 /* Match/Refinement vectors */
+                           3*(nparts+1) +                       /* Partition weights etc */
+                           graph->nvtxs*(sizeof(ListNodeType)/sizeof(idxtype)) + /* Greedy k-way balance/refine */
+                           20  /* padding for 64 bit machines */
+                           ;
+  }
+  else {
+    ctrl->wspace.edegrees = (EDegreeType *)idxmalloc(graph->nedges, "AllocateWorkSpace: edegrees");
+    ctrl->wspace.vedegrees = NULL;
+    ctrl->wspace.auxcore = (idxtype *)ctrl->wspace.edegrees;
+
+    ctrl->wspace.maxcore = 5*(graph->nvtxs+1) +                 /* Refinement vectors */
+                           4*(nparts+1) +                       /* Partition weights etc */
+                           2*graph->ncon*graph->nvtxs*(sizeof(ListNodeType)/sizeof(idxtype)) + /* 2-way refinement */
+                           2*graph->ncon*(NEG_GAINSPAN+PLUS_GAINSPAN+1)*(sizeof(ListNodeType *)/sizeof(idxtype)) + /* 2-way refinement */
+                           20  /* padding for 64 bit machines */
+                           ;
+  }
+
+  ctrl->wspace.maxcore += HTLENGTH;
+  ctrl->wspace.core = idxmalloc(ctrl->wspace.maxcore, "AllocateWorkSpace: maxcore");
+  ctrl->wspace.ccore = 0;
+}
+
+
+/*************************************************************************
+* This function allocates memory for the workspace
+**************************************************************************/
+void FreeWorkSpace(CtrlType *ctrl, GraphType *graph)
+{
+  GKfree(&ctrl->wspace.edegrees, &ctrl->wspace.vedegrees, &ctrl->wspace.core, &ctrl->wspace.pmat, LTERM);
+}
+
+/*************************************************************************
+* This function returns how may words are left in the workspace
+**************************************************************************/
+int WspaceAvail(CtrlType *ctrl)
+{
+  return ctrl->wspace.maxcore - ctrl->wspace.ccore;
+}
+
+
+/*************************************************************************
+* This function allocate space from the core 
+**************************************************************************/
+idxtype *idxwspacemalloc(CtrlType *ctrl, int n)
+{
+  n += n%2; /* This is a fix for 64 bit machines that require 8-byte pointer allignment */
+
+  ctrl->wspace.ccore += n;
+  ASSERT(ctrl->wspace.ccore <= ctrl->wspace.maxcore);
+  return ctrl->wspace.core + ctrl->wspace.ccore - n;
+}
+
+/*************************************************************************
+* This function frees space from the core 
+**************************************************************************/
+void idxwspacefree(CtrlType *ctrl, int n)
+{
+  n += n%2; /* This is a fix for 64 bit machines that require 8-byte pointer allignment */
+
+  ctrl->wspace.ccore -= n;
+  ASSERT(ctrl->wspace.ccore >= 0);
+}
+
+
+/*************************************************************************
+* This function allocate space from the core 
+**************************************************************************/
+float *fwspacemalloc(CtrlType *ctrl, int n)
+{
+  n += n%2; /* This is a fix for 64 bit machines that require 8-byte pointer allignment */
+
+  ctrl->wspace.ccore += n;
+  ASSERT(ctrl->wspace.ccore <= ctrl->wspace.maxcore);
+  return (float *) (ctrl->wspace.core + ctrl->wspace.ccore - n);
+}
+
+/*************************************************************************
+* This function frees space from the core 
+**************************************************************************/
+void fwspacefree(CtrlType *ctrl, int n)
+{
+  n += n%2; /* This is a fix for 64 bit machines that require 8-byte pointer allignment */
+
+  ctrl->wspace.ccore -= n;
+  ASSERT(ctrl->wspace.ccore >= 0);
+}
+
+
+
+/*************************************************************************
+* This function creates a CoarseGraphType data structure and initializes
+* the various fields
+**************************************************************************/
+GraphType *CreateGraph(void)
+{
+  GraphType *graph;
+
+  graph = (GraphType *)GKmalloc(sizeof(GraphType), "CreateCoarseGraph: graph");
+
+  InitGraph(graph);
+
+  return graph;
+}
+
+
+/*************************************************************************
+* This function creates a CoarseGraphType data structure and initializes
+* the various fields
+**************************************************************************/
+void InitGraph(GraphType *graph) 
+{
+  graph->gdata = graph->rdata = NULL;
+
+  graph->nvtxs = graph->nedges = -1;
+  graph->mincut = graph->minvol = -1;
+
+  graph->xadj = graph->vwgt = graph->adjncy = graph->adjwgt = NULL;
+  graph->adjwgtsum = NULL;
+  graph->label = NULL;
+  graph->cmap = NULL;
+
+  graph->where = graph->pwgts = NULL;
+  graph->id = graph->ed = NULL;
+  graph->bndptr = graph->bndind = NULL;
+  graph->rinfo = NULL;
+  graph->vrinfo = NULL;
+  graph->nrinfo = NULL;
+
+  graph->ncon = -1;
+  graph->nvwgt = NULL;
+  graph->npwgts = NULL;
+
+  graph->vsize = NULL;
+
+  graph->coarser = graph->finer = NULL;
+
+}
+
+/*************************************************************************
+* This function deallocates any memory stored in a graph
+**************************************************************************/
+void FreeGraph(GraphType *graph) 
+{
+
+  GKfree(&graph->gdata, &graph->nvwgt, &graph->rdata, &graph->npwgts, LTERM);
+  free(graph);
+}
+
diff --git a/contrib/Metis/mesh.c b/contrib/Metis/mesh.c
new file mode 100644
index 0000000000..607792a9a1
--- /dev/null
+++ b/contrib/Metis/mesh.c
@@ -0,0 +1,398 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * mesh.c
+ *
+ * This file contains routines for converting 3D and 4D finite element
+ * meshes into dual or nodal graphs
+ *
+ * Started 8/18/97
+ * George
+ *
+ * $Id: mesh.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+/*****************************************************************************
+* This function creates a graph corresponding to the dual of a finite element
+* mesh. At this point the supported elements are triangles, tetrahedrons, and
+* bricks.
+******************************************************************************/
+void METIS_MeshToDual(int *ne, int *nn, idxtype *elmnts, int *etype, int *numflag, 
+                      idxtype *dxadj, idxtype *dadjncy)
+{
+  int esizes[] = {-1, 3, 4, 8, 4};
+
+  if (*numflag == 1)
+    ChangeMesh2CNumbering((*ne)*esizes[*etype], elmnts);
+
+  GENDUALMETIS(*ne, *nn, *etype, elmnts, dxadj, dadjncy);
+
+  if (*numflag == 1)
+    ChangeMesh2FNumbering((*ne)*esizes[*etype], elmnts, *ne, dxadj, dadjncy);
+}
+
+
+/*****************************************************************************
+* This function creates a graph corresponding to the finite element mesh. 
+* At this point the supported elements are triangles, tetrahedrons.
+******************************************************************************/
+void METIS_MeshToNodal(int *ne, int *nn, idxtype *elmnts, int *etype, int *numflag, 
+                       idxtype *dxadj, idxtype *dadjncy)
+{
+  int esizes[] = {-1, 3, 4, 8, 4};
+
+  if (*numflag == 1)
+    ChangeMesh2CNumbering((*ne)*esizes[*etype], elmnts);
+
+  switch (*etype) {
+    case 1:
+      TRINODALMETIS(*ne, *nn, elmnts, dxadj, dadjncy);
+      break;
+    case 2:
+      TETNODALMETIS(*ne, *nn, elmnts, dxadj, dadjncy);
+      break;
+    case 3:
+      HEXNODALMETIS(*ne, *nn, elmnts, dxadj, dadjncy);
+      break;
+    case 4:
+      QUADNODALMETIS(*ne, *nn, elmnts, dxadj, dadjncy);
+      break;
+  }
+
+  if (*numflag == 1)
+    ChangeMesh2FNumbering((*ne)*esizes[*etype], elmnts, *nn, dxadj, dadjncy);
+}
+
+
+
+/*****************************************************************************
+* This function creates the dual of a finite element mesh
+******************************************************************************/
+void GENDUALMETIS(int nelmnts, int nvtxs, int etype, idxtype *elmnts, idxtype *dxadj, idxtype *dadjncy)
+{
+   int i, j, jj, k, kk, kkk, l, m, n, nedges, mask;
+   idxtype *nptr, *nind;
+   idxtype *mark, ind[200], wgt[200];
+   int esize, esizes[] = {-1, 3, 4, 8, 4},
+       mgcnum, mgcnums[] = {-1, 2, 3, 4, 2};
+
+   mask = (1<<11)-1;
+   mark = idxsmalloc(mask+1, -1, "GENDUALMETIS: mark");
+
+   /* Get the element size and magic number for the particular element */
+   esize = esizes[etype];
+   mgcnum = mgcnums[etype];
+
+   /* Construct the node-element list first */
+   nptr = idxsmalloc(nvtxs+1, 0, "GENDUALMETIS: nptr");
+   for (j=esize*nelmnts, i=0; i<j; i++) 
+     nptr[elmnts[i]]++;
+   MAKECSR(i, nvtxs, nptr);
+
+   nind = idxmalloc(nptr[nvtxs], "GENDUALMETIS: nind");
+   for (k=i=0; i<nelmnts; i++) {
+     for (j=0; j<esize; j++, k++) 
+       nind[nptr[elmnts[k]]++] = i;
+   }
+   for (i=nvtxs; i>0; i--)
+     nptr[i] = nptr[i-1];
+   nptr[0] = 0;
+
+   for (i=0; i<nelmnts; i++) 
+     dxadj[i] = esize*i;
+
+   for (i=0; i<nelmnts; i++) {
+     for (m=j=0; j<esize; j++) {
+       n = elmnts[esize*i+j];
+       for (k=nptr[n+1]-1; k>=nptr[n]; k--) {
+         if ((kk = nind[k]) <= i)
+           break;
+
+         kkk = kk&mask;
+         if ((l = mark[kkk]) == -1) {
+           ind[m] = kk;
+           wgt[m] = 1;
+           mark[kkk] = m++;
+         }
+         else if (ind[l] == kk) {
+           wgt[l]++;
+         }
+         else {
+           for (jj=0; jj<m; jj++) {
+             if (ind[jj] == kk) {
+               wgt[jj]++;
+               break;
+             }
+           }
+           if (jj == m) {
+             ind[m] = kk;
+             wgt[m++] = 1;
+           }
+         }
+       }
+     }
+     for (j=0; j<m; j++) {
+       if (wgt[j] == mgcnum) {
+         k = ind[j];
+         dadjncy[dxadj[i]++] = k;
+         dadjncy[dxadj[k]++] = i;
+       }
+       mark[ind[j]&mask] = -1;
+     }
+   }
+
+   /* Go and consolidate the dxadj and dadjncy */
+   for (j=i=0; i<nelmnts; i++) {
+     for (k=esize*i; k<dxadj[i]; k++, j++)
+       dadjncy[j] = dadjncy[k];
+     dxadj[i] = j;
+   }
+   for (i=nelmnts; i>0; i--)
+     dxadj[i] = dxadj[i-1];
+   dxadj[0] = 0;
+
+   free(mark);
+   free(nptr);
+   free(nind);
+
+}
+
+
+
+
+/*****************************************************************************
+* This function creates the nodal graph of a finite element mesh
+******************************************************************************/
+void TRINODALMETIS(int nelmnts, int nvtxs, idxtype *elmnts, idxtype *dxadj, idxtype *dadjncy)
+{
+   int i, j, jj, k, kk, kkk, l, m, n, nedges;
+   idxtype *nptr, *nind;
+   idxtype *mark;
+
+   /* Construct the node-element list first */
+   nptr = idxsmalloc(nvtxs+1, 0, "TRINODALMETIS: nptr");
+   for (j=3*nelmnts, i=0; i<j; i++) 
+     nptr[elmnts[i]]++;
+   MAKECSR(i, nvtxs, nptr);
+
+   nind = idxmalloc(nptr[nvtxs], "TRINODALMETIS: nind");
+   for (k=i=0; i<nelmnts; i++) {
+     for (j=0; j<3; j++, k++) 
+       nind[nptr[elmnts[k]]++] = i;
+   }
+   for (i=nvtxs; i>0; i--)
+     nptr[i] = nptr[i-1];
+   nptr[0] = 0;
+
+
+   mark = idxsmalloc(nvtxs, -1, "TRINODALMETIS: mark");
+
+   nedges = dxadj[0] = 0;
+   for (i=0; i<nvtxs; i++) {
+     mark[i] = i;
+     for (j=nptr[i]; j<nptr[i+1]; j++) {
+       for (jj=3*nind[j], k=0; k<3; k++, jj++) {
+         kk = elmnts[jj];
+         if (mark[kk] != i) {
+           mark[kk] = i;
+           dadjncy[nedges++] = kk;
+         }
+       }
+     }
+     dxadj[i+1] = nedges;
+   }
+
+   free(mark);
+   free(nptr);
+   free(nind);
+
+}
+
+
+/*****************************************************************************
+* This function creates the nodal graph of a finite element mesh
+******************************************************************************/
+void TETNODALMETIS(int nelmnts, int nvtxs, idxtype *elmnts, idxtype *dxadj, idxtype *dadjncy)
+{
+   int i, j, jj, k, kk, kkk, l, m, n, nedges;
+   idxtype *nptr, *nind;
+   idxtype *mark;
+
+   /* Construct the node-element list first */
+   nptr = idxsmalloc(nvtxs+1, 0, "TETNODALMETIS: nptr");
+   for (j=4*nelmnts, i=0; i<j; i++) 
+     nptr[elmnts[i]]++;
+   MAKECSR(i, nvtxs, nptr);
+
+   nind = idxmalloc(nptr[nvtxs], "TETNODALMETIS: nind");
+   for (k=i=0; i<nelmnts; i++) {
+     for (j=0; j<4; j++, k++) 
+       nind[nptr[elmnts[k]]++] = i;
+   }
+   for (i=nvtxs; i>0; i--)
+     nptr[i] = nptr[i-1];
+   nptr[0] = 0;
+
+
+   mark = idxsmalloc(nvtxs, -1, "TETNODALMETIS: mark");
+
+   nedges = dxadj[0] = 0;
+   for (i=0; i<nvtxs; i++) {
+     mark[i] = i;
+     for (j=nptr[i]; j<nptr[i+1]; j++) {
+       for (jj=4*nind[j], k=0; k<4; k++, jj++) {
+         kk = elmnts[jj];
+         if (mark[kk] != i) {
+           mark[kk] = i;
+           dadjncy[nedges++] = kk;
+         }
+       }
+     }
+     dxadj[i+1] = nedges;
+   }
+
+   free(mark);
+   free(nptr);
+   free(nind);
+
+}
+
+
+/*****************************************************************************
+* This function creates the nodal graph of a finite element mesh
+******************************************************************************/
+void HEXNODALMETIS(int nelmnts, int nvtxs, idxtype *elmnts, idxtype *dxadj, idxtype *dadjncy)
+{
+   int i, j, jj, k, kk, kkk, l, m, n, nedges;
+   idxtype *nptr, *nind;
+   idxtype *mark;
+   int table[8][3] = {1, 3, 4,
+                      0, 2, 5,
+                      1, 3, 6,
+                      0, 2, 7,
+                      0, 5, 7,
+                      1, 4, 6,
+                      2, 5, 7,
+                      3, 4, 6};
+
+   /* Construct the node-element list first */
+   nptr = idxsmalloc(nvtxs+1, 0, "HEXNODALMETIS: nptr");
+   for (j=8*nelmnts, i=0; i<j; i++) 
+     nptr[elmnts[i]]++;
+   MAKECSR(i, nvtxs, nptr);
+
+   nind = idxmalloc(nptr[nvtxs], "HEXNODALMETIS: nind");
+   for (k=i=0; i<nelmnts; i++) {
+     for (j=0; j<8; j++, k++) 
+       nind[nptr[elmnts[k]]++] = i;
+   }
+   for (i=nvtxs; i>0; i--)
+     nptr[i] = nptr[i-1];
+   nptr[0] = 0;
+
+
+   mark = idxsmalloc(nvtxs, -1, "HEXNODALMETIS: mark");
+
+   nedges = dxadj[0] = 0;
+   for (i=0; i<nvtxs; i++) {
+     mark[i] = i;
+     for (j=nptr[i]; j<nptr[i+1]; j++) {
+       jj=8*nind[j];
+       for (k=0; k<8; k++) {
+         if (elmnts[jj+k] == i)
+           break;
+       }
+       ASSERT(k != 8);
+
+       /* You found the index, now go and put the 3 neighbors */
+       kk = elmnts[jj+table[k][0]];
+       if (mark[kk] != i) {
+         mark[kk] = i;
+         dadjncy[nedges++] = kk;
+       }
+       kk = elmnts[jj+table[k][1]];
+       if (mark[kk] != i) {
+         mark[kk] = i;
+         dadjncy[nedges++] = kk;
+       }
+       kk = elmnts[jj+table[k][2]];
+       if (mark[kk] != i) {
+         mark[kk] = i;
+         dadjncy[nedges++] = kk;
+       }
+     }
+     dxadj[i+1] = nedges;
+   }
+
+   free(mark);
+   free(nptr);
+   free(nind);
+
+}
+
+
+/*****************************************************************************
+* This function creates the nodal graph of a finite element mesh
+******************************************************************************/
+void QUADNODALMETIS(int nelmnts, int nvtxs, idxtype *elmnts, idxtype *dxadj, idxtype *dadjncy)
+{
+   int i, j, jj, k, kk, kkk, l, m, n, nedges;
+   idxtype *nptr, *nind;
+   idxtype *mark;
+   int table[4][2] = {1, 3, 
+                      0, 2,
+                      1, 3, 
+                      0, 2}; 
+
+   /* Construct the node-element list first */
+   nptr = idxsmalloc(nvtxs+1, 0, "QUADNODALMETIS: nptr");
+   for (j=4*nelmnts, i=0; i<j; i++) 
+     nptr[elmnts[i]]++;
+   MAKECSR(i, nvtxs, nptr);
+
+   nind = idxmalloc(nptr[nvtxs], "QUADNODALMETIS: nind");
+   for (k=i=0; i<nelmnts; i++) {
+     for (j=0; j<4; j++, k++) 
+       nind[nptr[elmnts[k]]++] = i;
+   }
+   for (i=nvtxs; i>0; i--)
+     nptr[i] = nptr[i-1];
+   nptr[0] = 0;
+
+
+   mark = idxsmalloc(nvtxs, -1, "QUADNODALMETIS: mark");
+
+   nedges = dxadj[0] = 0;
+   for (i=0; i<nvtxs; i++) {
+     mark[i] = i;
+     for (j=nptr[i]; j<nptr[i+1]; j++) {
+       jj=4*nind[j];
+       for (k=0; k<4; k++) {
+         if (elmnts[jj+k] == i)
+           break;
+       }
+       ASSERT(k != 4);
+
+       /* You found the index, now go and put the 2 neighbors */
+       kk = elmnts[jj+table[k][0]];
+       if (mark[kk] != i) {
+         mark[kk] = i;
+         dadjncy[nedges++] = kk;
+       }
+       kk = elmnts[jj+table[k][1]];
+       if (mark[kk] != i) {
+         mark[kk] = i;
+         dadjncy[nedges++] = kk;
+       }
+     }
+     dxadj[i+1] = nedges;
+   }
+
+   free(mark);
+   free(nptr);
+   free(nind);
+
+}
diff --git a/contrib/Metis/meshpart.c b/contrib/Metis/meshpart.c
new file mode 100644
index 0000000000..e054e46c73
--- /dev/null
+++ b/contrib/Metis/meshpart.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * meshpart.c
+ *
+ * This file contains routines for partitioning finite element meshes.
+ *
+ * Started 9/29/97
+ * George
+ *
+ * $Id: meshpart.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function partitions a finite element mesh by partitioning its nodal
+* graph using KMETIS and then assigning elements in a load balanced fashion.
+**************************************************************************/
+void METIS_PartMeshNodal(int *ne, int *nn, idxtype *elmnts, int *etype, int *numflag, 
+                         int *nparts, int *edgecut, idxtype *epart, idxtype *npart)
+{
+  int i, j, k, me;
+  idxtype *xadj, *adjncy, *pwgts;
+  int options[10], pnumflag=0, wgtflag=0;
+  int nnbrs, nbrind[200], nbrwgt[200], maxpwgt;
+  int esize, esizes[] = {-1, 3, 4, 8, 4};
+
+  esize = esizes[*etype];
+
+  if (*numflag == 1)
+    ChangeMesh2CNumbering((*ne)*esize, elmnts);
+
+  xadj = idxmalloc(*nn+1, "METIS_MESHPARTNODAL: xadj");
+  adjncy = idxmalloc(20*(*nn), "METIS_MESHPARTNODAL: adjncy");
+
+  METIS_MeshToNodal(ne, nn, elmnts, etype, &pnumflag, xadj, adjncy);
+
+  adjncy = realloc(adjncy, xadj[*nn]*sizeof(idxtype));
+
+  options[0] = 0;
+  METIS_PartGraphKway(nn, xadj, adjncy, NULL, NULL, &wgtflag, &pnumflag, nparts, options, edgecut, npart);
+
+  /* OK, now compute an element partition based on the nodal partition npart */
+  idxset(*ne, -1, epart);
+  pwgts = idxsmalloc(*nparts, 0, "METIS_MESHPARTNODAL: pwgts");
+  for (i=0; i<*ne; i++) {
+    me = npart[elmnts[i*esize]];
+    for (j=1; j<esize; j++) {
+      if (npart[elmnts[i*esize+j]] != me)
+        break;
+    }
+    if (j == esize) {
+      epart[i] = me;
+      pwgts[me]++;
+    }
+  }
+
+  maxpwgt = 1.03*(*ne)/(*nparts);
+  for (i=0; i<*ne; i++) {
+    if (epart[i] == -1) { /* Assign the boundary element */
+      nnbrs = 0;
+      for (j=0; j<esize; j++) {
+        me = npart[elmnts[i*esize+j]];
+        for (k=0; k<nnbrs; k++) {
+          if (nbrind[k] == me) {
+            nbrwgt[k]++;
+            break;
+          }
+        }
+        if (k == nnbrs) {
+          nbrind[nnbrs] = me;
+          nbrwgt[nnbrs++] = 1;
+        }
+      }
+      /* Try to assign it first to the domain with most things in common */
+      j = iamax(nnbrs, nbrwgt);
+      if (pwgts[nbrind[j]] < maxpwgt) {
+        epart[i] = nbrind[j];
+      }
+      else {
+        /* If that fails, assign it to a light domain */
+        for (j=0; j<nnbrs; j++) {
+          if (pwgts[nbrind[j]] < maxpwgt) {
+            epart[i] = nbrind[j];
+            break;
+          }
+        }
+        if (j == nnbrs) 
+          epart[i] = nbrind[iamax(nnbrs, nbrwgt)];
+      }
+      pwgts[epart[i]]++;
+    }
+  }
+
+  if (*numflag == 1)
+    ChangeMesh2FNumbering2((*ne)*esize, elmnts, *ne, *nn, epart, npart);
+
+  GKfree(&xadj, &adjncy, &pwgts, LTERM);
+
+}
+
+
+/*************************************************************************
+* This function partitions a finite element mesh by partitioning its dual
+* graph using KMETIS and then assigning nodes in a load balanced fashion.
+**************************************************************************/
+void METIS_PartMeshDual(int *ne, int *nn, idxtype *elmnts, int *etype, int *numflag, 
+                        int *nparts, int *edgecut, idxtype *epart, idxtype *npart)
+{
+  int i, j, k, me;
+  idxtype *xadj, *adjncy, *pwgts, *nptr, *nind;
+  int options[10], pnumflag=0, wgtflag=0;
+  int nnbrs, nbrind[200], nbrwgt[200], maxpwgt;
+  int esize, esizes[] = {-1, 3, 4, 8, 4};
+
+  esize = esizes[*etype];
+
+  if (*numflag == 1)
+    ChangeMesh2CNumbering((*ne)*esize, elmnts);
+
+  xadj = idxmalloc(*ne+1, "METIS_MESHPARTNODAL: xadj");
+  adjncy = idxmalloc(esize*(*ne), "METIS_MESHPARTNODAL: adjncy");
+
+  METIS_MeshToDual(ne, nn, elmnts, etype, &pnumflag, xadj, adjncy);
+
+  options[0] = 0;
+  METIS_PartGraphKway(ne, xadj, adjncy, NULL, NULL, &wgtflag, &pnumflag, nparts, options, edgecut, epart);
+
+  /* Construct the node-element list */
+  nptr = idxsmalloc(*nn+1, 0, "METIS_MESHPARTDUAL: nptr");
+  for (j=esize*(*ne), i=0; i<j; i++) 
+    nptr[elmnts[i]]++;
+  MAKECSR(i, *nn, nptr);
+
+  nind = idxmalloc(nptr[*nn], "METIS_MESHPARTDUAL: nind");
+  for (k=i=0; i<(*ne); i++) {
+    for (j=0; j<esize; j++, k++) 
+      nind[nptr[elmnts[k]]++] = i;
+  }
+  for (i=(*nn); i>0; i--)
+    nptr[i] = nptr[i-1];
+  nptr[0] = 0;
+
+
+  /* OK, now compute a nodal partition based on the element partition npart */
+  idxset(*nn, -1, npart);
+  pwgts = idxsmalloc(*nparts, 0, "METIS_MESHPARTDUAL: pwgts");
+  for (i=0; i<*nn; i++) {
+    me = epart[nind[nptr[i]]];
+    for (j=nptr[i]+1; j<nptr[i+1]; j++) {
+      if (epart[nind[j]] != me)
+        break;
+    }
+    if (j == nptr[i+1]) {
+      npart[i] = me;
+      pwgts[me]++;
+    }
+  }
+
+  maxpwgt = 1.03*(*nn)/(*nparts);
+  for (i=0; i<*nn; i++) {
+    if (npart[i] == -1) { /* Assign the boundary element */
+      nnbrs = 0;
+      for (j=nptr[i]; j<nptr[i+1]; j++) {
+        me = epart[nind[j]];
+        for (k=0; k<nnbrs; k++) {
+          if (nbrind[k] == me) {
+            nbrwgt[k]++;
+            break;
+          }
+        }
+        if (k == nnbrs) {
+          nbrind[nnbrs] = me;
+          nbrwgt[nnbrs++] = 1;
+        }
+      }
+      /* Try to assign it first to the domain with most things in common */
+      j = iamax(nnbrs, nbrwgt);
+      if (pwgts[nbrind[j]] < maxpwgt) {
+        npart[i] = nbrind[j];
+      }
+      else {
+        /* If that fails, assign it to a light domain */
+        npart[i] = nbrind[0];
+        for (j=0; j<nnbrs; j++) {
+          if (pwgts[nbrind[j]] < maxpwgt) {
+            npart[i] = nbrind[j];
+            break;
+          }
+        }
+      }
+      pwgts[npart[i]]++;
+    }
+  }
+
+  if (*numflag == 1)
+    ChangeMesh2FNumbering2((*ne)*esize, elmnts, *ne, *nn, epart, npart);
+
+  GKfree(&xadj, &adjncy, &pwgts, &nptr, &nind, LTERM);
+
+}
diff --git a/contrib/Metis/metis.h b/contrib/Metis/metis.h
new file mode 100644
index 0000000000..ff4d5ac96b
--- /dev/null
+++ b/contrib/Metis/metis.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * metis.h
+ *
+ * This file includes all necessary header files
+ *
+ * Started 8/27/94
+ * George
+ *
+ * $Id: metis.h,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ */
+
+
+#include <stdio.h>
+#ifdef __STDC__
+#include <stdlib.h>
+#else
+#include <malloc.h>
+#endif
+#include <strings.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include <stdarg.h>
+#include <time.h>
+
+#ifdef DMALLOC
+#include <dmalloc.h>
+#endif
+
+#include <defs.h>
+#include <struct.h>
+#include <macros.h>
+#include <rename.h>
+#include <proto.h>
+
diff --git a/contrib/Metis/mfm.c b/contrib/Metis/mfm.c
new file mode 100644
index 0000000000..baf24795d3
--- /dev/null
+++ b/contrib/Metis/mfm.c
@@ -0,0 +1,344 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * mfm.c
+ *
+ * This file contains code that implements the edge-based FM refinement
+ *
+ * Started 7/23/97
+ * George
+ *
+ * $Id: mfm.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function performs an edge-based FM refinement
+**************************************************************************/
+void MocFM_2WayEdgeRefine(CtrlType *ctrl, GraphType *graph, float *tpwgts, int npasses)
+{
+  int i, ii, j, k, l, kwgt, nvtxs, ncon, nbnd, nswaps, from, to, pass, me, limit, tmp, cnum;
+  idxtype *xadj, *adjncy, *adjwgt, *where, *id, *ed, *bndptr, *bndind;
+  idxtype *moved, *swaps, *perm, *qnum;
+  float *nvwgt, *npwgts, mindiff[MAXNCON], origbal, minbal, newbal;
+  PQueueType parts[MAXNCON][2];
+  int higain, oldgain, mincut, initcut, newcut, mincutorder;
+  float rtpwgts[2];
+
+  nvtxs = graph->nvtxs;
+  ncon = graph->ncon;
+  xadj = graph->xadj;
+  nvwgt = graph->nvwgt;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+  where = graph->where;
+  id = graph->id;
+  ed = graph->ed;
+  npwgts = graph->npwgts;
+  bndptr = graph->bndptr;
+  bndind = graph->bndind;
+
+  moved = idxwspacemalloc(ctrl, nvtxs);
+  swaps = idxwspacemalloc(ctrl, nvtxs);
+  perm = idxwspacemalloc(ctrl, nvtxs);
+  qnum = idxwspacemalloc(ctrl, nvtxs);
+
+  limit = amin(amax(0.01*nvtxs, 25), 150);
+
+  /* Initialize the queues */
+  for (i=0; i<ncon; i++) {
+    PQueueInit(ctrl, &parts[i][0], nvtxs, PLUS_GAINSPAN+1);
+    PQueueInit(ctrl, &parts[i][1], nvtxs, PLUS_GAINSPAN+1);
+  }
+  for (i=0; i<nvtxs; i++)
+    qnum[i] = samax(ncon, nvwgt+i*ncon);
+
+  origbal = Compute2WayHLoadImbalance(ncon, npwgts, tpwgts);
+
+  rtpwgts[0] = origbal*tpwgts[0];
+  rtpwgts[1] = origbal*tpwgts[1];
+
+
+  if (ctrl->dbglvl&DBG_REFINE) {
+    printf("Parts: [");
+    for (l=0; l<ncon; l++)
+      printf("(%.3f, %.3f) ", npwgts[l], npwgts[ncon+l]);
+    printf("] T[%.3f %.3f], Nv-Nb[%5d, %5d]. ICut: %6d, LB: %.3f\n", tpwgts[0], tpwgts[1], graph->nvtxs, graph->nbnd, graph->mincut, origbal);
+  }
+
+  idxset(nvtxs, -1, moved);
+  for (pass=0; pass<npasses; pass++) { /* Do a number of passes */
+    for (i=0; i<ncon; i++) { 
+      PQueueReset(&parts[i][0]);
+      PQueueReset(&parts[i][1]);
+    }
+
+    mincutorder = -1;
+    newcut = mincut = initcut = graph->mincut;
+    for (i=0; i<ncon; i++)
+      mindiff[i] = fabs(tpwgts[0]-npwgts[i]);
+    minbal = Compute2WayHLoadImbalance(ncon, npwgts, tpwgts);
+
+    ASSERT(ComputeCut(graph, where) == graph->mincut);
+    ASSERT(CheckBnd(graph));
+
+    /* Insert boundary nodes in the priority queues */
+    nbnd = graph->nbnd;
+    RandomPermute(nbnd, perm, 1);
+    for (ii=0; ii<nbnd; ii++) {
+      i = bndind[perm[ii]];
+      ASSERT(ed[i] > 0 || id[i] == 0);
+      ASSERT(bndptr[i] != -1);
+      PQueueInsert(&parts[qnum[i]][where[i]], i, ed[i]-id[i]);
+    }
+
+    for (nswaps=0; nswaps<nvtxs; nswaps++) {
+      SelectQueue(ncon, npwgts, rtpwgts, &from, &cnum, parts);
+      to = (from+1)%2;
+
+      if (from == -1 || (higain = PQueueGetMax(&parts[cnum][from])) == -1)
+        break;
+      ASSERT(bndptr[higain] != -1);
+
+      saxpy(ncon, 1.0, nvwgt+higain*ncon, 1, npwgts+to*ncon, 1);
+      saxpy(ncon, -1.0, nvwgt+higain*ncon, 1, npwgts+from*ncon, 1);
+
+      newcut -= (ed[higain]-id[higain]);
+      newbal = Compute2WayHLoadImbalance(ncon, npwgts, tpwgts);
+
+      if ((newcut < mincut && newbal-origbal <= .00001) || 
+          (newcut == mincut && (newbal < minbal || 
+                                (newbal == minbal && BetterBalance(ncon, npwgts, tpwgts, mindiff))))) {
+        mincut = newcut;
+        minbal = newbal;
+        mincutorder = nswaps;
+        for (i=0; i<ncon; i++)
+          mindiff[i] = fabs(tpwgts[0]-npwgts[i]);
+      }
+      else if (nswaps-mincutorder > limit) { /* We hit the limit, undo last move */
+        newcut += (ed[higain]-id[higain]);
+        saxpy(ncon, 1.0, nvwgt+higain*ncon, 1, npwgts+from*ncon, 1);
+        saxpy(ncon, -1.0, nvwgt+higain*ncon, 1, npwgts+to*ncon, 1);
+        break;
+      }
+
+      where[higain] = to;
+      moved[higain] = nswaps;
+      swaps[nswaps] = higain;
+
+      if (ctrl->dbglvl&DBG_MOVEINFO) {
+        printf("Moved %6d from %d(%d). Gain: %5d, Cut: %5d, NPwgts: ", higain, from, cnum, ed[higain]-id[higain], newcut);
+        for (l=0; l<ncon; l++) 
+          printf("(%.3f, %.3f) ", npwgts[l], npwgts[ncon+l]);
+        printf(", %.3f LB: %.3f\n", minbal, newbal);
+      }
+
+
+      /**************************************************************
+      * Update the id[i]/ed[i] values of the affected nodes
+      ***************************************************************/
+      SWAP(id[higain], ed[higain], tmp);
+      if (ed[higain] == 0 && xadj[higain] < xadj[higain+1]) 
+        BNDDelete(nbnd, bndind,  bndptr, higain);
+
+      for (j=xadj[higain]; j<xadj[higain+1]; j++) {
+        k = adjncy[j];
+        oldgain = ed[k]-id[k];
+
+        kwgt = (to == where[k] ? adjwgt[j] : -adjwgt[j]);
+        INC_DEC(id[k], ed[k], kwgt);
+
+        /* Update its boundary information and queue position */
+        if (bndptr[k] != -1) { /* If k was a boundary vertex */
+          if (ed[k] == 0) { /* Not a boundary vertex any more */
+            BNDDelete(nbnd, bndind, bndptr, k);
+            if (moved[k] == -1)  /* Remove it if in the queues */
+              PQueueDelete(&parts[qnum[k]][where[k]], k, oldgain);
+          }
+          else { /* If it has not been moved, update its position in the queue */
+            if (moved[k] == -1)
+              PQueueUpdate(&parts[qnum[k]][where[k]], k, oldgain, ed[k]-id[k]);
+          }
+        }
+        else {
+          if (ed[k] > 0) {  /* It will now become a boundary vertex */
+            BNDInsert(nbnd, bndind, bndptr, k);
+            if (moved[k] == -1) 
+              PQueueInsert(&parts[qnum[k]][where[k]], k, ed[k]-id[k]);
+          }
+        }
+      }
+
+    }
+
+
+    /****************************************************************
+    * Roll back computations
+    *****************************************************************/
+    for (i=0; i<nswaps; i++)
+      moved[swaps[i]] = -1;  /* reset moved array */
+    for (nswaps--; nswaps>mincutorder; nswaps--) {
+      higain = swaps[nswaps];
+
+      to = where[higain] = (where[higain]+1)%2;
+      SWAP(id[higain], ed[higain], tmp);
+      if (ed[higain] == 0 && bndptr[higain] != -1 && xadj[higain] < xadj[higain+1])
+        BNDDelete(nbnd, bndind,  bndptr, higain);
+      else if (ed[higain] > 0 && bndptr[higain] == -1)
+        BNDInsert(nbnd, bndind,  bndptr, higain);
+
+      saxpy(ncon, 1.0, nvwgt+higain*ncon, 1, npwgts+to*ncon, 1);
+      saxpy(ncon, -1.0, nvwgt+higain*ncon, 1, npwgts+((to+1)%2)*ncon, 1);
+      for (j=xadj[higain]; j<xadj[higain+1]; j++) {
+        k = adjncy[j];
+
+        kwgt = (to == where[k] ? adjwgt[j] : -adjwgt[j]);
+        INC_DEC(id[k], ed[k], kwgt);
+
+        if (bndptr[k] != -1 && ed[k] == 0)
+          BNDDelete(nbnd, bndind, bndptr, k);
+        if (bndptr[k] == -1 && ed[k] > 0)
+          BNDInsert(nbnd, bndind, bndptr, k);
+      }
+    }
+
+    if (ctrl->dbglvl&DBG_REFINE) {
+      printf("\tMincut: %6d at %5d, NBND: %6d, NPwgts: [", mincut, mincutorder, nbnd);
+      for (l=0; l<ncon; l++)
+        printf("(%.3f, %.3f) ", npwgts[l], npwgts[ncon+l]);
+      printf("], LB: %.3f\n", Compute2WayHLoadImbalance(ncon, npwgts, tpwgts));
+    }
+
+    graph->mincut = mincut;
+    graph->nbnd = nbnd;
+
+    if (mincutorder == -1 || mincut == initcut)
+      break;
+  }
+
+  for (i=0; i<ncon; i++) {
+    PQueueFree(ctrl, &parts[i][0]);
+    PQueueFree(ctrl, &parts[i][1]);
+  }
+
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+
+}
+
+
+/*************************************************************************
+* This function selects the partition number and the queue from which
+* we will move vertices out
+**************************************************************************/ 
+void SelectQueue(int ncon, float *npwgts, float *tpwgts, int *from, int *cnum, PQueueType queues[MAXNCON][2])
+{
+  int i, part, maxgain=0;
+  float max, maxdiff=0.0;
+
+  *from = -1;
+  *cnum = -1;
+
+  /* First determine the side and the queue, irrespective of the presence of nodes */
+  for (part=0; part<2; part++) {
+    for (i=0; i<ncon; i++) {
+      if (npwgts[part*ncon+i]-tpwgts[part] >= maxdiff) {
+        maxdiff = npwgts[part*ncon+i]-tpwgts[part];
+        *from = part;
+        *cnum = i;
+      }
+    }
+  }
+
+  /* printf("Selected1 %d(%d) -> %d [%5f]\n", *from, *cnum, PQueueGetSize(&queues[*cnum][*from]), maxdiff); */
+
+  if (*from != -1 && PQueueGetSize(&queues[*cnum][*from]) == 0) {
+    /* The desired queue is empty, select a node from that side anyway */
+    for (i=0; i<ncon; i++) {
+      if (PQueueGetSize(&queues[i][*from]) > 0) {
+        max = npwgts[(*from)*ncon + i];
+        *cnum = i;
+        break;
+      }
+    }
+
+    for (i++; i<ncon; i++) {
+      if (npwgts[(*from)*ncon + i] > max && PQueueGetSize(&queues[i][*from]) > 0) {
+        max = npwgts[(*from)*ncon + i];
+        *cnum = i;
+      }
+    }
+  }
+
+  /* Check to see if you can focus on the cut */
+  if (maxdiff <= 0.0 || *from == -1) {
+    maxgain = -100000;
+
+    for (part=0; part<2; part++) {
+      for (i=0; i<ncon; i++) {
+        if (PQueueGetSize(&queues[i][part]) > 0 && PQueueGetKey(&queues[i][part]) > maxgain) {
+          maxgain = PQueueGetKey(&queues[i][part]); 
+          *from = part;
+          *cnum = i;
+        }
+      }
+    }
+  }
+
+  /* printf("Selected2 %d(%d) -> %d\n", *from, *cnum, PQueueGetSize(&queues[*cnum][*from])); */
+}
+
+
+
+
+
+/*************************************************************************
+* This function checks if the balance achieved is better than the diff 
+* For now, it uses a 2-norm measure
+**************************************************************************/ 
+int BetterBalance(int ncon, float *npwgts, float *tpwgts, float *diff)
+{
+  int i;
+  float ndiff[MAXNCON];
+
+  for (i=0; i<ncon; i++)
+    ndiff[i] = fabs(tpwgts[0]-npwgts[i]);
+   
+  return snorm2(ncon, ndiff) < snorm2(ncon, diff);
+}
+
+
+
+/*************************************************************************
+* This function computes the load imbalance over all the constrains
+**************************************************************************/ 
+float Compute2WayHLoadImbalance(int ncon, float *npwgts, float *tpwgts)
+{
+  int i;
+  float max=0.0, temp;
+
+  for (i=0; i<ncon; i++) {
+    /* temp = amax(npwgts[i]/tpwgts[0], npwgts[ncon+i]/tpwgts[1]); */
+    temp = fabs(tpwgts[0]-npwgts[i])/tpwgts[0];
+    max = (max < temp ? temp : max);
+  }
+  return 1.0+max;
+}
+
+
+/*************************************************************************
+* This function computes the load imbalance over all the constrains
+* For now assume that we just want balanced partitionings
+**************************************************************************/ 
+void Compute2WayHLoadImbalanceVec(int ncon, float *npwgts, float *tpwgts, float *lbvec)
+{
+  int i;
+
+  for (i=0; i<ncon; i++) 
+    lbvec[i] = 1.0 + fabs(tpwgts[0]-npwgts[i])/tpwgts[0];
+}
+
diff --git a/contrib/Metis/mfm2.c b/contrib/Metis/mfm2.c
new file mode 100644
index 0000000000..9b525d8bf7
--- /dev/null
+++ b/contrib/Metis/mfm2.c
@@ -0,0 +1,349 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * mfm2.c
+ *
+ * This file contains code that implements the edge-based FM refinement
+ *
+ * Started 7/23/97
+ * George
+ *
+ * $Id: mfm2.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function performs an edge-based FM refinement
+**************************************************************************/
+void MocFM_2WayEdgeRefine2(CtrlType *ctrl, GraphType *graph, float *tpwgts, float *orgubvec, 
+       int npasses)
+{
+  int i, ii, j, k, l, kwgt, nvtxs, ncon, nbnd, nswaps, from, to, pass, me, limit, tmp, cnum;
+  idxtype *xadj, *adjncy, *adjwgt, *where, *id, *ed, *bndptr, *bndind;
+  idxtype *moved, *swaps, *perm, *qnum;
+  float *nvwgt, *npwgts, origdiff[MAXNCON], origbal[MAXNCON], minbal[MAXNCON];
+  PQueueType parts[MAXNCON][2];
+  int higain, oldgain, mincut, initcut, newcut, mincutorder;
+  float *maxwgt, *minwgt, ubvec[MAXNCON], tvec[MAXNCON];
+
+  nvtxs = graph->nvtxs;
+  ncon = graph->ncon;
+  xadj = graph->xadj;
+  nvwgt = graph->nvwgt;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+  where = graph->where;
+  id = graph->id;
+  ed = graph->ed;
+  npwgts = graph->npwgts;
+  bndptr = graph->bndptr;
+  bndind = graph->bndind;
+
+  moved = idxwspacemalloc(ctrl, nvtxs);
+  swaps = idxwspacemalloc(ctrl, nvtxs);
+  perm = idxwspacemalloc(ctrl, nvtxs);
+  qnum = idxwspacemalloc(ctrl, nvtxs);
+
+  limit = amin(amax(0.01*nvtxs, 15), 100);
+
+  Compute2WayHLoadImbalanceVec(ncon, npwgts, tpwgts, origbal);
+  for (i=0; i<ncon; i++) {
+    origdiff[i] = fabs(tpwgts[0]-npwgts[i]);
+    ubvec[i] = amax(origbal[i], orgubvec[i]);
+  }
+
+  /* Setup the weight intervals of the two subdomains */
+  minwgt = fwspacemalloc(ctrl, 2*ncon);
+  maxwgt = fwspacemalloc(ctrl, 2*ncon);
+
+  for (i=0; i<2; i++) {
+    for (j=0; j<ncon; j++) {
+      maxwgt[i*ncon+j] = tpwgts[i]*ubvec[j];
+      minwgt[i*ncon+j] = tpwgts[i]*(1.0/ubvec[j]);
+    }
+  }
+
+  /* Initialize the queues */
+  for (i=0; i<ncon; i++) {
+    PQueueInit(ctrl, &parts[i][0], nvtxs, PLUS_GAINSPAN+1);
+    PQueueInit(ctrl, &parts[i][1], nvtxs, PLUS_GAINSPAN+1);
+  }
+  for (i=0; i<nvtxs; i++)
+    qnum[i] = samax(ncon, nvwgt+i*ncon);
+
+
+  if (ctrl->dbglvl&DBG_REFINE) {
+    printf("Parts: [");
+    for (l=0; l<ncon; l++)
+      printf("(%.3f, %.3f) ", npwgts[l], npwgts[ncon+l]);
+    printf("] T[%.3f %.3f], Nv-Nb[%5d, %5d]. ICut: %6d, LB: ", tpwgts[0], tpwgts[1], 
+            graph->nvtxs, graph->nbnd, graph->mincut);
+    for (i=0; i<ncon; i++)
+      printf("%.3f ", origbal[i]);
+    printf("\n");
+  }
+
+  idxset(nvtxs, -1, moved);
+  for (pass=0; pass<npasses; pass++) { /* Do a number of passes */
+    for (i=0; i<ncon; i++) { 
+      PQueueReset(&parts[i][0]);
+      PQueueReset(&parts[i][1]);
+    }
+
+    mincutorder = -1;
+    newcut = mincut = initcut = graph->mincut;
+    Compute2WayHLoadImbalanceVec(ncon, npwgts, tpwgts, minbal);
+
+    ASSERT(ComputeCut(graph, where) == graph->mincut);
+    ASSERT(CheckBnd(graph));
+
+    /* Insert boundary nodes in the priority queues */
+    nbnd = graph->nbnd;
+    RandomPermute(nbnd, perm, 1);
+    for (ii=0; ii<nbnd; ii++) {
+      i = bndind[perm[ii]];
+      ASSERT(ed[i] > 0 || id[i] == 0);
+      ASSERT(bndptr[i] != -1);
+      PQueueInsert(&parts[qnum[i]][where[i]], i, ed[i]-id[i]);
+    }
+
+    for (nswaps=0; nswaps<nvtxs; nswaps++) {
+      SelectQueue2(ncon, npwgts, tpwgts, &from, &cnum, parts, maxwgt);
+      to = (from+1)%2;
+
+      if (from == -1 || (higain = PQueueGetMax(&parts[cnum][from])) == -1)
+        break;
+      ASSERT(bndptr[higain] != -1);
+
+      newcut -= (ed[higain]-id[higain]);
+      saxpy(ncon, 1.0, nvwgt+higain*ncon, 1, npwgts+to*ncon, 1);
+      saxpy(ncon, -1.0, nvwgt+higain*ncon, 1, npwgts+from*ncon, 1);
+
+      Compute2WayHLoadImbalanceVec(ncon, npwgts, tpwgts, tvec);
+      if ((newcut < mincut && AreAllBelow(ncon, tvec, ubvec)) ||
+          (newcut == mincut && IsBetter2wayBalance(ncon, tvec, minbal, ubvec))) {
+        mincut = newcut;
+        for (i=0; i<ncon; i++) 
+          minbal[i] = tvec[i];
+        mincutorder = nswaps;
+      }
+      else if (nswaps-mincutorder > limit) { /* We hit the limit, undo last move */
+        newcut += (ed[higain]-id[higain]);
+        saxpy(ncon, 1.0, nvwgt+higain*ncon, 1, npwgts+from*ncon, 1);
+        saxpy(ncon, -1.0, nvwgt+higain*ncon, 1, npwgts+to*ncon, 1);
+        break;
+      }
+
+      where[higain] = to;
+      moved[higain] = nswaps;
+      swaps[nswaps] = higain;
+
+      if (ctrl->dbglvl&DBG_MOVEINFO) {
+        printf("Moved %6d from %d(%d). Gain: %5d, Cut: %5d, NPwgts: ", higain, from, cnum, ed[higain]-id[higain], newcut);
+        for (l=0; l<ncon; l++) 
+          printf("(%.3f, %.3f) ", npwgts[l], npwgts[ncon+l]);
+
+        printf(", LB: ");
+        for (i=0; i<ncon; i++) 
+          printf("%.3f ", tvec[i]);
+        if (mincutorder == nswaps)
+          printf(" *\n");
+        else
+          printf("\n");
+      }
+
+
+      /**************************************************************
+      * Update the id[i]/ed[i] values of the affected nodes
+      ***************************************************************/
+      SWAP(id[higain], ed[higain], tmp);
+      if (ed[higain] == 0 && xadj[higain] < xadj[higain+1]) 
+        BNDDelete(nbnd, bndind,  bndptr, higain);
+
+      for (j=xadj[higain]; j<xadj[higain+1]; j++) {
+        k = adjncy[j];
+        oldgain = ed[k]-id[k];
+
+        kwgt = (to == where[k] ? adjwgt[j] : -adjwgt[j]);
+        INC_DEC(id[k], ed[k], kwgt);
+
+        /* Update its boundary information and queue position */
+        if (bndptr[k] != -1) { /* If k was a boundary vertex */
+          if (ed[k] == 0) { /* Not a boundary vertex any more */
+            BNDDelete(nbnd, bndind, bndptr, k);
+            if (moved[k] == -1)  /* Remove it if in the queues */
+              PQueueDelete(&parts[qnum[k]][where[k]], k, oldgain);
+          }
+          else { /* If it has not been moved, update its position in the queue */
+            if (moved[k] == -1)
+              PQueueUpdate(&parts[qnum[k]][where[k]], k, oldgain, ed[k]-id[k]);
+          }
+        }
+        else {
+          if (ed[k] > 0) {  /* It will now become a boundary vertex */
+            BNDInsert(nbnd, bndind, bndptr, k);
+            if (moved[k] == -1) 
+              PQueueInsert(&parts[qnum[k]][where[k]], k, ed[k]-id[k]);
+          }
+        }
+      }
+
+    }
+
+
+    /****************************************************************
+    * Roll back computations
+    *****************************************************************/
+    for (i=0; i<nswaps; i++)
+      moved[swaps[i]] = -1;  /* reset moved array */
+    for (nswaps--; nswaps>mincutorder; nswaps--) {
+      higain = swaps[nswaps];
+
+      to = where[higain] = (where[higain]+1)%2;
+      SWAP(id[higain], ed[higain], tmp);
+      if (ed[higain] == 0 && bndptr[higain] != -1 && xadj[higain] < xadj[higain+1])
+        BNDDelete(nbnd, bndind,  bndptr, higain);
+      else if (ed[higain] > 0 && bndptr[higain] == -1)
+        BNDInsert(nbnd, bndind,  bndptr, higain);
+
+      saxpy(ncon, 1.0, nvwgt+higain*ncon, 1, npwgts+to*ncon, 1);
+      saxpy(ncon, -1.0, nvwgt+higain*ncon, 1, npwgts+((to+1)%2)*ncon, 1);
+      for (j=xadj[higain]; j<xadj[higain+1]; j++) {
+        k = adjncy[j];
+
+        kwgt = (to == where[k] ? adjwgt[j] : -adjwgt[j]);
+        INC_DEC(id[k], ed[k], kwgt);
+
+        if (bndptr[k] != -1 && ed[k] == 0)
+          BNDDelete(nbnd, bndind, bndptr, k);
+        if (bndptr[k] == -1 && ed[k] > 0)
+          BNDInsert(nbnd, bndind, bndptr, k);
+      }
+    }
+
+    if (ctrl->dbglvl&DBG_REFINE) {
+      printf("\tMincut: %6d at %5d, NBND: %6d, NPwgts: [", mincut, mincutorder, nbnd);
+      for (l=0; l<ncon; l++)
+        printf("(%.3f, %.3f) ", npwgts[l], npwgts[ncon+l]);
+      printf("], LB: ");
+      Compute2WayHLoadImbalanceVec(ncon, npwgts, tpwgts, tvec);
+      for (i=0; i<ncon; i++) 
+        printf("%.3f ", tvec[i]);
+      printf("\n");
+    }
+
+    graph->mincut = mincut;
+    graph->nbnd = nbnd;
+
+    if (mincutorder == -1 || mincut == initcut)
+      break;
+  }
+
+  for (i=0; i<ncon; i++) {
+    PQueueFree(ctrl, &parts[i][0]);
+    PQueueFree(ctrl, &parts[i][1]);
+  }
+
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+  fwspacefree(ctrl, 2*ncon);
+  fwspacefree(ctrl, 2*ncon);
+
+}
+
+
+/*************************************************************************
+* This function selects the partition number and the queue from which
+* we will move vertices out
+**************************************************************************/ 
+void SelectQueue2(int ncon, float *npwgts, float *tpwgts, int *from, int *cnum, 
+       PQueueType queues[MAXNCON][2], float *maxwgt)
+{
+  int i, j, maxgain=0;
+  float diff, max, maxdiff=0.0;
+
+  *from = -1;
+  *cnum = -1;
+
+  /* First determine the side and the queue, irrespective of the presence of nodes */
+  for (j=0; j<2; j++) {
+    for (i=0; i<ncon; i++) {
+      diff = npwgts[j*ncon+i]-maxwgt[j*ncon+i];
+      if (diff >= maxdiff) {
+        maxdiff = diff;
+        *from = j;
+        *cnum = i;
+      }
+    }
+  }
+
+  if (*from != -1 && PQueueGetSize(&queues[*cnum][*from]) == 0) {
+    /* The desired queue is empty, select a node from that side anyway */
+    for (i=0; i<ncon; i++) {
+      if (PQueueGetSize(&queues[i][*from]) > 0) {
+        max = (npwgts[(*from)*ncon+i] - maxwgt[(*from)*ncon+i]);
+        *cnum = i;
+        break;
+      }
+    }
+
+    for (i++; i<ncon; i++) {
+      diff = npwgts[(*from)*ncon+i] - maxwgt[(*from)*ncon+i];
+      if (diff > max && PQueueGetSize(&queues[i][*from]) > 0) {
+        max = diff;
+        *cnum = i;
+      }
+    }
+  }
+
+  /* Check to see if you can focus on the cut */
+  if (maxdiff <= 0.0 || *from == -1) {
+    maxgain = -100000;
+
+    for (j=0; j<2; j++) {
+      for (i=0; i<ncon; i++) {
+        if (PQueueGetSize(&queues[i][j]) > 0 && PQueueGetKey(&queues[i][j]) > maxgain) {
+          maxgain = PQueueGetKey(&queues[i][j]); 
+          *from = j;
+          *cnum = i;
+        }
+      }
+    }
+
+    /* printf("(%2d %2d) %3d\n", *from, *cnum, maxgain); */
+  }
+}
+
+
+/*************************************************************************
+* This function checks if the newbal is better than oldbal given the
+* ubvector ubvec
+**************************************************************************/
+int IsBetter2wayBalance(int ncon, float *newbal, float *oldbal, float *ubvec)
+{
+  int i, j;
+  float max1=0.0, max2=0.0, sum1=0.0, sum2=0.0, tmp;
+
+  for (i=0; i<ncon; i++) {
+    tmp = (newbal[i]-1)/(ubvec[i]-1);
+    max1 = (max1 < tmp ? tmp : max1);
+    sum1 += tmp;
+
+    tmp = (oldbal[i]-1)/(ubvec[i]-1);
+    max2 = (max2 < tmp ? tmp : max2);
+    sum2 += tmp;
+  }
+
+  if (max1 < max2)
+    return 1;
+  else if (max1 > max2)
+    return 0;
+  else
+    return sum1 <= sum2;
+}
+
+
diff --git a/contrib/Metis/mincover.c b/contrib/Metis/mincover.c
new file mode 100644
index 0000000000..0e5a923308
--- /dev/null
+++ b/contrib/Metis/mincover.c
@@ -0,0 +1,259 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * mincover.c
+ *
+ * This file implements the minimum cover algorithm
+ *
+ * Started 8/1/97
+ * George
+ *
+ * $Id: mincover.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ */
+
+#include <metis.h>
+
+/*************************************************************************
+* Constants used by mincover algorithm
+**************************************************************************/
+#define INCOL 10
+#define INROW 20
+#define VC 1
+#define SC 2
+#define HC 3
+#define VR 4
+#define SR 5
+#define HR 6
+
+
+/*************************************************************************
+* This function returns the min-cover of a bipartite graph.
+* The algorithm used is due to Hopcroft and Karp as modified by Duff etal
+* adj: the adjacency list of the bipartite graph
+*       asize: the number of vertices in the first part of the bipartite graph
+* bsize-asize: the number of vertices in the second part
+*        0..(asize-1) > A vertices
+*        asize..bsize > B vertices
+*
+* Returns:
+*  cover : the actual cover (array)
+*  csize : the size of the cover
+**************************************************************************/
+void MinCover(idxtype *xadj, idxtype *adjncy, int asize, int bsize, idxtype *cover, int *csize)
+{
+  int i, j;
+  idxtype *mate, *queue, *flag, *level, *lst;
+  int fptr, rptr, lstptr;
+  int row, maxlevel, col;
+
+  mate = idxsmalloc(bsize, -1, "MinCover: mate");
+  flag = idxmalloc(bsize, "MinCover: flag");
+  level = idxmalloc(bsize, "MinCover: level");
+  queue = idxmalloc(bsize, "MinCover: queue");
+  lst = idxmalloc(bsize, "MinCover: lst");
+
+  /* Get a cheap matching */
+  for (i=0; i<asize; i++) {
+    for (j=xadj[i]; j<xadj[i+1]; j++) {
+      if (mate[adjncy[j]] == -1) {
+        mate[i] = adjncy[j];
+        mate[adjncy[j]] = i;
+        break;
+      }
+    }
+  }
+
+  /* Get into the main loop */
+  while (1) {
+    /* Initialization */
+    fptr = rptr = 0;   /* Empty Queue */
+    lstptr = 0;        /* Empty List */
+    for (i=0; i<bsize; i++) {
+      level[i] = -1;
+      flag[i] = 0;
+    }
+    maxlevel = bsize;
+
+    /* Insert free nodes into the queue */
+    for (i=0; i<asize; i++) 
+      if (mate[i] == -1) {
+        queue[rptr++] = i;
+        level[i] = 0;
+      }
+
+    /* Perform the BFS */
+    while (fptr != rptr) {
+      row = queue[fptr++];
+      if (level[row] < maxlevel) {
+        flag[row] = 1;
+        for (j=xadj[row]; j<xadj[row+1]; j++) {
+          col = adjncy[j];
+          if (!flag[col]) {  /* If this column has not been accessed yet */
+            flag[col] = 1;
+            if (mate[col] == -1) { /* Free column node was found */
+              maxlevel = level[row];
+              lst[lstptr++] = col;
+            }
+            else { /* This column node is matched */
+              if (flag[mate[col]]) 
+                printf("\nSomething wrong, flag[%d] is 1",mate[col]);
+              queue[rptr++] = mate[col];
+              level[mate[col]] = level[row] + 1;
+            }
+          }
+        }
+      } 
+    }
+
+    if (lstptr == 0)
+      break;   /* No free columns can be reached */
+
+    /* Perform restricted DFS from the free column nodes */
+    for (i=0; i<lstptr; i++)
+      MinCover_Augment(xadj, adjncy, lst[i], mate, flag, level, maxlevel);
+  }
+
+  MinCover_Decompose(xadj, adjncy, asize, bsize, mate, cover, csize);
+
+  GKfree(&mate, &flag, &level, &queue, &lst, LTERM);
+
+}
+
+
+/*************************************************************************
+* This function perfoms a restricted DFS and augments matchings
+**************************************************************************/
+int MinCover_Augment(idxtype *xadj, idxtype *adjncy, int col, idxtype *mate, idxtype *flag, idxtype *level, int maxlevel)
+{
+  int i;
+  int row = -1;
+  int status;
+
+  flag[col] = 2;
+  for (i=xadj[col]; i<xadj[col+1]; i++) {
+    row = adjncy[i];
+
+    if (flag[row] == 1) { /* First time through this row node */
+      if (level[row] == maxlevel) {  /* (col, row) is an edge of the G^T */
+        flag[row] = 2;  /* Mark this node as being visited */
+        if (maxlevel != 0)
+          status = MinCover_Augment(xadj, adjncy, mate[row], mate, flag, level, maxlevel-1);
+        else
+          status = 1;
+
+        if (status) {
+          mate[col] = row;
+          mate[row] = col;
+          return 1;
+        }
+      }
+    }
+  }
+
+  return 0;
+}
+
+
+
+/*************************************************************************
+* This function performs a coarse decomposition and determines the 
+* min-cover.
+* REF: Pothen ACMTrans. on Amth Software
+**************************************************************************/
+void MinCover_Decompose(idxtype *xadj, idxtype *adjncy, int asize, int bsize, idxtype *mate, idxtype *cover, int *csize)
+{
+  int i, k;
+  idxtype *where;
+  int card[10];
+
+  where = idxmalloc(bsize, "MinCover_Decompose: where");
+  for (i=0; i<10; i++)
+    card[i] = 0;
+
+  for (i=0; i<asize; i++)
+    where[i] = SC;
+  for (; i<bsize; i++)
+    where[i] = SR;
+
+  for (i=0; i<asize; i++) 
+    if (mate[i] == -1)  
+      MinCover_ColDFS(xadj, adjncy, i, mate, where, INCOL);
+  for (; i<bsize; i++) 
+    if (mate[i] == -1)  
+      MinCover_RowDFS(xadj, adjncy, i, mate, where, INROW);
+
+  for (i=0; i<bsize; i++) 
+    card[where[i]]++;
+
+  k = 0;
+  if (abs(card[VC]+card[SC]-card[HR]) < abs(card[VC]-card[SR]-card[HR])) {  /* S = VC+SC+HR */
+    /* printf("%d %d ",vc+sc, hr); */
+    for (i=0; i<bsize; i++) 
+      if (where[i] == VC || where[i] == SC || where[i] == HR)
+        cover[k++] = i;
+  }
+  else {  /* S = VC+SR+HR */
+    /* printf("%d %d ",vc, hr+sr); */
+    for (i=0; i<bsize; i++) 
+      if (where[i] == VC || where[i] == SR || where[i] == HR)
+        cover[k++] = i;
+  }
+
+  *csize = k;
+  free(where);
+
+}
+
+
+/*************************************************************************
+* This function perfoms a dfs starting from an unmatched col node
+* forming alternate paths
+**************************************************************************/
+void MinCover_ColDFS(idxtype *xadj, idxtype *adjncy, int root, idxtype *mate, idxtype *where, int flag)
+{
+  int i;
+
+  if (flag == INCOL) {
+    if (where[root] == HC)
+      return;
+    where[root] = HC;
+    for (i=xadj[root]; i<xadj[root+1]; i++) 
+      MinCover_ColDFS(xadj, adjncy, adjncy[i], mate, where, INROW);
+  }
+  else {
+    if (where[root] == HR)
+      return;
+    where[root] = HR;
+    if (mate[root] != -1)
+      MinCover_ColDFS(xadj, adjncy, mate[root], mate, where, INCOL);
+  }
+
+}
+
+/*************************************************************************
+* This function perfoms a dfs starting from an unmatched col node
+* forming alternate paths
+**************************************************************************/
+void MinCover_RowDFS(idxtype *xadj, idxtype *adjncy, int root, idxtype *mate, idxtype *where, int flag)
+{
+  int i;
+
+  if (flag == INROW) {
+    if (where[root] == VR)
+      return;
+    where[root] = VR;
+    for (i=xadj[root]; i<xadj[root+1]; i++) 
+      MinCover_RowDFS(xadj, adjncy, adjncy[i], mate, where, INCOL);
+  }
+  else {
+    if (where[root] == VC)
+      return;
+    where[root] = VC;
+    if (mate[root] != -1)
+      MinCover_RowDFS(xadj, adjncy, mate[root], mate, where, INROW);
+  }
+
+}
+
+
+
diff --git a/contrib/Metis/minitpart.c b/contrib/Metis/minitpart.c
new file mode 100644
index 0000000000..7b2091fcfd
--- /dev/null
+++ b/contrib/Metis/minitpart.c
@@ -0,0 +1,355 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * minitpart.c
+ *
+ * This file contains code that performs the initial partition of the
+ * coarsest graph
+ *
+ * Started 7/23/97
+ * George
+ *
+ * $Id: minitpart.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+/*************************************************************************
+* This function computes the initial bisection of the coarsest graph
+**************************************************************************/
+void MocInit2WayPartition(CtrlType *ctrl, GraphType *graph, float *tpwgts, float ubfactor) 
+{
+  int i, dbglvl;
+
+  dbglvl = ctrl->dbglvl;
+  IFSET(ctrl->dbglvl, DBG_REFINE, ctrl->dbglvl -= DBG_REFINE);
+  IFSET(ctrl->dbglvl, DBG_MOVEINFO, ctrl->dbglvl -= DBG_MOVEINFO);
+
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->InitPartTmr));
+
+  switch (ctrl->IType) {
+    case IPART_GGPKL:
+      MocGrowBisection(ctrl, graph, tpwgts, ubfactor);
+      break;
+    case IPART_RANDOM:
+      MocRandomBisection(ctrl, graph, tpwgts, ubfactor);
+      break;
+    default:
+      errexit("Unknown initial partition type: %d\n", ctrl->IType);
+  }
+
+  IFSET(ctrl->dbglvl, DBG_IPART, printf("Initial Cut: %d [%d]\n", graph->mincut, graph->where[0]));
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->InitPartTmr));
+  ctrl->dbglvl = dbglvl;
+
+}
+
+
+
+
+
+/*************************************************************************
+* This function takes a graph and produces a bisection by using a region
+* growing algorithm. The resulting partition is returned in
+* graph->where
+**************************************************************************/
+void MocGrowBisection(CtrlType *ctrl, GraphType *graph, float *tpwgts, float ubfactor)
+{
+  int i, j, k, nvtxs, ncon, from, bestcut, mincut, nbfs;
+  idxtype *bestwhere, *where;
+
+  nvtxs = graph->nvtxs;
+
+  MocAllocate2WayPartitionMemory(ctrl, graph);
+  where = graph->where;
+
+  bestwhere = idxmalloc(nvtxs, "BisectGraph: bestwhere");
+  nbfs = 2*(nvtxs <= ctrl->CoarsenTo ? SMALLNIPARTS : LARGENIPARTS);
+  bestcut = idxsum(graph->nedges, graph->adjwgt);  
+
+  for (; nbfs>0; nbfs--) {
+    idxset(nvtxs, 1, where);
+    where[RandomInRange(nvtxs)] = 0;
+
+    MocCompute2WayPartitionParams(ctrl, graph);
+
+    MocInit2WayBalance(ctrl, graph, tpwgts);
+
+    MocFM_2WayEdgeRefine(ctrl, graph, tpwgts, 4); 
+
+    MocBalance2Way(ctrl, graph, tpwgts, 1.02);
+    MocFM_2WayEdgeRefine(ctrl, graph, tpwgts, 4); 
+
+    if (bestcut >= graph->mincut) {
+      bestcut = graph->mincut;
+      idxcopy(nvtxs, where, bestwhere);
+      if (bestcut == 0)
+        break;
+    }
+  }
+
+  graph->mincut = bestcut;
+  idxcopy(nvtxs, bestwhere, where);
+
+  GKfree(&bestwhere, LTERM);
+}
+
+
+
+/*************************************************************************
+* This function takes a graph and produces a bisection by using a region
+* growing algorithm. The resulting partition is returned in
+* graph->where
+**************************************************************************/
+void MocRandomBisection(CtrlType *ctrl, GraphType *graph, float *tpwgts, float ubfactor)
+{
+  int i, ii, j, k, nvtxs, ncon, from, bestcut, mincut, nbfs, qnum;
+  idxtype *bestwhere, *where, *perm;
+  int counts[MAXNCON];
+  float *nvwgt;
+
+  nvtxs = graph->nvtxs;
+  ncon = graph->ncon;
+  nvwgt = graph->nvwgt;
+
+  MocAllocate2WayPartitionMemory(ctrl, graph);
+  where = graph->where;
+
+  bestwhere = idxmalloc(nvtxs, "BisectGraph: bestwhere");
+  nbfs = 2*(nvtxs <= ctrl->CoarsenTo ? SMALLNIPARTS : LARGENIPARTS);
+  bestcut = idxsum(graph->nedges, graph->adjwgt);  
+  perm = idxmalloc(nvtxs, "BisectGraph: perm");
+
+  for (; nbfs>0; nbfs--) {
+    for (i=0; i<ncon; i++)
+      counts[i] = 0;
+
+    RandomPermute(nvtxs, perm, 1);
+
+    /* Partition by spliting the queues randomly */
+    for (ii=0; ii<nvtxs; ii++) {
+      i = perm[ii];
+      qnum = samax(ncon, nvwgt+i*ncon);
+      where[i] = counts[qnum];
+      counts[qnum] = (counts[qnum]+1)%2;
+    }
+
+    MocCompute2WayPartitionParams(ctrl, graph);
+
+    MocFM_2WayEdgeRefine(ctrl, graph, tpwgts, 6); 
+    MocBalance2Way(ctrl, graph, tpwgts, 1.02);
+    MocFM_2WayEdgeRefine(ctrl, graph, tpwgts, 6); 
+    MocBalance2Way(ctrl, graph, tpwgts, 1.02);
+    MocFM_2WayEdgeRefine(ctrl, graph, tpwgts, 6); 
+
+    /*
+    printf("Edgecut: %6d, NPwgts: [", graph->mincut);
+    for (i=0; i<graph->ncon; i++)
+      printf("(%.3f %.3f) ", graph->npwgts[i], graph->npwgts[graph->ncon+i]);
+    printf("]\n");
+    */
+
+    if (bestcut >= graph->mincut) {
+      bestcut = graph->mincut;
+      idxcopy(nvtxs, where, bestwhere);
+      if (bestcut == 0)
+        break;
+    }
+  }
+
+  graph->mincut = bestcut;
+  idxcopy(nvtxs, bestwhere, where);
+
+  GKfree(&bestwhere, &perm, LTERM);
+}
+
+
+
+
+/*************************************************************************
+* This function balances two partitions by moving the highest gain 
+* (including negative gain) vertices to the other domain.
+* It is used only when tha unbalance is due to non contigous
+* subdomains. That is, the are no boundary vertices.
+* It moves vertices from the domain that is overweight to the one that 
+* is underweight.
+**************************************************************************/
+void MocInit2WayBalance(CtrlType *ctrl, GraphType *graph, float *tpwgts)
+{
+  int i, ii, j, k, l, kwgt, nvtxs, nbnd, ncon, nswaps, from, to, pass, me, cnum, tmp;
+  idxtype *xadj, *adjncy, *adjwgt, *where, *id, *ed, *bndptr, *bndind;
+  idxtype *perm, *qnum;
+  float *nvwgt, *npwgts;
+  PQueueType parts[MAXNCON][2];
+  int higain, oldgain, mincut;
+
+  nvtxs = graph->nvtxs;
+  ncon = graph->ncon;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  nvwgt = graph->nvwgt;
+  adjwgt = graph->adjwgt;
+  where = graph->where;
+  id = graph->id;
+  ed = graph->ed;
+  npwgts = graph->npwgts;
+  bndptr = graph->bndptr;
+  bndind = graph->bndind;
+
+  perm = idxwspacemalloc(ctrl, nvtxs);
+  qnum = idxwspacemalloc(ctrl, nvtxs);
+
+  /* This is called for initial partitioning so we know from where to pick nodes */
+  from = 1;
+  to = (from+1)%2;
+
+  if (ctrl->dbglvl&DBG_REFINE) {
+    printf("Parts: [");
+    for (l=0; l<ncon; l++)
+      printf("(%.3f, %.3f) ", npwgts[l], npwgts[ncon+l]);
+    printf("] T[%.3f %.3f], Nv-Nb[%5d, %5d]. ICut: %6d, LB: %.3f [B]\n", tpwgts[0], tpwgts[1], 
+           graph->nvtxs, graph->nbnd, graph->mincut, 
+           Compute2WayHLoadImbalance(ncon, npwgts, tpwgts));
+  }
+
+  for (i=0; i<ncon; i++) {
+    PQueueInit(ctrl, &parts[i][0], nvtxs, PLUS_GAINSPAN+1);
+    PQueueInit(ctrl, &parts[i][1], nvtxs, PLUS_GAINSPAN+1);
+  }
+
+  ASSERT(ComputeCut(graph, where) == graph->mincut);
+  ASSERT(CheckBnd(graph));
+  ASSERT(CheckGraph(graph));
+
+  /* Compute the queues in which each vertex will be assigned to */
+  for (i=0; i<nvtxs; i++)
+    qnum[i] = samax(ncon, nvwgt+i*ncon);
+
+  /* Insert the nodes of the proper partition in the appropriate priority queue */
+  RandomPermute(nvtxs, perm, 1);
+  for (ii=0; ii<nvtxs; ii++) {
+    i = perm[ii];
+    if (where[i] == from) {
+      if (ed[i] > 0)
+        PQueueInsert(&parts[qnum[i]][0], i, ed[i]-id[i]);
+      else
+        PQueueInsert(&parts[qnum[i]][1], i, ed[i]-id[i]);
+    }
+  }
+
+
+  mincut = graph->mincut;
+  nbnd = graph->nbnd;
+  for (nswaps=0; nswaps<nvtxs; nswaps++) {
+    if (AreAnyVwgtsBelow(ncon, 1.0, npwgts+from*ncon, 0.0, nvwgt, tpwgts[from]))
+      break;
+
+    if ((cnum = SelectQueueOneWay(ncon, npwgts, tpwgts, from, parts)) == -1)
+      break;
+
+    if ((higain = PQueueGetMax(&parts[cnum][0])) == -1)
+      higain = PQueueGetMax(&parts[cnum][1]);
+
+    mincut -= (ed[higain]-id[higain]);
+    saxpy(ncon, 1.0, nvwgt+higain*ncon, 1, npwgts+to*ncon, 1);
+    saxpy(ncon, -1.0, nvwgt+higain*ncon, 1, npwgts+from*ncon, 1);
+
+    where[higain] = to;
+
+    if (ctrl->dbglvl&DBG_MOVEINFO) {
+      printf("Moved %6d from %d(%d). [%5d] %5d, NPwgts: ", higain, from, cnum, ed[higain]-id[higain], mincut);
+      for (l=0; l<ncon; l++) 
+        printf("(%.3f, %.3f) ", npwgts[l], npwgts[ncon+l]);
+      printf(", LB: %.3f\n", Compute2WayHLoadImbalance(ncon, npwgts, tpwgts));
+      if (ed[higain] == 0 && id[higain] > 0)
+        printf("\t Pulled from the interior!\n");
+    }
+
+
+    /**************************************************************
+    * Update the id[i]/ed[i] values of the affected nodes
+    ***************************************************************/
+    SWAP(id[higain], ed[higain], tmp);
+    if (ed[higain] == 0 && bndptr[higain] != -1 && xadj[higain] < xadj[higain+1]) 
+      BNDDelete(nbnd, bndind,  bndptr, higain);
+    if (ed[higain] > 0 && bndptr[higain] == -1)
+      BNDInsert(nbnd, bndind,  bndptr, higain);
+
+    for (j=xadj[higain]; j<xadj[higain+1]; j++) {
+      k = adjncy[j];
+      oldgain = ed[k]-id[k];
+
+      kwgt = (to == where[k] ? adjwgt[j] : -adjwgt[j]);
+      INC_DEC(id[k], ed[k], kwgt);
+
+      /* Update the queue position */
+      if (where[k] == from) {
+        if (ed[k] > 0 && bndptr[k] == -1) {  /* It moves in boundary */
+          PQueueDelete(&parts[qnum[k]][1], k, oldgain);
+          PQueueInsert(&parts[qnum[k]][0], k, ed[k]-id[k]);
+        }
+        else { /* It must be in the boundary already */
+          if (bndptr[k] == -1)
+            printf("What you thought was wrong!\n");
+          PQueueUpdate(&parts[qnum[k]][0], k, oldgain, ed[k]-id[k]);
+        }
+      }
+
+      /* Update its boundary information */
+      if (ed[k] == 0 && bndptr[k] != -1) 
+        BNDDelete(nbnd, bndind, bndptr, k);
+      else if (ed[k] > 0 && bndptr[k] == -1)  
+        BNDInsert(nbnd, bndind, bndptr, k);
+    }
+
+    ASSERTP(ComputeCut(graph, where) == mincut, ("%d != %d\n", ComputeCut(graph, where), mincut));
+
+  }
+
+  if (ctrl->dbglvl&DBG_REFINE) {
+    printf("\tMincut: %6d, NBND: %6d, NPwgts: ", mincut, nbnd);
+    for (l=0; l<ncon; l++)
+      printf("(%.3f, %.3f) ", npwgts[l], npwgts[ncon+l]);
+    printf(", LB: %.3f\n", Compute2WayHLoadImbalance(ncon, npwgts, tpwgts));
+  }
+
+  graph->mincut = mincut;
+  graph->nbnd = nbnd;
+
+  for (i=0; i<ncon; i++) {
+    PQueueFree(ctrl, &parts[i][0]);
+    PQueueFree(ctrl, &parts[i][1]);
+  }
+
+  ASSERT(ComputeCut(graph, where) == graph->mincut);
+  ASSERT(CheckBnd(graph));
+
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+}
+
+
+
+
+/*************************************************************************
+* This function selects the partition number and the queue from which
+* we will move vertices out
+**************************************************************************/ 
+int SelectQueueOneWay(int ncon, float *npwgts, float *tpwgts, int from, PQueueType queues[MAXNCON][2])
+{
+  int i, cnum=-1;
+  float max=0.0;
+
+  for (i=0; i<ncon; i++) {
+    if (npwgts[from*ncon+i]-tpwgts[from] >= max && 
+        PQueueGetSize(&queues[i][0]) + PQueueGetSize(&queues[i][1]) > 0) {
+      max = npwgts[from*ncon+i]-tpwgts[0];
+      cnum = i;
+    }
+  }
+
+  return cnum;
+}
+
+
diff --git a/contrib/Metis/minitpart2.c b/contrib/Metis/minitpart2.c
new file mode 100644
index 0000000000..bade21bc52
--- /dev/null
+++ b/contrib/Metis/minitpart2.c
@@ -0,0 +1,368 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * minitpart2.c
+ *
+ * This file contains code that performs the initial partition of the
+ * coarsest graph
+ *
+ * Started 7/23/97
+ * George
+ *
+ * $Id: minitpart2.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+/*************************************************************************
+* This function computes the initial bisection of the coarsest graph
+**************************************************************************/
+void MocInit2WayPartition2(CtrlType *ctrl, GraphType *graph, float *tpwgts, float *ubvec) 
+{
+  int dbglvl;
+
+  dbglvl = ctrl->dbglvl;
+  IFSET(ctrl->dbglvl, DBG_REFINE, ctrl->dbglvl -= DBG_REFINE);
+  IFSET(ctrl->dbglvl, DBG_MOVEINFO, ctrl->dbglvl -= DBG_MOVEINFO);
+
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->InitPartTmr));
+
+  switch (ctrl->IType) {
+    case IPART_GGPKL:
+    case IPART_RANDOM:
+      MocGrowBisection2(ctrl, graph, tpwgts, ubvec);
+      break;
+    case 3:
+      MocGrowBisectionNew2(ctrl, graph, tpwgts, ubvec);
+      break;
+    default:
+      errexit("Unknown initial partition type: %d\n", ctrl->IType);
+  }
+
+  IFSET(ctrl->dbglvl, DBG_IPART, printf("Initial Cut: %d\n", graph->mincut));
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->InitPartTmr));
+  ctrl->dbglvl = dbglvl;
+
+}
+
+
+
+
+/*************************************************************************
+* This function takes a graph and produces a bisection by using a region
+* growing algorithm. The resulting partition is returned in
+* graph->where
+**************************************************************************/
+void MocGrowBisection2(CtrlType *ctrl, GraphType *graph, float *tpwgts, float *ubvec)
+{
+  int i, j, k, nvtxs, ncon, from, bestcut, mincut, nbfs;
+  idxtype *bestwhere, *where;
+
+  nvtxs = graph->nvtxs;
+
+  MocAllocate2WayPartitionMemory(ctrl, graph);
+  where = graph->where;
+
+  bestwhere = idxmalloc(nvtxs, "BisectGraph: bestwhere");
+  nbfs = 2*(nvtxs <= ctrl->CoarsenTo ? SMALLNIPARTS : LARGENIPARTS);
+  bestcut = idxsum(graph->nedges, graph->adjwgt);  
+
+  for (; nbfs>0; nbfs--) {
+    idxset(nvtxs, 1, where);
+    where[RandomInRange(nvtxs)] = 0;
+
+    MocCompute2WayPartitionParams(ctrl, graph);
+
+    MocBalance2Way2(ctrl, graph, tpwgts, ubvec);
+
+    MocFM_2WayEdgeRefine2(ctrl, graph, tpwgts, ubvec, 4); 
+
+    MocBalance2Way2(ctrl, graph, tpwgts, ubvec);
+    MocFM_2WayEdgeRefine2(ctrl, graph, tpwgts, ubvec, 4); 
+
+    if (bestcut > graph->mincut) {
+      bestcut = graph->mincut;
+      idxcopy(nvtxs, where, bestwhere);
+      if (bestcut == 0)
+        break;
+    }
+  }
+
+  graph->mincut = bestcut;
+  idxcopy(nvtxs, bestwhere, where);
+
+  GKfree(&bestwhere, LTERM);
+}
+
+
+
+
+
+
+/*************************************************************************
+* This function takes a graph and produces a bisection by using a region
+* growing algorithm. The resulting partition is returned in
+* graph->where
+**************************************************************************/
+void MocGrowBisectionNew2(CtrlType *ctrl, GraphType *graph, float *tpwgts, float *ubvec)
+{
+  int i, j, k, nvtxs, ncon, from, bestcut, mincut, nbfs;
+  idxtype *bestwhere, *where;
+
+  nvtxs = graph->nvtxs;
+
+  MocAllocate2WayPartitionMemory(ctrl, graph);
+  where = graph->where;
+
+  bestwhere = idxmalloc(nvtxs, "BisectGraph: bestwhere");
+  nbfs = 2*(nvtxs <= ctrl->CoarsenTo ? SMALLNIPARTS : LARGENIPARTS);
+  bestcut = idxsum(graph->nedges, graph->adjwgt);  
+
+  for (; nbfs>0; nbfs--) {
+    idxset(nvtxs, 1, where);
+    where[RandomInRange(nvtxs)] = 0;
+
+    MocCompute2WayPartitionParams(ctrl, graph);
+
+    MocInit2WayBalance2(ctrl, graph, tpwgts, ubvec);
+
+    MocFM_2WayEdgeRefine2(ctrl, graph, tpwgts, ubvec, 4); 
+
+    if (bestcut > graph->mincut) {
+      bestcut = graph->mincut;
+      idxcopy(nvtxs, where, bestwhere);
+      if (bestcut == 0)
+        break;
+    }
+  }
+
+  graph->mincut = bestcut;
+  idxcopy(nvtxs, bestwhere, where);
+
+  GKfree(&bestwhere, LTERM);
+}
+
+
+
+/*************************************************************************
+* This function balances two partitions by moving the highest gain 
+* (including negative gain) vertices to the other domain.
+* It is used only when tha unbalance is due to non contigous
+* subdomains. That is, the are no boundary vertices.
+* It moves vertices from the domain that is overweight to the one that 
+* is underweight.
+**************************************************************************/
+void MocInit2WayBalance2(CtrlType *ctrl, GraphType *graph, float *tpwgts, float *ubvec)
+{
+  int i, ii, j, k, l, kwgt, nvtxs, nbnd, ncon, nswaps, from, to, pass, me, cnum, tmp, imin;
+  idxtype *xadj, *adjncy, *adjwgt, *where, *id, *ed, *bndptr, *bndind;
+  idxtype *moved, *perm, *qnum;
+  float *nvwgt, *npwgts, minwgt;
+  PQueueType parts[MAXNCON][2];
+  int higain, oldgain, mincut;
+
+  nvtxs = graph->nvtxs;
+  ncon = graph->ncon;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  nvwgt = graph->nvwgt;
+  adjwgt = graph->adjwgt;
+  where = graph->where;
+  id = graph->id;
+  ed = graph->ed;
+  npwgts = graph->npwgts;
+  bndptr = graph->bndptr;
+  bndind = graph->bndind;
+
+  moved = idxwspacemalloc(ctrl, nvtxs);
+  perm = idxwspacemalloc(ctrl, nvtxs);
+  qnum = idxwspacemalloc(ctrl, nvtxs);
+
+  /* This is called for initial partitioning so we know from where to pick nodes */
+  from = 1;
+  to = (from+1)%2;
+
+  if (ctrl->dbglvl&DBG_REFINE) {
+    printf("Parts: [");
+    for (l=0; l<ncon; l++)
+      printf("(%.3f, %.3f) ", npwgts[l], npwgts[ncon+l]);
+    printf("] T[%.3f %.3f], Nv-Nb[%5d, %5d]. ICut: %6d, LB: %.3f [B]\n", tpwgts[0], tpwgts[1], graph->nvtxs, graph->nbnd, graph->mincut, ComputeLoadImbalance(ncon, 2, npwgts, tpwgts));
+  }
+
+  for (i=0; i<ncon; i++) {
+    PQueueInit(ctrl, &parts[i][0], nvtxs, PLUS_GAINSPAN+1);
+    PQueueInit(ctrl, &parts[i][1], nvtxs, PLUS_GAINSPAN+1);
+  }
+
+  idxset(nvtxs, -1, moved);
+
+  ASSERT(ComputeCut(graph, where) == graph->mincut);
+  ASSERT(CheckBnd(graph));
+  ASSERT(CheckGraph(graph));
+
+  /* Compute the queues in which each vertex will be assigned to */
+  for (i=0; i<nvtxs; i++)
+    qnum[i] = samax(ncon, nvwgt+i*ncon);
+
+  /* Insert the nodes of the proper partition in the appropriate priority queue */
+  RandomPermute(nvtxs, perm, 1);
+  for (ii=0; ii<nvtxs; ii++) {
+    i = perm[ii];
+    if (where[i] == from) {
+      if (ed[i] > 0)
+        PQueueInsert(&parts[qnum[i]][0], i, ed[i]-id[i]);
+      else
+        PQueueInsert(&parts[qnum[i]][1], i, ed[i]-id[i]);
+    }
+  }
+
+/*
+  for (i=0; i<ncon; i++)
+    printf("Queue #%d has %d %d\n", i, parts[i][0].nnodes, parts[i][1].nnodes);
+*/
+
+  /* Determine the termination criterion */
+  imin = 0;
+  for (i=1; i<ncon; i++) 
+    imin = (ubvec[i] < ubvec[imin] ? i : imin);
+  minwgt = .5/ubvec[imin];
+
+  mincut = graph->mincut;
+  nbnd = graph->nbnd;
+  for (nswaps=0; nswaps<nvtxs; nswaps++) {
+    /* Exit as soon as the minimum weight crossed over */
+    if (npwgts[to*ncon+imin] > minwgt)  
+      break;
+
+    if ((cnum = SelectQueueOneWay2(ncon, npwgts+to*ncon, parts, ubvec)) == -1)
+      break;
+
+    if ((higain = PQueueGetMax(&parts[cnum][0])) == -1)
+      higain = PQueueGetMax(&parts[cnum][1]);
+
+    mincut -= (ed[higain]-id[higain]);
+    saxpy(ncon, 1.0, nvwgt+higain*ncon, 1, npwgts+to*ncon, 1);
+    saxpy(ncon, -1.0, nvwgt+higain*ncon, 1, npwgts+from*ncon, 1);
+
+    where[higain] = to;
+    moved[higain] = nswaps;
+
+    if (ctrl->dbglvl&DBG_MOVEINFO) {
+      printf("Moved %6d from %d(%d). [%5d] %5d, NPwgts: ", higain, from, cnum, ed[higain]-id[higain], mincut);
+      for (l=0; l<ncon; l++) 
+        printf("(%.3f, %.3f) ", npwgts[l], npwgts[ncon+l]);
+      printf(", LB: %.3f\n", ComputeLoadImbalance(ncon, 2, npwgts, tpwgts));
+      if (ed[higain] == 0 && id[higain] > 0)
+        printf("\t Pulled from the interior!\n");
+    }
+
+
+    /**************************************************************
+    * Update the id[i]/ed[i] values of the affected nodes
+    ***************************************************************/
+    SWAP(id[higain], ed[higain], tmp);
+    if (ed[higain] == 0 && bndptr[higain] != -1 && xadj[higain] < xadj[higain+1]) 
+      BNDDelete(nbnd, bndind,  bndptr, higain);
+    if (ed[higain] > 0 && bndptr[higain] == -1)
+      BNDInsert(nbnd, bndind,  bndptr, higain);
+
+    for (j=xadj[higain]; j<xadj[higain+1]; j++) {
+      k = adjncy[j];
+      oldgain = ed[k]-id[k];
+
+      kwgt = (to == where[k] ? adjwgt[j] : -adjwgt[j]);
+      INC_DEC(id[k], ed[k], kwgt);
+
+      /* Update the queue position */
+      if (moved[k] == -1 && where[k] == from) {
+        if (ed[k] > 0 && bndptr[k] == -1) {  /* It moves in boundary */
+          PQueueDelete(&parts[qnum[k]][1], k, oldgain);
+          PQueueInsert(&parts[qnum[k]][0], k, ed[k]-id[k]);
+        }
+        else { /* It must be in the boundary already */
+          if (bndptr[k] == -1)
+            printf("What you thought was wrong!\n");
+          PQueueUpdate(&parts[qnum[k]][0], k, oldgain, ed[k]-id[k]);
+        }
+      }
+
+      /* Update its boundary information */
+      if (ed[k] == 0 && bndptr[k] != -1) 
+        BNDDelete(nbnd, bndind, bndptr, k);
+      else if (ed[k] > 0 && bndptr[k] == -1)  
+        BNDInsert(nbnd, bndind, bndptr, k);
+    }
+
+    ASSERTP(ComputeCut(graph, where) == mincut, ("%d != %d\n", ComputeCut(graph, where), mincut));
+
+  }
+
+  if (ctrl->dbglvl&DBG_REFINE) {
+    printf("\tMincut: %6d, NBND: %6d, NPwgts: ", mincut, nbnd);
+    for (l=0; l<ncon; l++)
+      printf("(%.3f, %.3f) ", npwgts[l], npwgts[ncon+l]);
+    printf(", LB: %.3f\n", ComputeLoadImbalance(ncon, 2, npwgts, tpwgts));
+  }
+
+  graph->mincut = mincut;
+  graph->nbnd = nbnd;
+
+  for (i=0; i<ncon; i++) {
+    PQueueFree(ctrl, &parts[i][0]);
+    PQueueFree(ctrl, &parts[i][1]);
+  }
+
+  ASSERT(ComputeCut(graph, where) == graph->mincut);
+  ASSERT(CheckBnd(graph));
+
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+}
+
+
+
+/*************************************************************************
+* This function selects the partition number and the queue from which
+* we will move vertices out
+**************************************************************************/ 
+int SelectQueueOneWay2(int ncon, float *pto, PQueueType queues[MAXNCON][2], float *ubvec)
+{
+  int i, cnum=-1, imax, maxgain;
+  float max=0.0;
+  float twgt[MAXNCON];
+
+  for (i=0; i<ncon; i++) {
+    if (max < pto[i]) {
+      imax = i;
+      max = pto[i];
+    }
+  }
+  for (i=0; i<ncon; i++) 
+    twgt[i] = (max/(ubvec[imax]*ubvec[i]))/pto[i];
+  twgt[imax] = 0.0;
+
+  max = 0.0;
+  for (i=0; i<ncon; i++) {
+    if (max < twgt[i] && (PQueueGetSize(&queues[i][0]) > 0 || PQueueGetSize(&queues[i][1]) > 0)) {
+      max = twgt[i];
+      cnum = i;
+    }
+  }
+  if (max > 1)
+    return cnum;
+
+  /* optimize of cut */
+  maxgain = -10000000;
+  for (i=0; i<ncon; i++) {
+    if (PQueueGetSize(&queues[i][0]) > 0 && PQueueGetKey(&queues[i][0]) > maxgain) {
+      maxgain = PQueueGetKey(&queues[i][0]);
+      cnum = i;
+    }
+  }
+
+  return cnum;
+
+}
+
diff --git a/contrib/Metis/mkmetis.c b/contrib/Metis/mkmetis.c
new file mode 100644
index 0000000000..3edf8575f0
--- /dev/null
+++ b/contrib/Metis/mkmetis.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * mkmetis.c
+ *
+ * This file contains the top level routines for the multilevel k-way partitioning
+ * algorithm KMETIS.
+ *
+ * Started 7/28/97
+ * George
+ *
+ * $Id: mkmetis.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+
+
+/*************************************************************************
+* This function is the entry point for KWMETIS
+**************************************************************************/
+void METIS_mCPartGraphKway(int *nvtxs, int *ncon, idxtype *xadj, idxtype *adjncy, 
+                          idxtype *vwgt, idxtype *adjwgt, int *wgtflag, int *numflag, 
+                          int *nparts, float *rubvec, int *options, int *edgecut, 
+                          idxtype *part)
+{
+  int i, j;
+  GraphType graph;
+  CtrlType ctrl;
+
+  if (*numflag == 1)
+    Change2CNumbering(*nvtxs, xadj, adjncy);
+
+  SetUpGraph(&graph, OP_KMETIS, *nvtxs, *ncon, xadj, adjncy, vwgt, adjwgt, *wgtflag);
+
+  if (options[0] == 0) {  /* Use the default parameters */
+    ctrl.CType  = McKMETIS_CTYPE;
+    ctrl.IType  = McKMETIS_ITYPE;
+    ctrl.RType  = McKMETIS_RTYPE;
+    ctrl.dbglvl = McKMETIS_DBGLVL;
+  }
+  else {
+    ctrl.CType  = options[OPTION_CTYPE];
+    ctrl.IType  = options[OPTION_ITYPE];
+    ctrl.RType  = options[OPTION_RTYPE];
+    ctrl.dbglvl = options[OPTION_DBGLVL];
+  }
+  ctrl.optype = OP_KMETIS;
+  ctrl.CoarsenTo = amax((*nvtxs)/(20*log2(*nparts)), 30*(*nparts));
+
+  ctrl.nmaxvwgt = 1.5/(1.0*ctrl.CoarsenTo);
+
+  InitRandom(-1);
+
+  AllocateWorkSpace(&ctrl, &graph, *nparts);
+
+  IFSET(ctrl.dbglvl, DBG_TIME, InitTimers(&ctrl));
+  IFSET(ctrl.dbglvl, DBG_TIME, starttimer(ctrl.TotalTmr));
+
+  *edgecut = MCMlevelKWayPartitioning(&ctrl, &graph, *nparts, part, rubvec);
+
+  IFSET(ctrl.dbglvl, DBG_TIME, stoptimer(ctrl.TotalTmr));
+  IFSET(ctrl.dbglvl, DBG_TIME, PrintTimers(&ctrl));
+
+  FreeWorkSpace(&ctrl, &graph);
+
+  if (*numflag == 1)
+    Change2FNumbering(*nvtxs, xadj, adjncy, part);
+}
+
+
+/*************************************************************************
+* This function takes a graph and produces a bisection of it
+**************************************************************************/
+int MCMlevelKWayPartitioning(CtrlType *ctrl, GraphType *graph, int nparts, idxtype *part, 
+      float *rubvec)
+{
+  int i, j, nvtxs;
+  GraphType *cgraph;
+  int options[10], edgecut;
+
+  cgraph = MCCoarsen2Way(ctrl, graph);
+
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->InitPartTmr));
+  MocAllocateKWayPartitionMemory(ctrl, cgraph, nparts);
+
+  options[0] = 1; 
+  options[OPTION_CTYPE] = MATCH_SBHEM_INFNORM;
+  options[OPTION_ITYPE] = IPART_RANDOM;
+  options[OPTION_RTYPE] = RTYPE_FM;
+  options[OPTION_DBGLVL] = 0;
+
+  /* Determine what you will use as the initial partitioner, based on tolerances */
+  for (i=0; i<graph->ncon; i++) {
+    if (rubvec[i] > 1.2)
+      break;
+  }
+  if (i == graph->ncon)
+    METIS_mCPartGraphRecursiveInternal(&cgraph->nvtxs, &cgraph->ncon, 
+          cgraph->xadj, cgraph->adjncy, cgraph->nvwgt, cgraph->adjwgt, &nparts, 
+          options, &edgecut, cgraph->where);
+  else
+    METIS_mCHPartGraphRecursiveInternal(&cgraph->nvtxs, &cgraph->ncon, 
+          cgraph->xadj, cgraph->adjncy, cgraph->nvwgt, cgraph->adjwgt, &nparts, 
+          rubvec, options, &edgecut, cgraph->where);
+
+
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->InitPartTmr));
+  IFSET(ctrl->dbglvl, DBG_IPART, printf("Initial %d-way partitioning cut: %d\n", nparts, edgecut));
+
+  IFSET(ctrl->dbglvl, DBG_KWAYPINFO, ComputePartitionInfo(cgraph, nparts, cgraph->where));
+
+  MocRefineKWayHorizontal(ctrl, graph, cgraph, nparts, rubvec);
+
+  idxcopy(graph->nvtxs, graph->where, part);
+
+  GKfree(&graph->nvwgt, &graph->npwgts, &graph->gdata, &graph->rdata, LTERM);
+
+  return graph->mincut;
+
+}
+
diff --git a/contrib/Metis/mkwayfmh.c b/contrib/Metis/mkwayfmh.c
new file mode 100644
index 0000000000..fe0ee41b05
--- /dev/null
+++ b/contrib/Metis/mkwayfmh.c
@@ -0,0 +1,677 @@
+/*
+ * mkwayfmh.c
+ *
+ * This file contains code that implements the multilevel k-way refinement
+ *
+ * Started 7/28/97
+ * George
+ *
+ * $Id: mkwayfmh.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+
+
+/*************************************************************************
+* This function performs k-way refinement
+**************************************************************************/
+void MCRandom_KWayEdgeRefineHorizontal(CtrlType *ctrl, GraphType *graph, int nparts, 
+       float *orgubvec, int npasses)
+{
+  int i, ii, iii, j, jj, k, l, pass, nvtxs, ncon, nmoves, nbnd, myndegrees, same; 
+  int from, me, to, oldcut, gain;
+  idxtype *xadj, *adjncy, *adjwgt;
+  idxtype *where, *perm, *bndptr, *bndind;
+  EDegreeType *myedegrees;
+  RInfoType *myrinfo;
+  float *npwgts, *nvwgt, *minwgt, *maxwgt, maxlb, minlb, ubvec[MAXNCON], tvec[MAXNCON];
+
+  nvtxs = graph->nvtxs;
+  ncon = graph->ncon;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  bndptr = graph->bndptr;
+  bndind = graph->bndind;
+
+  where = graph->where;
+  npwgts = graph->npwgts;
+  
+  /* Setup the weight intervals of the various subdomains */
+  minwgt =  fwspacemalloc(ctrl, nparts*ncon);
+  maxwgt = fwspacemalloc(ctrl, nparts*ncon);
+
+  /* See if the orgubvec consists of identical constraints */
+  maxlb = minlb = orgubvec[0];
+  for (i=1; i<ncon; i++) {
+    minlb = (orgubvec[i] < minlb ? orgubvec[i] : minlb);
+    maxlb = (orgubvec[i] > maxlb ? orgubvec[i] : maxlb);
+  }
+  same = (fabs(maxlb-minlb) < .01 ? 1 : 0);
+
+
+  /* Let's not get very optimistic. Let Balancing do the work */
+  ComputeHKWayLoadImbalance(ncon, nparts, npwgts, ubvec);
+  for (i=0; i<ncon; i++)
+    ubvec[i] = amax(ubvec[i], orgubvec[i]);
+
+  if (!same) {
+    for (i=0; i<nparts; i++) {
+      for (j=0; j<ncon; j++) {
+        maxwgt[i*ncon+j] = ubvec[j]/nparts;
+        minwgt[i*ncon+j] = 1.0/(ubvec[j]*nparts);
+      }
+    }
+  }
+  else {
+    maxlb = ubvec[0];
+    for (i=1; i<ncon; i++) 
+      maxlb = (ubvec[i] > maxlb ? ubvec[i] : maxlb);
+
+    for (i=0; i<nparts; i++) {
+      for (j=0; j<ncon; j++) {
+        maxwgt[i*ncon+j] = maxlb/nparts;
+        minwgt[i*ncon+j] = 1.0/(maxlb*nparts);
+      }
+    }
+  }
+
+
+  perm = idxwspacemalloc(ctrl, nvtxs);
+
+  if (ctrl->dbglvl&DBG_REFINE) {
+    printf("Partitions: [%5.4f %5.4f], Nv-Nb[%6d %6d]. Cut: %6d, LB: ",
+            npwgts[samin(ncon*nparts, npwgts)], npwgts[samax(ncon*nparts, npwgts)], 
+            graph->nvtxs, graph->nbnd, graph->mincut);
+    ComputeHKWayLoadImbalance(ncon, nparts, npwgts, tvec);
+    for (i=0; i<ncon; i++)
+      printf("%.3f ", tvec[i]);
+    printf("\n");
+  }
+
+  for (pass=0; pass<npasses; pass++) {
+    ASSERT(ComputeCut(graph, where) == graph->mincut);
+
+    oldcut = graph->mincut;
+    nbnd = graph->nbnd;
+
+    RandomPermute(nbnd, perm, 1);
+    for (nmoves=iii=0; iii<graph->nbnd; iii++) {
+      ii = perm[iii];
+      if (ii >= nbnd)
+        continue;
+      i = bndind[ii];
+
+      myrinfo = graph->rinfo+i;
+
+      if (myrinfo->ed >= myrinfo->id) { /* Total ED is too high */
+        from = where[i];
+        nvwgt = graph->nvwgt+i*ncon;
+
+        if (myrinfo->id > 0 && AreAllHVwgtsBelow(ncon, 1.0, npwgts+from*ncon, -1.0, nvwgt, minwgt+from*ncon)) 
+          continue;   /* This cannot be moved! */
+
+        myedegrees = myrinfo->edegrees;
+        myndegrees = myrinfo->ndegrees;
+
+        for (k=0; k<myndegrees; k++) {
+          to = myedegrees[k].pid;
+          gain = myedegrees[k].ed - myrinfo->id; 
+          if (gain >= 0 && 
+              (AreAllHVwgtsBelow(ncon, 1.0, npwgts+to*ncon, 1.0, nvwgt, maxwgt+to*ncon) ||
+               IsHBalanceBetterFT(ncon, nparts, npwgts+from*ncon, npwgts+to*ncon, nvwgt, ubvec)))
+            break;
+        }
+        if (k == myndegrees)
+          continue;  /* break out if you did not find a candidate */
+
+        for (j=k+1; j<myndegrees; j++) {
+          to = myedegrees[j].pid;
+          if ((myedegrees[j].ed > myedegrees[k].ed &&
+               (AreAllHVwgtsBelow(ncon, 1.0, npwgts+to*ncon, 1.0, nvwgt, maxwgt+to*ncon) || 
+               IsHBalanceBetterFT(ncon, nparts, npwgts+from*ncon, npwgts+to*ncon, nvwgt, ubvec))) ||
+              (myedegrees[j].ed == myedegrees[k].ed && 
+               IsHBalanceBetterTT(ncon, nparts, npwgts+myedegrees[k].pid*ncon, npwgts+to*ncon, nvwgt, ubvec)))
+            k = j;
+        }
+
+        to = myedegrees[k].pid;
+
+        if (myedegrees[k].ed-myrinfo->id == 0 
+            && !IsHBalanceBetterFT(ncon, nparts, npwgts+from*ncon, npwgts+to*ncon, nvwgt, ubvec)
+            && AreAllHVwgtsBelow(ncon, 1.0, npwgts+from*ncon, 0.0, npwgts+from*ncon, maxwgt+from*ncon)) 
+          continue;
+
+        /*=====================================================================
+        * If we got here, we can now move the vertex from 'from' to 'to' 
+        *======================================================================*/
+        graph->mincut -= myedegrees[k].ed-myrinfo->id;
+
+        IFSET(ctrl->dbglvl, DBG_MOVEINFO, printf("\t\tMoving %6d to %3d. Gain: %4d. Cut: %6d\n", i, to, myedegrees[k].ed-myrinfo->id, graph->mincut));
+
+        /* Update where, weight, and ID/ED information of the vertex you moved */
+        saxpy(ncon, 1.0, nvwgt, 1, npwgts+to*ncon, 1);
+        saxpy(ncon, -1.0, nvwgt, 1, npwgts+from*ncon, 1);
+        where[i] = to;
+        myrinfo->ed += myrinfo->id-myedegrees[k].ed;
+        SWAP(myrinfo->id, myedegrees[k].ed, j);
+        if (myedegrees[k].ed == 0) 
+          myedegrees[k] = myedegrees[--myrinfo->ndegrees];
+        else
+          myedegrees[k].pid = from;
+
+        if (myrinfo->ed-myrinfo->id < 0)
+          BNDDelete(nbnd, bndind, bndptr, i);
+
+        /* Update the degrees of adjacent vertices */
+        for (j=xadj[i]; j<xadj[i+1]; j++) {
+          ii = adjncy[j];
+          me = where[ii];
+
+          myrinfo = graph->rinfo+ii;
+          if (myrinfo->edegrees == NULL) {
+            myrinfo->edegrees = ctrl->wspace.edegrees+ctrl->wspace.cdegree;
+            ctrl->wspace.cdegree += xadj[ii+1]-xadj[ii];
+          }
+          myedegrees = myrinfo->edegrees;
+
+          ASSERT(CheckRInfo(myrinfo));
+
+          if (me == from) {
+            INC_DEC(myrinfo->ed, myrinfo->id, adjwgt[j]);
+
+            if (myrinfo->ed-myrinfo->id >= 0 && bndptr[ii] == -1)
+              BNDInsert(nbnd, bndind, bndptr, ii);
+          }
+          else if (me == to) {
+            INC_DEC(myrinfo->id, myrinfo->ed, adjwgt[j]);
+
+            if (myrinfo->ed-myrinfo->id < 0 && bndptr[ii] != -1)
+              BNDDelete(nbnd, bndind, bndptr, ii);
+          }
+
+          /* Remove contribution from the .ed of 'from' */
+          if (me != from) {
+            for (k=0; k<myrinfo->ndegrees; k++) {
+              if (myedegrees[k].pid == from) {
+                if (myedegrees[k].ed == adjwgt[j])
+                  myedegrees[k] = myedegrees[--myrinfo->ndegrees];
+                else
+                  myedegrees[k].ed -= adjwgt[j];
+                break;
+              }
+            }
+          }
+
+          /* Add contribution to the .ed of 'to' */
+          if (me != to) {
+            for (k=0; k<myrinfo->ndegrees; k++) {
+              if (myedegrees[k].pid == to) {
+                myedegrees[k].ed += adjwgt[j];
+                break;
+              }
+            }
+            if (k == myrinfo->ndegrees) {
+              myedegrees[myrinfo->ndegrees].pid = to;
+              myedegrees[myrinfo->ndegrees++].ed = adjwgt[j];
+            }
+          }
+
+          ASSERT(myrinfo->ndegrees <= xadj[ii+1]-xadj[ii]);
+          ASSERT(CheckRInfo(myrinfo));
+
+        }
+        nmoves++;
+      }
+    }
+
+    graph->nbnd = nbnd;
+
+    if (ctrl->dbglvl&DBG_REFINE) {
+      printf("\t [%5.4f %5.4f], Nb: %6d, Nmoves: %5d, Cut: %6d, LB: ",
+              npwgts[samin(ncon*nparts, npwgts)], npwgts[samax(ncon*nparts, npwgts)], 
+              nbnd, nmoves, graph->mincut);
+      ComputeHKWayLoadImbalance(ncon, nparts, npwgts, tvec);
+      for (i=0; i<ncon; i++)
+        printf("%.3f ", tvec[i]);
+      printf("\n");
+    }
+
+    if (graph->mincut == oldcut)
+      break;
+  }
+
+  fwspacefree(ctrl, ncon*nparts);
+  fwspacefree(ctrl, ncon*nparts);
+  idxwspacefree(ctrl, nvtxs);
+}
+
+
+
+/*************************************************************************
+* This function performs k-way refinement
+**************************************************************************/
+void MCGreedy_KWayEdgeBalanceHorizontal(CtrlType *ctrl, GraphType *graph, int nparts, 
+       float *ubvec, int npasses)
+{
+  int i, ii, iii, j, jj, k, l, pass, nvtxs, ncon, nbnd, myndegrees, oldgain, gain, nmoves; 
+  int from, me, to, oldcut;
+  idxtype *xadj, *adjncy, *adjwgt;
+  idxtype *where, *perm, *bndptr, *bndind, *moved;
+  EDegreeType *myedegrees;
+  RInfoType *myrinfo;
+  PQueueType queue;
+  float *npwgts, *nvwgt, *minwgt, *maxwgt, tvec[MAXNCON];
+
+  nvtxs = graph->nvtxs;
+  ncon = graph->ncon;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  bndind = graph->bndind;
+  bndptr = graph->bndptr;
+
+  where = graph->where;
+  npwgts = graph->npwgts;
+  
+  /* Setup the weight intervals of the various subdomains */
+  minwgt =  fwspacemalloc(ctrl, ncon*nparts);
+  maxwgt = fwspacemalloc(ctrl, ncon*nparts);
+
+  for (i=0; i<nparts; i++) {
+    for (j=0; j<ncon; j++) {
+      maxwgt[i*ncon+j] = ubvec[j]/nparts;
+      minwgt[i*ncon+j] = 1.0/(ubvec[j]*nparts);
+    }
+  }
+
+  perm = idxwspacemalloc(ctrl, nvtxs);
+  moved = idxwspacemalloc(ctrl, nvtxs);
+
+  PQueueInit(ctrl, &queue, nvtxs, graph->adjwgtsum[idxamax(nvtxs, graph->adjwgtsum)]);
+
+  if (ctrl->dbglvl&DBG_REFINE) {
+    printf("Partitions: [%5.4f %5.4f], Nv-Nb[%6d %6d]. Cut: %6d, LB: ",
+            npwgts[samin(ncon*nparts, npwgts)], npwgts[samax(ncon*nparts, npwgts)], 
+            graph->nvtxs, graph->nbnd, graph->mincut);
+    ComputeHKWayLoadImbalance(ncon, nparts, npwgts, tvec);
+    for (i=0; i<ncon; i++)
+      printf("%.3f ", tvec[i]);
+    printf("[B]\n");
+  }
+
+
+  for (pass=0; pass<npasses; pass++) {
+    ASSERT(ComputeCut(graph, where) == graph->mincut);
+
+    /* Check to see if things are out of balance, given the tolerance */
+    if (MocIsHBalanced(ncon, nparts, npwgts, ubvec))
+      break;
+
+    PQueueReset(&queue);
+    idxset(nvtxs, -1, moved);
+
+    oldcut = graph->mincut;
+    nbnd = graph->nbnd;
+
+    RandomPermute(nbnd, perm, 1);
+    for (ii=0; ii<nbnd; ii++) {
+      i = bndind[perm[ii]];
+      PQueueInsert(&queue, i, graph->rinfo[i].ed - graph->rinfo[i].id);
+      moved[i] = 2;
+    }
+
+    nmoves = 0;
+    for (;;) {
+      if ((i = PQueueGetMax(&queue)) == -1) 
+        break;
+      moved[i] = 1;
+
+      myrinfo = graph->rinfo+i;
+      from = where[i];
+      nvwgt = graph->nvwgt+i*ncon;
+
+      if (AreAllHVwgtsBelow(ncon, 1.0, npwgts+from*ncon, -1.0, nvwgt, minwgt+from*ncon))
+        continue;   /* This cannot be moved! */
+
+      myedegrees = myrinfo->edegrees;
+      myndegrees = myrinfo->ndegrees;
+
+      for (k=0; k<myndegrees; k++) {
+        to = myedegrees[k].pid;
+        if (IsHBalanceBetterFT(ncon, nparts, npwgts+from*ncon, npwgts+to*ncon, nvwgt, ubvec))
+          break;
+      }
+      if (k == myndegrees) 
+        continue;  /* break out if you did not find a candidate */
+
+      for (j=k+1; j<myndegrees; j++) {
+        to = myedegrees[j].pid;
+        if (IsHBalanceBetterTT(ncon, nparts, npwgts+myedegrees[k].pid*ncon, npwgts+to*ncon, nvwgt, ubvec)) 
+          k = j;
+      }
+
+      to = myedegrees[k].pid;
+
+      j = 0;
+      if (!AreAllHVwgtsBelow(ncon, 1.0, npwgts+from*ncon, 0.0, nvwgt, maxwgt+from*ncon))
+        j++;
+      if (myedegrees[k].ed-myrinfo->id >= 0)
+        j++;
+      if (!AreAllHVwgtsAbove(ncon, 1.0, npwgts+to*ncon, 0.0, nvwgt, minwgt+to*ncon) &&
+          AreAllHVwgtsBelow(ncon, 1.0, npwgts+to*ncon, 1.0, nvwgt, maxwgt+to*ncon))
+        j++;
+      if (j == 0)
+        continue;
+
+/* DELETE
+      if (myedegrees[k].ed-myrinfo->id < 0 && 
+          AreAllHVwgtsBelow(ncon, 1.0, npwgts+from*ncon, 0.0, nvwgt, maxwgt+from*ncon) &&
+          AreAllHVwgtsAbove(ncon, 1.0, npwgts+to*ncon, 0.0, nvwgt, minwgt+to*ncon) &&
+          AreAllHVwgtsBelow(ncon, 1.0, npwgts+to*ncon, 1.0, nvwgt, maxwgt+to*ncon))
+        continue;
+*/
+      /*=====================================================================
+      * If we got here, we can now move the vertex from 'from' to 'to' 
+      *======================================================================*/
+      graph->mincut -= myedegrees[k].ed-myrinfo->id;
+
+      IFSET(ctrl->dbglvl, DBG_MOVEINFO, printf("\t\tMoving %6d to %3d. Gain: %4d. Cut: %6d\n", i, to, myedegrees[k].ed-myrinfo->id, graph->mincut));
+
+      /* Update where, weight, and ID/ED information of the vertex you moved */
+      saxpy(ncon, 1.0, nvwgt, 1, npwgts+to*ncon, 1);
+      saxpy(ncon, -1.0, nvwgt, 1, npwgts+from*ncon, 1);
+      where[i] = to;
+      myrinfo->ed += myrinfo->id-myedegrees[k].ed;
+      SWAP(myrinfo->id, myedegrees[k].ed, j);
+      if (myedegrees[k].ed == 0) 
+        myedegrees[k] = myedegrees[--myrinfo->ndegrees];
+      else
+        myedegrees[k].pid = from;
+
+      if (myrinfo->ed == 0)
+        BNDDelete(nbnd, bndind, bndptr, i);
+
+      /* Update the degrees of adjacent vertices */
+      for (j=xadj[i]; j<xadj[i+1]; j++) {
+        ii = adjncy[j];
+        me = where[ii];
+
+        myrinfo = graph->rinfo+ii;
+        if (myrinfo->edegrees == NULL) {
+          myrinfo->edegrees = ctrl->wspace.edegrees+ctrl->wspace.cdegree;
+          ctrl->wspace.cdegree += xadj[ii+1]-xadj[ii];
+        }
+        myedegrees = myrinfo->edegrees;
+
+        ASSERT(CheckRInfo(myrinfo));
+
+        oldgain = (myrinfo->ed-myrinfo->id);
+
+        if (me == from) {
+          INC_DEC(myrinfo->ed, myrinfo->id, adjwgt[j]);
+
+          if (myrinfo->ed > 0 && bndptr[ii] == -1)
+            BNDInsert(nbnd, bndind, bndptr, ii);
+        }
+        else if (me == to) {
+          INC_DEC(myrinfo->id, myrinfo->ed, adjwgt[j]);
+
+          if (myrinfo->ed == 0 && bndptr[ii] != -1)
+            BNDDelete(nbnd, bndind, bndptr, ii);
+        }
+
+        /* Remove contribution from the .ed of 'from' */
+        if (me != from) {
+          for (k=0; k<myrinfo->ndegrees; k++) {
+            if (myedegrees[k].pid == from) {
+              if (myedegrees[k].ed == adjwgt[j])
+                myedegrees[k] = myedegrees[--myrinfo->ndegrees];
+              else
+                myedegrees[k].ed -= adjwgt[j];
+              break;
+            }
+          }
+        }
+
+        /* Add contribution to the .ed of 'to' */
+        if (me != to) {
+          for (k=0; k<myrinfo->ndegrees; k++) {
+            if (myedegrees[k].pid == to) {
+              myedegrees[k].ed += adjwgt[j];
+              break;
+            }
+          }
+          if (k == myrinfo->ndegrees) {
+            myedegrees[myrinfo->ndegrees].pid = to;
+            myedegrees[myrinfo->ndegrees++].ed = adjwgt[j];
+          }
+        }
+
+
+        /* Update the queue */
+        if (me == to || me == from) { 
+          gain = myrinfo->ed-myrinfo->id;
+          if (moved[ii] == 2) {
+            if (myrinfo->ed > 0)
+              PQueueUpdate(&queue, ii, oldgain, gain);
+            else {
+              PQueueDelete(&queue, ii, oldgain);
+              moved[ii] = -1;
+            }
+          }
+          else if (moved[ii] == -1 && myrinfo->ed > 0) {
+            PQueueInsert(&queue, ii, gain);
+            moved[ii] = 2;
+          }
+        } 
+
+        ASSERT(myrinfo->ndegrees <= xadj[ii+1]-xadj[ii]);
+        ASSERT(CheckRInfo(myrinfo));
+      }
+      nmoves++;
+    }
+
+    graph->nbnd = nbnd;
+
+    if (ctrl->dbglvl&DBG_REFINE) {
+      printf("\t [%5.4f %5.4f], Nb: %6d, Nmoves: %5d, Cut: %6d, LB: ",
+              npwgts[samin(ncon*nparts, npwgts)], npwgts[samax(ncon*nparts, npwgts)], 
+              nbnd, nmoves, graph->mincut);
+      ComputeHKWayLoadImbalance(ncon, nparts, npwgts, tvec);
+      for (i=0; i<ncon; i++)
+        printf("%.3f ", tvec[i]);
+      printf("\n");
+    }
+
+    if (nmoves == 0)
+      break;
+  }
+
+  PQueueFree(ctrl, &queue);
+
+  fwspacefree(ctrl, ncon*nparts);
+  fwspacefree(ctrl, ncon*nparts);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+
+}
+
+
+
+
+
+/*************************************************************************
+* This function checks if the vertex weights of two vertices are below 
+* a given set of values
+**************************************************************************/
+int AreAllHVwgtsBelow(int ncon, float alpha, float *vwgt1, float beta, float *vwgt2, float *limit)
+{
+  int i;
+
+  for (i=0; i<ncon; i++)
+    if (alpha*vwgt1[i] + beta*vwgt2[i] > limit[i])
+      return 0;
+
+  return 1;
+}
+
+
+
+/*************************************************************************
+* This function checks if the vertex weights of two vertices are above 
+* a given set of values
+**************************************************************************/
+int AreAllHVwgtsAbove(int ncon, float alpha, float *vwgt1, float beta, float *vwgt2, float *limit)
+{
+  int i;
+
+  for (i=0; i<ncon; i++)
+    if (alpha*vwgt1[i] + beta*vwgt2[i] < limit[i])
+      return 0;
+
+  return 1;
+}
+
+
+/*************************************************************************
+* This function computes the load imbalance over all the constrains
+* For now assume that we just want balanced partitionings
+**************************************************************************/ 
+void ComputeHKWayLoadImbalance(int ncon, int nparts, float *npwgts, float *lbvec)
+{
+  int i, j;
+  float max;
+
+  for (i=0; i<ncon; i++) {
+    max = 0.0;
+    for (j=0; j<nparts; j++) {
+      if (npwgts[j*ncon+i] > max)
+        max = npwgts[j*ncon+i];
+    }
+
+    lbvec[i] = max*nparts;
+  }
+}
+
+
+/*************************************************************************
+* This function determines if a partitioning is horizontally balanced
+**************************************************************************/
+int MocIsHBalanced(int ncon, int nparts, float *npwgts, float *ubvec)
+{
+  int i, j;
+  float max;
+
+  for (i=0; i<ncon; i++) {
+    max = 0.0;
+    for (j=0; j<nparts; j++) {
+      if (npwgts[j*ncon+i] > max)
+        max = npwgts[j*ncon+i];
+    }
+
+    if (ubvec[i] < max*nparts)
+      return 0;
+  }
+
+  return 1;
+}
+
+
+
+
+
+/*************************************************************************
+* This function checks if the pairwise balance of the between the two 
+* partitions will improve by moving the vertex v from pfrom to pto,
+* subject to the target partition weights of tfrom, and tto respectively
+**************************************************************************/
+int IsHBalanceBetterFT(int ncon, int nparts, float *pfrom, float *pto, float *vwgt, float *ubvec)
+{
+  int i, j, k;
+  float blb1=0.0, alb1=0.0, sblb=0.0, salb=0.0;
+  float blb2=0.0, alb2=0.0;
+  float temp;
+
+  for (i=0; i<ncon; i++) {
+    temp = amax(pfrom[i], pto[i])*nparts/ubvec[i];
+    if (blb1 < temp) {
+      blb2 = blb1;
+      blb1 = temp;
+    }
+    else if (blb2 < temp)
+      blb2 = temp;
+    sblb += temp;
+
+    temp = amax(pfrom[i]-vwgt[i], pto[i]+vwgt[i])*nparts/ubvec[i];
+    if (alb1 < temp) {
+      alb2 = alb1;
+      alb1 = temp;
+    }
+    else if (alb2 < temp)
+      alb2 = temp;
+    salb += temp;
+  }
+
+  if (alb1 < blb1)
+    return 1;
+  if (blb1 < alb1)
+    return 0;
+  if (alb2 < blb2)
+    return 1;
+  if (blb2 < alb2)
+    return 0;
+  
+  return salb < sblb;
+
+}
+
+
+
+
+/*************************************************************************
+* This function checks if it will be better to move a vertex to pt2 than
+* to pt1 subject to their target weights of tt1 and tt2, respectively
+* This routine takes into account the weight of the vertex in question
+**************************************************************************/
+int IsHBalanceBetterTT(int ncon, int nparts, float *pt1, float *pt2, float *vwgt, float *ubvec)
+{
+  int i;
+  float m11=0.0, m12=0.0, m21=0.0, m22=0.0, sm1=0.0, sm2=0.0, temp;
+
+  for (i=0; i<ncon; i++) {
+    temp = (pt1[i]+vwgt[i])*nparts/ubvec[i];
+    if (m11 < temp) {
+      m12 = m11;
+      m11 = temp;
+    }
+    else if (m12 < temp)
+      m12 = temp;
+    sm1 += temp;
+
+    temp = (pt2[i]+vwgt[i])*nparts/ubvec[i];
+    if (m21 < temp) {
+      m22 = m21;
+      m21 = temp;
+    }
+    else if (m22 < temp)
+      m22 = temp;
+    sm2 += temp;
+  }
+
+  if (m21 < m11)
+    return 1;
+  if (m21 > m11)
+    return 0;
+  if (m22 < m12)
+    return 1;
+  if (m22 > m12)
+    return 0;
+
+  return sm2 < sm1;  
+}
+
diff --git a/contrib/Metis/mkwayrefine.c b/contrib/Metis/mkwayrefine.c
new file mode 100644
index 0000000000..24000aec1a
--- /dev/null
+++ b/contrib/Metis/mkwayrefine.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * mkwayrefine.c
+ *
+ * This file contains the driving routines for multilevel k-way refinement
+ *
+ * Started 7/28/97
+ * George
+ *
+ * $Id: mkwayrefine.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function is the entry point of refinement
+**************************************************************************/
+void MocRefineKWayHorizontal(CtrlType *ctrl, GraphType *orggraph, GraphType *graph, int nparts, 
+       float *ubvec)
+{
+
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->UncoarsenTmr));
+
+  /* Compute the parameters of the coarsest graph */
+  MocComputeKWayPartitionParams(ctrl, graph, nparts);
+
+  for (;;) {
+    IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->RefTmr));
+
+    if (!MocIsHBalanced(graph->ncon, nparts, graph->npwgts, ubvec)) {
+      MocComputeKWayBalanceBoundary(ctrl, graph, nparts);
+      MCGreedy_KWayEdgeBalanceHorizontal(ctrl, graph, nparts, ubvec, 4); 
+      ComputeKWayBoundary(ctrl, graph, nparts);
+    }
+
+    MCRandom_KWayEdgeRefineHorizontal(ctrl, graph, nparts, ubvec, 10); 
+
+    IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->RefTmr));
+
+    if (graph == orggraph)
+      break;
+
+    graph = graph->finer;
+    IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->ProjectTmr));
+    MocProjectKWayPartition(ctrl, graph, nparts);
+    IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->ProjectTmr));
+  }
+
+  if (!MocIsHBalanced(graph->ncon, nparts, graph->npwgts, ubvec)) {
+    MocComputeKWayBalanceBoundary(ctrl, graph, nparts);
+    MCGreedy_KWayEdgeBalanceHorizontal(ctrl, graph, nparts, ubvec, 4); 
+    ComputeKWayBoundary(ctrl, graph, nparts);
+    MCRandom_KWayEdgeRefineHorizontal(ctrl, graph, nparts, ubvec, 10); 
+  }
+
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->UncoarsenTmr));
+}
+
+
+
+
+/*************************************************************************
+* This function allocates memory for k-way edge refinement
+**************************************************************************/
+void MocAllocateKWayPartitionMemory(CtrlType *ctrl, GraphType *graph, int nparts)
+{
+  int nvtxs, ncon, pad64;
+
+  nvtxs = graph->nvtxs;
+  ncon = graph->ncon;
+
+  pad64 = (3*nvtxs)%2;
+
+  graph->rdata = idxmalloc(3*nvtxs+(sizeof(RInfoType)/sizeof(idxtype))*nvtxs+pad64, "AllocateKWayPartitionMemory: rdata");
+  graph->where          = graph->rdata;
+  graph->bndptr         = graph->rdata + nvtxs;
+  graph->bndind         = graph->rdata + 2*nvtxs;
+  graph->rinfo          = (RInfoType *)(graph->rdata + 3*nvtxs + pad64);
+
+  graph->npwgts         = fmalloc(ncon*nparts, "MocAllocateKWayPartitionMemory: npwgts");
+}
+
+
+/*************************************************************************
+* This function computes the initial id/ed 
+**************************************************************************/
+void MocComputeKWayPartitionParams(CtrlType *ctrl, GraphType *graph, int nparts)
+{
+  int i, j, k, l, nvtxs, ncon, nbnd, mincut, me, other;
+  idxtype *xadj, *adjncy, *adjwgt, *where, *bndind, *bndptr;
+  RInfoType *rinfo, *myrinfo;
+  EDegreeType *myedegrees;
+  float *nvwgt, *npwgts;
+
+  nvtxs = graph->nvtxs;
+  ncon = graph->ncon;
+  xadj = graph->xadj;
+  nvwgt = graph->nvwgt;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  where = graph->where;
+  npwgts = sset(ncon*nparts, 0.0, graph->npwgts);
+  bndind = graph->bndind;
+  bndptr = idxset(nvtxs, -1, graph->bndptr);
+  rinfo = graph->rinfo;
+
+
+  /*------------------------------------------------------------
+  / Compute now the id/ed degrees
+  /------------------------------------------------------------*/
+  ctrl->wspace.cdegree = 0;
+  nbnd = mincut = 0;
+  for (i=0; i<nvtxs; i++) {
+    me = where[i];
+    saxpy(ncon, 1.0, nvwgt+i*ncon, 1, npwgts+me*ncon, 1);
+
+    myrinfo = rinfo+i;
+    myrinfo->id = myrinfo->ed = myrinfo->ndegrees = 0;
+    myrinfo->edegrees = NULL;
+
+    for (j=xadj[i]; j<xadj[i+1]; j++) {
+      if (me != where[adjncy[j]])
+        myrinfo->ed += adjwgt[j];
+    }
+    myrinfo->id = graph->adjwgtsum[i] - myrinfo->ed;
+
+    if (myrinfo->ed > 0) 
+      mincut += myrinfo->ed;
+
+    if (myrinfo->ed-myrinfo->id >= 0)
+      BNDInsert(nbnd, bndind, bndptr, i);
+
+    /* Time to compute the particular external degrees */
+    if (myrinfo->ed > 0) { 
+      myedegrees = myrinfo->edegrees = ctrl->wspace.edegrees+ctrl->wspace.cdegree;
+      ctrl->wspace.cdegree += xadj[i+1]-xadj[i];
+
+      for (j=xadj[i]; j<xadj[i+1]; j++) {
+        other = where[adjncy[j]];
+        if (me != other) {
+          for (k=0; k<myrinfo->ndegrees; k++) {
+            if (myedegrees[k].pid == other) {
+              myedegrees[k].ed += adjwgt[j];
+              break;
+            }
+          }
+          if (k == myrinfo->ndegrees) {
+            myedegrees[myrinfo->ndegrees].pid = other;
+            myedegrees[myrinfo->ndegrees++].ed = adjwgt[j];
+          }
+        }
+      }
+
+      ASSERT(myrinfo->ndegrees <= xadj[i+1]-xadj[i]);
+    }
+  }
+
+  graph->mincut = mincut/2;
+  graph->nbnd = nbnd;
+
+}
+
+
+
+/*************************************************************************
+* This function projects a partition, and at the same time computes the
+* parameters for refinement.
+**************************************************************************/
+void MocProjectKWayPartition(CtrlType *ctrl, GraphType *graph, int nparts)
+{
+  int i, j, k, nvtxs, nbnd, me, other, istart, iend, ndegrees;
+  idxtype *xadj, *adjncy, *adjwgt, *adjwgtsum;
+  idxtype *cmap, *where, *bndptr, *bndind;
+  idxtype *cwhere;
+  GraphType *cgraph;
+  RInfoType *crinfo, *rinfo, *myrinfo;
+  EDegreeType *myedegrees;
+  idxtype *htable;
+
+  cgraph = graph->coarser;
+  cwhere = cgraph->where;
+  crinfo = cgraph->rinfo;
+
+  nvtxs = graph->nvtxs;
+  cmap = graph->cmap;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+  adjwgtsum = graph->adjwgtsum;
+
+  MocAllocateKWayPartitionMemory(ctrl, graph, nparts);
+  where = graph->where;
+  rinfo = graph->rinfo;
+  bndind = graph->bndind;
+  bndptr = idxset(nvtxs, -1, graph->bndptr);
+
+  /* Go through and project partition and compute id/ed for the nodes */
+  for (i=0; i<nvtxs; i++) {
+    k = cmap[i];
+    where[i] = cwhere[k];
+    cmap[i] = crinfo[k].ed;  /* For optimization */
+  }
+
+  htable = idxset(nparts, -1, idxwspacemalloc(ctrl, nparts));
+
+  ctrl->wspace.cdegree = 0;
+  for (nbnd=0, i=0; i<nvtxs; i++) {
+    me = where[i];
+
+    myrinfo = rinfo+i;
+    myrinfo->id = myrinfo->ed = myrinfo->ndegrees = 0;
+    myrinfo->edegrees = NULL;
+
+    myrinfo->id = adjwgtsum[i];
+
+    if (cmap[i] > 0) { /* If it is an interface node. Note cmap[i] = crinfo[cmap[i]].ed */
+      istart = xadj[i];
+      iend = xadj[i+1];
+
+      myedegrees = myrinfo->edegrees = ctrl->wspace.edegrees+ctrl->wspace.cdegree;
+      ctrl->wspace.cdegree += iend-istart;
+
+      ndegrees = 0;
+      for (j=istart; j<iend; j++) {
+        other = where[adjncy[j]];
+        if (me != other) {
+          myrinfo->ed += adjwgt[j];
+          if ((k = htable[other]) == -1) {
+            htable[other] = ndegrees;
+            myedegrees[ndegrees].pid = other;
+            myedegrees[ndegrees++].ed = adjwgt[j];
+          }
+          else {
+            myedegrees[k].ed += adjwgt[j];
+          }
+        }
+      }
+      myrinfo->id -= myrinfo->ed;
+
+      /* Remove space for edegrees if it was interior */
+      if (myrinfo->ed == 0) { 
+        myrinfo->edegrees = NULL;
+        ctrl->wspace.cdegree -= iend-istart;
+      }
+      else {
+        if (myrinfo->ed-myrinfo->id >= 0) 
+          BNDInsert(nbnd, bndind, bndptr, i); 
+
+        myrinfo->ndegrees = ndegrees;
+
+        for (j=0; j<ndegrees; j++)
+          htable[myedegrees[j].pid] = -1;
+      }
+    }
+  }
+
+  scopy(graph->ncon*nparts, cgraph->npwgts, graph->npwgts);
+  graph->mincut = cgraph->mincut;
+  graph->nbnd = nbnd;
+
+  FreeGraph(graph->coarser);
+  graph->coarser = NULL;
+
+  idxwspacefree(ctrl, nparts);
+
+  ASSERT(CheckBnd2(graph));
+
+}
+
+
+
+/*************************************************************************
+* This function computes the boundary definition for balancing
+**************************************************************************/
+void MocComputeKWayBalanceBoundary(CtrlType *ctrl, GraphType *graph, int nparts)
+{
+  int i, nvtxs, nbnd;
+  idxtype *bndind, *bndptr;
+
+  nvtxs = graph->nvtxs;
+  bndind = graph->bndind;
+  bndptr = idxset(nvtxs, -1, graph->bndptr);
+
+
+  /* Compute the new boundary */
+  nbnd = 0;
+  for (i=0; i<nvtxs; i++) {
+    if (graph->rinfo[i].ed > 0) 
+      BNDInsert(nbnd, bndind, bndptr, i);
+  }
+
+  graph->nbnd = nbnd;
+}
+
diff --git a/contrib/Metis/mmatch.c b/contrib/Metis/mmatch.c
new file mode 100644
index 0000000000..ea15ed6c7f
--- /dev/null
+++ b/contrib/Metis/mmatch.c
@@ -0,0 +1,506 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * mmatch.c
+ *
+ * This file contains the code that computes matchings and creates the next
+ * level coarse graph.
+ *
+ * Started 7/23/97
+ * George
+ *
+ * $Id: mmatch.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function finds a matching using the HEM heuristic
+**************************************************************************/
+void MCMatch_RM(CtrlType *ctrl, GraphType *graph)
+{
+  int i, ii, j, k, nvtxs, ncon, cnvtxs, maxidx;
+  idxtype *xadj, *adjncy, *adjwgt;
+  idxtype *match, *cmap, *perm;
+  float *nvwgt;
+
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->MatchTmr));
+
+  nvtxs = graph->nvtxs;
+  ncon = graph->ncon;
+  xadj = graph->xadj;
+  nvwgt = graph->nvwgt;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  cmap = graph->cmap;
+  match = idxset(nvtxs, UNMATCHED, idxwspacemalloc(ctrl, nvtxs));
+
+  perm = idxwspacemalloc(ctrl, nvtxs);
+  RandomPermute(nvtxs, perm, 1);
+
+  cnvtxs = 0;
+  for (ii=0; ii<nvtxs; ii++) {
+    i = perm[ii];
+
+    if (match[i] == UNMATCHED) {  /* Unmatched */
+      maxidx = i;
+
+      /* Find a random matching, subject to maxvwgt constraints */
+      for (j=xadj[i]; j<xadj[i+1]; j++) {
+        k = adjncy[j];
+        if (match[k] == UNMATCHED && AreAllVwgtsBelowFast(ncon, nvwgt+i*ncon, nvwgt+k*ncon, ctrl->nmaxvwgt)) {
+          maxidx = k;
+          break;
+        }
+      }
+
+      cmap[i] = cmap[maxidx] = cnvtxs++;
+      match[i] = maxidx;
+      match[maxidx] = i;
+    }
+  }
+
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->MatchTmr));
+
+  CreateCoarseGraph(ctrl, graph, cnvtxs, match, perm);
+
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+}
+
+
+
+/*************************************************************************
+* This function finds a matching using the HEM heuristic
+**************************************************************************/
+void MCMatch_HEM(CtrlType *ctrl, GraphType *graph)
+{
+  int i, ii, j, k, l, nvtxs, cnvtxs, ncon, maxidx, maxwgt;
+  idxtype *xadj, *adjncy, *adjwgt;
+  idxtype *match, *cmap, *perm;
+  float *nvwgt;
+
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->MatchTmr));
+
+  nvtxs = graph->nvtxs;
+  ncon = graph->ncon;
+  xadj = graph->xadj;
+  nvwgt = graph->nvwgt;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  cmap = graph->cmap;
+  match = idxset(nvtxs, UNMATCHED, idxwspacemalloc(ctrl, nvtxs));
+
+  perm = idxwspacemalloc(ctrl, nvtxs);
+  RandomPermute(nvtxs, perm, 1);
+
+  cnvtxs = 0;
+  for (ii=0; ii<nvtxs; ii++) {
+    i = perm[ii];
+
+    if (match[i] == UNMATCHED) {  /* Unmatched */
+      maxidx = i;
+      maxwgt = 0;
+
+      /* Find a heavy-edge matching, subject to maxvwgt constraints */
+      for (j=xadj[i]; j<xadj[i+1]; j++) {
+        k = adjncy[j];
+        if (match[k] == UNMATCHED && maxwgt <= adjwgt[j] &&
+               AreAllVwgtsBelowFast(ncon, nvwgt+i*ncon, nvwgt+k*ncon, ctrl->nmaxvwgt)) {
+          maxwgt = adjwgt[j];
+          maxidx = adjncy[j];
+        }
+      }
+
+      cmap[i] = cmap[maxidx] = cnvtxs++;
+      match[i] = maxidx;
+      match[maxidx] = i;
+    }
+  }
+
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->MatchTmr));
+
+  CreateCoarseGraph(ctrl, graph, cnvtxs, match, perm);
+
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+}
+
+
+
+/*************************************************************************
+* This function finds a matching using the HEM heuristic
+**************************************************************************/
+void MCMatch_SHEM(CtrlType *ctrl, GraphType *graph)
+{
+  int i, ii, j, k, nvtxs, cnvtxs, ncon, maxidx, maxwgt, avgdegree;
+  idxtype *xadj, *adjncy, *adjwgt;
+  idxtype *match, *cmap, *degrees, *perm, *tperm;
+  float *nvwgt;
+
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->MatchTmr));
+
+  nvtxs = graph->nvtxs;
+  ncon = graph->ncon;
+  xadj = graph->xadj;
+  nvwgt = graph->nvwgt;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  cmap = graph->cmap;
+  match = idxset(nvtxs, UNMATCHED, idxwspacemalloc(ctrl, nvtxs));
+
+  perm = idxwspacemalloc(ctrl, nvtxs);
+  tperm = idxwspacemalloc(ctrl, nvtxs);
+  degrees = idxwspacemalloc(ctrl, nvtxs);
+
+  RandomPermute(nvtxs, tperm, 1);
+  avgdegree = 0.7*(xadj[nvtxs]/nvtxs);
+  for (i=0; i<nvtxs; i++) 
+    degrees[i] = (xadj[i+1]-xadj[i] > avgdegree ? avgdegree : xadj[i+1]-xadj[i]);
+  BucketSortKeysInc(nvtxs, avgdegree, degrees, tperm, perm);
+
+  cnvtxs = 0;
+
+  /* Take care any islands. Islands are matched with non-islands due to coarsening */
+  for (ii=0; ii<nvtxs; ii++) {
+    i = perm[ii];
+
+    if (match[i] == UNMATCHED) {  /* Unmatched */
+      if (xadj[i] < xadj[i+1])
+        break;
+
+      maxidx = i;
+      for (j=nvtxs-1; j>ii; j--) {
+        k = perm[j];
+        if (match[k] == UNMATCHED && xadj[k] < xadj[k+1]) {
+          maxidx = k;
+          break;
+        }
+      }
+
+      cmap[i] = cmap[maxidx] = cnvtxs++;
+      match[i] = maxidx;
+      match[maxidx] = i;
+    }
+  }
+
+  /* Continue with normal matching */
+  for (; ii<nvtxs; ii++) {
+    i = perm[ii];
+
+    if (match[i] == UNMATCHED) {  /* Unmatched */
+      maxidx = i;
+      maxwgt = 0;
+
+      /* Find a heavy-edge matching, subject to maxvwgt constraints */
+      for (j=xadj[i]; j<xadj[i+1]; j++) {
+        k = adjncy[j];
+        if (match[k] == UNMATCHED && maxwgt <= adjwgt[j] &&
+               AreAllVwgtsBelowFast(ncon, nvwgt+i*ncon, nvwgt+k*ncon, ctrl->nmaxvwgt)) {
+          maxwgt = adjwgt[j];
+          maxidx = adjncy[j];
+        }
+      }
+
+      cmap[i] = cmap[maxidx] = cnvtxs++;
+      match[i] = maxidx;
+      match[maxidx] = i;
+    }
+  }
+
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->MatchTmr));
+
+  idxwspacefree(ctrl, nvtxs);  /* degrees */
+  idxwspacefree(ctrl, nvtxs);  /* tperm */
+
+  CreateCoarseGraph(ctrl, graph, cnvtxs, match, perm);
+
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+}
+
+
+
+/*************************************************************************
+* This function finds a matching using the HEM heuristic
+**************************************************************************/
+void MCMatch_SHEBM(CtrlType *ctrl, GraphType *graph, int norm)
+{
+  int i, ii, j, k, nvtxs, cnvtxs, ncon, maxidx, maxwgt, avgdegree;
+  idxtype *xadj, *adjncy, *adjwgt;
+  idxtype *match, *cmap, *degrees, *perm, *tperm;
+  float *nvwgt;
+
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->MatchTmr));
+
+  nvtxs = graph->nvtxs;
+  ncon = graph->ncon;
+  xadj = graph->xadj;
+  nvwgt = graph->nvwgt;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  cmap = graph->cmap;
+  match = idxset(nvtxs, UNMATCHED, idxwspacemalloc(ctrl, nvtxs));
+
+  perm = idxwspacemalloc(ctrl, nvtxs);
+  tperm = idxwspacemalloc(ctrl, nvtxs);
+  degrees = idxwspacemalloc(ctrl, nvtxs);
+
+  RandomPermute(nvtxs, tperm, 1);
+  avgdegree = 0.7*(xadj[nvtxs]/nvtxs);
+  for (i=0; i<nvtxs; i++) 
+    degrees[i] = (xadj[i+1]-xadj[i] > avgdegree ? avgdegree : xadj[i+1]-xadj[i]);
+  BucketSortKeysInc(nvtxs, avgdegree, degrees, tperm, perm);
+
+  cnvtxs = 0;
+
+  /* Take care any islands. Islands are matched with non-islands due to coarsening */
+  for (ii=0; ii<nvtxs; ii++) {
+    i = perm[ii];
+
+    if (match[i] == UNMATCHED) {  /* Unmatched */
+      if (xadj[i] < xadj[i+1])
+        break;
+
+      maxidx = i;
+      for (j=nvtxs-1; j>ii; j--) {
+        k = perm[j];
+        if (match[k] == UNMATCHED && xadj[k] < xadj[k+1]) {
+          maxidx = k;
+          break;
+        }
+      }
+
+      cmap[i] = cmap[maxidx] = cnvtxs++;
+      match[i] = maxidx;
+      match[maxidx] = i;
+    }
+  }
+
+  /* Continue with normal matching */
+  for (; ii<nvtxs; ii++) {
+    i = perm[ii];
+
+    if (match[i] == UNMATCHED) {  /* Unmatched */
+      maxidx = i;
+      maxwgt = -1;
+
+      /* Find a heavy-edge matching, subject to maxvwgt constraints */
+      for (j=xadj[i]; j<xadj[i+1]; j++) {
+        k = adjncy[j];
+
+        if (match[k] == UNMATCHED && 
+            AreAllVwgtsBelowFast(ncon, nvwgt+i*ncon, nvwgt+k*ncon, ctrl->nmaxvwgt) &&
+            (maxwgt < adjwgt[j] || 
+              (maxwgt == adjwgt[j] && 
+               BetterVBalance(ncon, norm, nvwgt+i*ncon, nvwgt+maxidx*ncon, nvwgt+k*ncon) >= 0
+              )
+            )
+           ) {
+          maxwgt = adjwgt[j];
+          maxidx = k;
+        }
+      }
+
+      cmap[i] = cmap[maxidx] = cnvtxs++;
+      match[i] = maxidx;
+      match[maxidx] = i;
+    }
+  }
+
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->MatchTmr));
+
+  idxwspacefree(ctrl, nvtxs);  /* degrees */
+  idxwspacefree(ctrl, nvtxs);  /* tperm */
+
+  CreateCoarseGraph(ctrl, graph, cnvtxs, match, perm);
+
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+}
+
+
+
+/*************************************************************************
+* This function finds a matching using the HEM heuristic
+**************************************************************************/
+void MCMatch_SBHEM(CtrlType *ctrl, GraphType *graph, int norm)
+{
+  int i, ii, j, k, nvtxs, cnvtxs, ncon, maxidx, maxwgt, avgdegree;
+  idxtype *xadj, *adjncy, *adjwgt;
+  idxtype *match, *cmap, *degrees, *perm, *tperm;
+  float *nvwgt, vbal;
+
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->MatchTmr));
+
+  nvtxs = graph->nvtxs;
+  ncon = graph->ncon;
+  xadj = graph->xadj;
+  nvwgt = graph->nvwgt;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  cmap = graph->cmap;
+  match = idxset(nvtxs, UNMATCHED, idxwspacemalloc(ctrl, nvtxs));
+
+  perm = idxwspacemalloc(ctrl, nvtxs);
+  tperm = idxwspacemalloc(ctrl, nvtxs);
+  degrees = idxwspacemalloc(ctrl, nvtxs);
+
+  RandomPermute(nvtxs, tperm, 1);
+  avgdegree = 0.7*(xadj[nvtxs]/nvtxs);
+  for (i=0; i<nvtxs; i++) 
+    degrees[i] = (xadj[i+1]-xadj[i] > avgdegree ? avgdegree : xadj[i+1]-xadj[i]);
+  BucketSortKeysInc(nvtxs, avgdegree, degrees, tperm, perm);
+
+  cnvtxs = 0;
+
+  /* Take care any islands. Islands are matched with non-islands due to coarsening */
+  for (ii=0; ii<nvtxs; ii++) {
+    i = perm[ii];
+
+    if (match[i] == UNMATCHED) {  /* Unmatched */
+      if (xadj[i] < xadj[i+1])
+        break;
+
+      maxidx = i;
+      for (j=nvtxs-1; j>ii; j--) {
+        k = perm[j];
+        if (match[k] == UNMATCHED && xadj[k] < xadj[k+1]) {
+          maxidx = k;
+          break;
+        }
+      }
+
+      cmap[i] = cmap[maxidx] = cnvtxs++;
+      match[i] = maxidx;
+      match[maxidx] = i;
+    }
+  }
+
+  /* Continue with normal matching */
+  for (; ii<nvtxs; ii++) {
+    i = perm[ii];
+
+    if (match[i] == UNMATCHED) {  /* Unmatched */
+      maxidx = i;
+      maxwgt = -1;
+      vbal = 0.0;
+
+      /* Find a heavy-edge matching, subject to maxvwgt constraints */
+      for (j=xadj[i]; j<xadj[i+1]; j++) {
+        k = adjncy[j];
+        if (match[k] == UNMATCHED && AreAllVwgtsBelowFast(ncon, nvwgt+i*ncon, nvwgt+k*ncon, ctrl->nmaxvwgt)) {
+          if (maxidx != i)
+            vbal = BetterVBalance(ncon, norm, nvwgt+i*ncon, nvwgt+maxidx*ncon, nvwgt+k*ncon);
+
+          if (vbal > 0 || (vbal > -.01 && maxwgt < adjwgt[j])) {
+            maxwgt = adjwgt[j];
+            maxidx = k;
+          }
+        }
+      }
+
+      cmap[i] = cmap[maxidx] = cnvtxs++;
+      match[i] = maxidx;
+      match[maxidx] = i;
+    }
+  }
+
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->MatchTmr));
+
+  idxwspacefree(ctrl, nvtxs);  /* degrees */
+  idxwspacefree(ctrl, nvtxs);  /* tperm */
+
+  CreateCoarseGraph(ctrl, graph, cnvtxs, match, perm);
+
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+}
+
+
+
+
+
+/*************************************************************************
+* This function checks if v+u2 provides a better balance in the weight 
+* vector that v+u1
+**************************************************************************/
+float BetterVBalance(int ncon, int norm, float *vwgt, float *u1wgt, float *u2wgt)
+{
+  int i;
+  float sum1, sum2, max1, max2, min1, min2, diff1, diff2;
+
+  if (norm == -1) {
+    max1 = min1 = vwgt[0]+u1wgt[0];
+    max2 = min2 = vwgt[0]+u2wgt[0];
+    sum1 = vwgt[0]+u1wgt[0];
+    sum2 = vwgt[0]+u2wgt[0];
+
+    for (i=1; i<ncon; i++) {
+      if (max1 < vwgt[i]+u1wgt[i])
+        max1 = vwgt[i]+u1wgt[i];
+      if (min1 > vwgt[i]+u1wgt[i])
+        min1 = vwgt[i]+u1wgt[i];
+
+      if (max2 < vwgt[i]+u2wgt[i])
+        max2 = vwgt[i]+u2wgt[i];
+      if (min2 > vwgt[i]+u2wgt[i])
+        min2 = vwgt[i]+u2wgt[i];
+
+      sum1 += vwgt[i]+u1wgt[i];
+      sum2 += vwgt[i]+u2wgt[i];
+    }
+
+    if (sum1 == 0.0)
+      return 1;
+    else if (sum2 == 0.0)
+      return -1;
+    else
+      return ((max1-min1)/sum1) - ((max2-min2)/sum2);
+  }
+  else if (norm == 1) {
+    sum1 = sum2 = 0.0;
+    for (i=0; i<ncon; i++) {
+      sum1 += vwgt[i]+u1wgt[i];
+      sum2 += vwgt[i]+u2wgt[i];
+    }
+    sum1 = sum1/(1.0*ncon);
+    sum2 = sum2/(1.0*ncon);
+
+    diff1 = diff2 = 0.0;
+    for (i=0; i<ncon; i++) {
+      diff1 += fabs(sum1 - (vwgt[i]+u1wgt[i]));
+      diff2 += fabs(sum2 - (vwgt[i]+u2wgt[i]));
+    }
+
+    return diff1 - diff2;
+  }
+  else {
+    errexit("Unknown norm: %d\n", norm);
+  }
+  return 0.0;
+}
+
+
+/*************************************************************************
+* This function checks if the vertex weights of two vertices are below 
+* a given set of values
+**************************************************************************/
+int AreAllVwgtsBelowFast(int ncon, float *vwgt1, float *vwgt2, float limit)
+{
+  int i;
+
+  for (i=0; i<ncon; i++)
+    if (vwgt1[i] + vwgt2[i] > limit)
+      return 0;
+
+  return 1;
+}
+
diff --git a/contrib/Metis/mmd.c b/contrib/Metis/mmd.c
new file mode 100644
index 0000000000..ad986c2111
--- /dev/null
+++ b/contrib/Metis/mmd.c
@@ -0,0 +1,593 @@
+/*
+ * mmd.c
+ *
+ * **************************************************************
+ * The following C function was developed from a FORTRAN subroutine
+ * in SPARSPAK written by Eleanor Chu, Alan George, Joseph Liu
+ * and Esmond Ng.
+ * 
+ * The FORTRAN-to-C transformation and modifications such as dynamic
+ * memory allocation and deallocation were performed by Chunguang
+ * Sun.
+ * ************************************************************** 
+ *
+ * Taken from SMMS, George 12/13/94
+ *
+ * The meaning of invperm, and perm vectors is different from that
+ * in genqmd_ of SparsPak
+ *
+ * $Id: mmd.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+*  genmmd  -- multiple minimum external degree
+*  purpose -- this routine implements the minimum degree
+*     algorithm. it makes use of the implicit representation
+*     of elimination graphs by quotient graphs, and the notion
+*     of indistinguishable nodes. It also implements the modifications
+*     by multiple elimination and minimum external degree.
+*     Caution -- the adjacency vector adjncy will be destroyed.
+*  Input parameters --
+*     neqns -- number of equations.
+*     (xadj, adjncy) -- the adjacency structure.
+*     delta  -- tolerance value for multiple elimination.
+*     maxint -- maximum machine representable (short) integer
+*               (any smaller estimate will do) for marking nodes.
+*  Output parameters --
+*     perm -- the minimum degree ordering.
+*     invp -- the inverse of perm.
+*     *ncsub -- an upper bound on the number of nonzero subscripts
+*               for the compressed storage scheme.
+*  Working parameters --
+*     head -- vector for head of degree lists.
+*     invp  -- used temporarily for degree forward link.
+*     perm  -- used temporarily for degree backward link.
+*     qsize -- vector for size of supernodes.
+*     list -- vector for temporary linked lists.
+*     marker -- a temporary marker vector.
+*  Subroutines used -- mmdelm, mmdint, mmdnum, mmdupd.
+**************************************************************************/
+void genmmd(int neqns, idxtype *xadj, idxtype *adjncy, idxtype *invp, idxtype *perm,
+     int delta, idxtype *head, idxtype *qsize, idxtype *list, idxtype *marker,
+     int maxint, int *ncsub)
+{
+    int  ehead, i, mdeg, mdlmt, mdeg_node, nextmd, num, tag;
+
+    if (neqns <= 0)  
+      return;
+
+    /* Adjust from C to Fortran */
+    xadj--; adjncy--; invp--; perm--; head--; qsize--; list--; marker--;
+
+    /* initialization for the minimum degree algorithm. */
+    *ncsub = 0;
+    mmdint(neqns, xadj, adjncy, head, invp, perm, qsize, list, marker);
+
+    /*  'num' counts the number of ordered nodes plus 1. */
+    num = 1;
+
+    /* eliminate all isolated nodes. */
+    nextmd = head[1];
+    while (nextmd > 0) {
+      mdeg_node = nextmd;
+      nextmd = invp[mdeg_node];
+      marker[mdeg_node] = maxint;
+      invp[mdeg_node] = -num;
+      num = num + 1;
+    }
+
+    /* search for node of the minimum degree. 'mdeg' is the current */
+    /* minimum degree; 'tag' is used to facilitate marking nodes.   */
+    if (num > neqns) 
+      goto n1000;
+    tag = 1;
+    head[1] = 0;
+    mdeg = 2;
+
+    /* infinite loop here ! */
+    while (1) {
+      while (head[mdeg] <= 0) 
+        mdeg++;
+
+      /* use value of 'delta' to set up 'mdlmt', which governs */
+      /* when a degree update is to be performed.              */
+      mdlmt = mdeg + delta;
+      ehead = 0;
+
+n500:
+      mdeg_node = head[mdeg];
+      while (mdeg_node <= 0) {
+        mdeg++;
+
+        if (mdeg > mdlmt) 
+          goto n900;
+        mdeg_node = head[mdeg];
+      };
+
+      /*  remove 'mdeg_node' from the degree structure. */
+      nextmd = invp[mdeg_node];
+      head[mdeg] = nextmd;
+      if (nextmd > 0)  
+        perm[nextmd] = -mdeg;
+      invp[mdeg_node] = -num;
+      *ncsub += mdeg + qsize[mdeg_node] - 2;
+      if ((num+qsize[mdeg_node]) > neqns)  
+        goto n1000;
+
+      /*  eliminate 'mdeg_node' and perform quotient graph */
+      /*  transformation. reset 'tag' value if necessary.    */
+      tag++;
+      if (tag >= maxint) {
+        tag = 1;
+        for (i = 1; i <= neqns; i++)
+          if (marker[i] < maxint)  
+            marker[i] = 0;
+      };
+
+      mmdelm(mdeg_node, xadj, adjncy, head, invp, perm, qsize, list, marker, maxint, tag);
+
+      num += qsize[mdeg_node];
+      list[mdeg_node] = ehead;
+      ehead = mdeg_node;
+      if (delta >= 0) 
+        goto n500;
+
+ n900:
+      /* update degrees of the nodes involved in the  */
+      /* minimum degree nodes elimination.            */
+      if (num > neqns)  
+        goto n1000;
+      mmdupd( ehead, neqns, xadj, adjncy, delta, &mdeg, head, invp, perm, qsize, list, marker, maxint, &tag);
+    }; /* end of -- while ( 1 ) -- */
+
+n1000:
+    mmdnum( neqns, perm, invp, qsize );
+
+    /* Adjust from Fortran back to C*/
+    xadj++; adjncy++; invp++; perm++; head++; qsize++; list++; marker++;
+}
+
+
+/**************************************************************************
+*           mmdelm ...... multiple minimum degree elimination
+* Purpose -- This routine eliminates the node mdeg_node of minimum degree
+*     from the adjacency structure, which is stored in the quotient
+*     graph format. It also transforms the quotient graph representation
+*     of the elimination graph.
+* Input parameters --
+*     mdeg_node -- node of minimum degree.
+*     maxint -- estimate of maximum representable (short) integer.
+*     tag    -- tag value.
+* Updated parameters --
+*     (xadj, adjncy) -- updated adjacency structure.
+*     (head, forward, backward) -- degree doubly linked structure.
+*     qsize -- size of supernode.
+*     marker -- marker vector.
+*     list -- temporary linked list of eliminated nabors.
+***************************************************************************/
+void mmdelm(int mdeg_node, idxtype *xadj, idxtype *adjncy, idxtype *head, idxtype *forward,
+     idxtype *backward, idxtype *qsize, idxtype *list, idxtype *marker, int maxint,int tag)
+{
+    int   element, i,   istop, istart, j,
+          jstop, jstart, link,
+          nabor, node, npv, nqnbrs, nxnode,
+          pvnode, rlmt, rloc, rnode, xqnbr;
+
+    /* find the reachable set of 'mdeg_node' and */
+    /* place it in the data structure.           */
+    marker[mdeg_node] = tag;
+    istart = xadj[mdeg_node];
+    istop = xadj[mdeg_node+1] - 1;
+
+    /* 'element' points to the beginning of the list of  */
+    /* eliminated nabors of 'mdeg_node', and 'rloc' gives the */
+    /* storage location for the next reachable node.   */
+    element = 0;
+    rloc = istart;
+    rlmt = istop;
+    for ( i = istart; i <= istop; i++ ) {
+        nabor = adjncy[i];
+        if ( nabor == 0 ) break;
+        if ( marker[nabor] < tag ) {
+           marker[nabor] = tag;
+           if ( forward[nabor] < 0 )  {
+              list[nabor] = element;
+              element = nabor;
+           } else {
+              adjncy[rloc] = nabor;
+              rloc++;
+           };
+        }; /* end of -- if -- */
+    }; /* end of -- for -- */
+
+  /* merge with reachable nodes from generalized elements. */
+  while ( element > 0 ) {
+      adjncy[rlmt] = -element;
+      link = element;
+
+n400:
+      jstart = xadj[link];
+      jstop = xadj[link+1] - 1;
+      for ( j = jstart; j <= jstop; j++ ) {
+          node = adjncy[j];
+          link = -node;
+          if ( node < 0 )  goto n400;
+          if ( node == 0 ) break;
+          if ((marker[node]<tag)&&(forward[node]>=0)) {
+             marker[node] = tag;
+             /*use storage from eliminated nodes if necessary.*/
+             while ( rloc >= rlmt ) {
+                   link = -adjncy[rlmt];
+                   rloc = xadj[link];
+                   rlmt = xadj[link+1] - 1;
+             };
+             adjncy[rloc] = node;
+             rloc++;
+          };
+      }; /* end of -- for ( j = jstart; -- */
+      element = list[element];
+    };  /* end of -- while ( element > 0 ) -- */
+    if ( rloc <= rlmt ) adjncy[rloc] = 0;
+    /* for each node in the reachable set, do the following. */
+    link = mdeg_node;
+
+n1100:
+    istart = xadj[link];
+    istop = xadj[link+1] - 1;
+    for ( i = istart; i <= istop; i++ ) {
+        rnode = adjncy[i];
+        link = -rnode;
+        if ( rnode < 0 ) goto n1100;
+        if ( rnode == 0 ) return;
+
+        /* 'rnode' is in the degree list structure. */
+        pvnode = backward[rnode];
+        if (( pvnode != 0 ) && ( pvnode != (-maxint) )) {
+           /* then remove 'rnode' from the structure. */
+           nxnode = forward[rnode];
+           if ( nxnode > 0 ) backward[nxnode] = pvnode;
+           if ( pvnode > 0 ) forward[pvnode] = nxnode;
+           npv = -pvnode;
+           if ( pvnode < 0 ) head[npv] = nxnode;
+        };
+
+        /* purge inactive quotient nabors of 'rnode'. */
+        jstart = xadj[rnode];
+        jstop = xadj[rnode+1] - 1;
+        xqnbr = jstart;
+        for ( j = jstart; j <= jstop; j++ ) {
+            nabor = adjncy[j];
+            if ( nabor == 0 ) break;
+            if ( marker[nabor] < tag ) {
+                adjncy[xqnbr] = nabor;
+                xqnbr++;
+            };
+        };
+
+        /* no active nabor after the purging. */
+        nqnbrs = xqnbr - jstart;
+        if ( nqnbrs <= 0 ) {
+           /* merge 'rnode' with 'mdeg_node'. */
+           qsize[mdeg_node] += qsize[rnode];
+           qsize[rnode] = 0;
+           marker[rnode] = maxint;
+           forward[rnode] = -mdeg_node;
+           backward[rnode] = -maxint;
+        } else {
+           /* flag 'rnode' for degree update, and  */
+           /* add 'mdeg_node' as a nabor of 'rnode'.      */
+           forward[rnode] = nqnbrs + 1;
+           backward[rnode] = 0;
+           adjncy[xqnbr] = mdeg_node;
+           xqnbr++;
+           if ( xqnbr <= jstop )  adjncy[xqnbr] = 0;
+        };
+      }; /* end of -- for ( i = istart; -- */
+      return;
+ }
+
+/***************************************************************************
+*    mmdint ---- mult minimum degree initialization
+*    purpose -- this routine performs initialization for the
+*       multiple elimination version of the minimum degree algorithm.
+*    input parameters --
+*       neqns  -- number of equations.
+*       (xadj, adjncy) -- adjacency structure.
+*    output parameters --
+*       (head, dfrow, backward) -- degree doubly linked structure.
+*       qsize -- size of supernode ( initialized to one).
+*       list -- linked list.
+*       marker -- marker vector.
+****************************************************************************/
+int  mmdint(int neqns, idxtype *xadj, idxtype *adjncy, idxtype *head, idxtype *forward,
+     idxtype *backward, idxtype *qsize, idxtype *list, idxtype *marker)
+{
+    int  fnode, ndeg, node;
+
+    for ( node = 1; node <= neqns; node++ ) {
+        head[node] = 0;
+        qsize[node] = 1;
+        marker[node] = 0;
+        list[node] = 0;
+    };
+
+    /* initialize the degree doubly linked lists. */
+    for ( node = 1; node <= neqns; node++ ) {
+        ndeg = xadj[node+1] - xadj[node]/* + 1*/;   /* george */
+        if (ndeg == 0)
+          ndeg = 1;
+        fnode = head[ndeg];
+        forward[node] = fnode;
+        head[ndeg] = node;
+        if ( fnode > 0 ) backward[fnode] = node;
+        backward[node] = -ndeg;
+    };
+    return 0;
+}
+
+/****************************************************************************
+* mmdnum --- multi minimum degree numbering
+* purpose -- this routine performs the final step in producing
+*    the permutation and inverse permutation vectors in the
+*    multiple elimination version of the minimum degree
+*    ordering algorithm.
+* input parameters --
+*     neqns -- number of equations.
+*     qsize -- size of supernodes at elimination.
+* updated parameters --
+*     invp -- inverse permutation vector. on input,
+*             if qsize[node] = 0, then node has been merged
+*             into the node -invp[node]; otherwise,
+*            -invp[node] is its inverse labelling.
+* output parameters --
+*     perm -- the permutation vector.
+****************************************************************************/
+void mmdnum(int neqns, idxtype *perm, idxtype *invp, idxtype *qsize)
+{
+  int father, nextf, node, nqsize, num, root;
+
+  for ( node = 1; node <= neqns; node++ ) {
+      nqsize = qsize[node];
+      if ( nqsize <= 0 ) perm[node] = invp[node];
+      if ( nqsize > 0 )  perm[node] = -invp[node];
+  };
+
+  /* for each node which has been merged, do the following. */
+  for ( node = 1; node <= neqns; node++ ) {
+      if ( perm[node] <= 0 )  {
+
+	 /* trace the merged tree until one which has not */
+         /* been merged, call it root.                    */
+         father = node;
+         while ( perm[father] <= 0 )
+            father = - perm[father];
+
+         /* number node after root. */
+         root = father;
+         num = perm[root] + 1;
+         invp[node] = -num;
+         perm[root] = num;
+
+         /* shorten the merged tree. */
+         father = node;
+         nextf = - perm[father];
+         while ( nextf > 0 ) {
+            perm[father] = -root;
+            father = nextf;
+            nextf = -perm[father];
+         };
+      };  /* end of -- if ( perm[node] <= 0 ) -- */
+  }; /* end of -- for ( node = 1; -- */
+
+  /* ready to compute perm. */
+  for ( node = 1; node <= neqns; node++ ) {
+        num = -invp[node];
+        invp[node] = num;
+        perm[num] = node;
+  };
+  return;
+}
+
+/****************************************************************************
+* mmdupd ---- multiple minimum degree update
+* purpose -- this routine updates the degrees of nodes after a
+*            multiple elimination step.
+* input parameters --
+*    ehead -- the beginning of the list of eliminated nodes
+*             (i.e., newly formed elements).
+*    neqns -- number of equations.
+*    (xadj, adjncy) -- adjacency structure.
+*    delta -- tolerance value for multiple elimination.
+*    maxint -- maximum machine representable (short) integer.
+* updated parameters --
+*    mdeg -- new minimum degree after degree update.
+*    (head, forward, backward) -- degree doubly linked structure.
+*    qsize -- size of supernode.
+*    list -- marker vector for degree update.
+*    *tag   -- tag value.
+****************************************************************************/
+void mmdupd(int ehead, int neqns, idxtype *xadj, idxtype *adjncy, int delta, int *mdeg,
+     idxtype *head, idxtype *forward, idxtype *backward, idxtype *qsize, idxtype *list,
+     idxtype *marker, int maxint,int *tag)
+{
+ int  deg, deg0, element, enode, fnode, i, iq2, istop,
+      istart, j, jstop, jstart, link, mdeg0, mtag, nabor,
+      node, q2head, qxhead;
+
+      mdeg0 = *mdeg + delta;
+      element = ehead;
+
+n100:
+      if ( element <= 0 ) return;
+
+      /* for each of the newly formed element, do the following. */
+      /* reset tag value if necessary.                           */
+      mtag = *tag + mdeg0;
+      if ( mtag >= maxint ) {
+         *tag = 1;
+         for ( i = 1; i <= neqns; i++ )
+             if ( marker[i] < maxint ) marker[i] = 0;
+         mtag = *tag + mdeg0;
+      };
+
+      /* create two linked lists from nodes associated with 'element': */
+      /* one with two nabors (q2head) in the adjacency structure, and the*/
+      /* other with more than two nabors (qxhead). also compute 'deg0',*/
+      /* number of nodes in this element.                              */
+      q2head = 0;
+      qxhead = 0;
+      deg0 = 0;
+      link =element;
+
+n400:
+      istart = xadj[link];
+      istop = xadj[link+1] - 1;
+      for ( i = istart; i <= istop; i++ ) {
+          enode = adjncy[i];
+          link = -enode;
+          if ( enode < 0 )  goto n400;
+          if ( enode == 0 ) break;
+          if ( qsize[enode] != 0 ) {
+             deg0 += qsize[enode];
+             marker[enode] = mtag;
+
+             /*'enode' requires a degree update*/
+             if ( backward[enode] == 0 ) {
+                /* place either in qxhead or q2head list. */
+                if ( forward[enode] != 2 ) {
+                     list[enode] = qxhead;
+                     qxhead = enode;
+                } else {
+                     list[enode] = q2head;
+                     q2head = enode;
+                };
+             };
+          }; /* enf of -- if ( qsize[enode] != 0 ) -- */
+      }; /* end of -- for ( i = istart; -- */
+
+      /* for each node in q2 list, do the following. */
+      enode = q2head;
+      iq2 = 1;
+
+n900:
+      if ( enode <= 0 ) goto n1500;
+      if ( backward[enode] != 0 ) goto n2200;
+      (*tag)++;
+      deg = deg0;
+
+      /* identify the other adjacent element nabor. */
+      istart = xadj[enode];
+      nabor = adjncy[istart];
+      if ( nabor == element ) nabor = adjncy[istart+1];
+      link = nabor;
+      if ( forward[nabor] >= 0 ) {
+           /* nabor is uneliminated, increase degree count. */
+           deg += qsize[nabor];
+           goto n2100;
+      };
+
+       /* the nabor is eliminated. for each node in the 2nd element */
+       /* do the following.                                         */
+n1000:
+       istart = xadj[link];
+       istop = xadj[link+1] - 1;
+       for ( i = istart; i <= istop; i++ ) {
+           node = adjncy[i];
+           link = -node;
+           if ( node != enode ) {
+                if ( node < 0 ) goto n1000;
+                if ( node == 0 )  goto n2100;
+                if ( qsize[node] != 0 ) {
+                     if ( marker[node] < *tag ) {
+                        /* 'node' is not yet considered. */
+                        marker[node] = *tag;
+                        deg += qsize[node];
+                     } else {
+                        if ( backward[node] == 0 ) {
+                             if ( forward[node] == 2 ) {
+                                /* 'node' is indistinguishable from 'enode'.*/
+                                /* merge them into a new supernode.         */
+                                qsize[enode] += qsize[node];
+                                qsize[node] = 0;
+                                marker[node] = maxint;
+                                forward[node] = -enode;
+                                backward[node] = -maxint;
+                             } else {
+                                /* 'node' is outmacthed by 'enode' */
+				if (backward[node]==0) backward[node] = -maxint;
+                             };
+                        }; /* end of -- if ( backward[node] == 0 ) -- */
+                    }; /* end of -- if ( marker[node] < *tag ) -- */
+                }; /* end of -- if ( qsize[node] != 0 ) -- */
+              }; /* end of -- if ( node != enode ) -- */
+          }; /* end of -- for ( i = istart; -- */
+          goto n2100;
+
+n1500:
+          /* for each 'enode' in the 'qx' list, do the following. */
+          enode = qxhead;
+          iq2 = 0;
+
+n1600:    if ( enode <= 0 )  goto n2300;
+          if ( backward[enode] != 0 )  goto n2200;
+          (*tag)++;
+          deg = deg0;
+
+          /*for each unmarked nabor of 'enode', do the following.*/
+          istart = xadj[enode];
+          istop = xadj[enode+1] - 1;
+          for ( i = istart; i <= istop; i++ ) {
+                nabor = adjncy[i];
+                if ( nabor == 0 ) break;
+                if ( marker[nabor] < *tag ) {
+                     marker[nabor] = *tag;
+                     link = nabor;
+                     if ( forward[nabor] >= 0 ) 
+                          /*if uneliminated, include it in deg count.*/
+                          deg += qsize[nabor];
+                     else {
+n1700:
+                          /* if eliminated, include unmarked nodes in this*/
+                          /* element into the degree count.             */
+                          jstart = xadj[link];
+                          jstop = xadj[link+1] - 1;
+                          for ( j = jstart; j <= jstop; j++ ) {
+                                node = adjncy[j];
+                                link = -node;
+                                if ( node < 0 ) goto n1700;
+                                if ( node == 0 ) break;
+                                if ( marker[node] < *tag ) {
+                                    marker[node] = *tag;
+                                    deg += qsize[node];
+                                };
+                          }; /* end of -- for ( j = jstart; -- */
+                     }; /* end of -- if ( forward[nabor] >= 0 ) -- */
+                  }; /* end of -- if ( marker[nabor] < *tag ) -- */
+          }; /* end of -- for ( i = istart; -- */
+
+n2100:
+          /* update external degree of 'enode' in degree structure, */
+          /* and '*mdeg' if necessary.                     */
+          deg = deg - qsize[enode] + 1;
+          fnode = head[deg];
+          forward[enode] = fnode;
+          backward[enode] = -deg;
+          if ( fnode > 0 ) backward[fnode] = enode;
+          head[deg] = enode;
+          if ( deg < *mdeg ) *mdeg = deg;
+
+n2200:
+          /* get next enode in current element. */
+          enode = list[enode];
+          if ( iq2 == 1 ) goto n900;
+          goto n1600;
+
+n2300:
+          /* get next element in the list. */
+          *tag = mtag;
+          element = list[element];
+          goto n100;
+    }
diff --git a/contrib/Metis/mpmetis.c b/contrib/Metis/mpmetis.c
new file mode 100644
index 0000000000..4d677c11ed
--- /dev/null
+++ b/contrib/Metis/mpmetis.c
@@ -0,0 +1,402 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * mpmetis.c
+ *
+ * This file contains the top level routines for the multilevel recursive
+ * bisection algorithm PMETIS.
+ *
+ * Started 7/24/97
+ * George
+ *
+ * $Id: mpmetis.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+
+
+/*************************************************************************
+* This function is the entry point for PWMETIS that accepts exact weights
+* for the target partitions
+**************************************************************************/
+void METIS_mCPartGraphRecursive(int *nvtxs, int *ncon, idxtype *xadj, idxtype *adjncy, 
+       idxtype *vwgt, idxtype *adjwgt, int *wgtflag, int *numflag, int *nparts, 
+       int *options, int *edgecut, idxtype *part)
+{
+  int i, j;
+  GraphType graph;
+  CtrlType ctrl;
+
+  if (*numflag == 1)
+    Change2CNumbering(*nvtxs, xadj, adjncy);
+
+  SetUpGraph(&graph, OP_PMETIS, *nvtxs, *ncon, xadj, adjncy, vwgt, adjwgt, *wgtflag);
+
+  if (options[0] == 0) {  /* Use the default parameters */
+    ctrl.CType  = McPMETIS_CTYPE;
+    ctrl.IType  = McPMETIS_ITYPE;
+    ctrl.RType  = McPMETIS_RTYPE;
+    ctrl.dbglvl = McPMETIS_DBGLVL;
+  }
+  else {
+    ctrl.CType  = options[OPTION_CTYPE];
+    ctrl.IType  = options[OPTION_ITYPE];
+    ctrl.RType  = options[OPTION_RTYPE];
+    ctrl.dbglvl = options[OPTION_DBGLVL];
+  }
+  ctrl.optype = OP_PMETIS;
+  ctrl.CoarsenTo = 100;
+
+  ctrl.nmaxvwgt = 1.5/(1.0*ctrl.CoarsenTo);
+
+  InitRandom(-1);
+
+  AllocateWorkSpace(&ctrl, &graph, *nparts);
+
+  IFSET(ctrl.dbglvl, DBG_TIME, InitTimers(&ctrl));
+  IFSET(ctrl.dbglvl, DBG_TIME, starttimer(ctrl.TotalTmr));
+
+  *edgecut = MCMlevelRecursiveBisection(&ctrl, &graph, *nparts, part, 1.000, 0);
+
+  IFSET(ctrl.dbglvl, DBG_TIME, stoptimer(ctrl.TotalTmr));
+  IFSET(ctrl.dbglvl, DBG_TIME, PrintTimers(&ctrl));
+
+  FreeWorkSpace(&ctrl, &graph);
+
+  if (*numflag == 1)
+    Change2FNumbering(*nvtxs, xadj, adjncy, part);
+}
+
+
+
+/*************************************************************************
+* This function is the entry point for PWMETIS that accepts exact weights
+* for the target partitions
+**************************************************************************/
+void METIS_mCHPartGraphRecursive(int *nvtxs, int *ncon, idxtype *xadj, idxtype *adjncy, 
+       idxtype *vwgt, idxtype *adjwgt, int *wgtflag, int *numflag, int *nparts, 
+       float *ubvec, int *options, int *edgecut, idxtype *part)
+{
+  int i, j;
+  GraphType graph;
+  CtrlType ctrl;
+  float *myubvec;
+
+  if (*numflag == 1)
+    Change2CNumbering(*nvtxs, xadj, adjncy);
+
+  SetUpGraph(&graph, OP_PMETIS, *nvtxs, *ncon, xadj, adjncy, vwgt, adjwgt, *wgtflag);
+
+  if (options[0] == 0) {  /* Use the default parameters */
+    ctrl.CType = PMETIS_CTYPE;
+    ctrl.IType = PMETIS_ITYPE;
+    ctrl.RType = PMETIS_RTYPE;
+    ctrl.dbglvl = PMETIS_DBGLVL;
+  }
+  else {
+    ctrl.CType = options[OPTION_CTYPE];
+    ctrl.IType = options[OPTION_ITYPE];
+    ctrl.RType = options[OPTION_RTYPE];
+    ctrl.dbglvl = options[OPTION_DBGLVL];
+  }
+  ctrl.optype = OP_PMETIS;
+  ctrl.CoarsenTo = 100;
+
+  ctrl.nmaxvwgt = 1.5/(1.0*ctrl.CoarsenTo);
+
+  myubvec = fmalloc(*ncon, "PWMETIS: mytpwgts");
+  scopy(*ncon, ubvec, myubvec);
+
+  InitRandom(-1);
+
+  AllocateWorkSpace(&ctrl, &graph, *nparts);
+
+  IFSET(ctrl.dbglvl, DBG_TIME, InitTimers(&ctrl));
+  IFSET(ctrl.dbglvl, DBG_TIME, starttimer(ctrl.TotalTmr));
+
+  *edgecut = MCHMlevelRecursiveBisection(&ctrl, &graph, *nparts, part, myubvec, 0);
+
+  IFSET(ctrl.dbglvl, DBG_TIME, stoptimer(ctrl.TotalTmr));
+  IFSET(ctrl.dbglvl, DBG_TIME, PrintTimers(&ctrl));
+
+  FreeWorkSpace(&ctrl, &graph);
+  GKfree(&myubvec, LTERM);
+
+  if (*numflag == 1)
+    Change2FNumbering(*nvtxs, xadj, adjncy, part);
+}
+
+
+
+/*************************************************************************
+* This function is the entry point for PWMETIS that accepts exact weights
+* for the target partitions
+**************************************************************************/
+void METIS_mCPartGraphRecursiveInternal(int *nvtxs, int *ncon, idxtype *xadj, idxtype *adjncy, 
+       float *nvwgt, idxtype *adjwgt, int *nparts, int *options, int *edgecut, idxtype *part)
+{
+  int i, j;
+  GraphType graph;
+  CtrlType ctrl;
+
+  SetUpGraph2(&graph, *nvtxs, *ncon, xadj, adjncy, nvwgt, adjwgt);
+
+  if (options[0] == 0) {  /* Use the default parameters */
+    ctrl.CType = PMETIS_CTYPE;
+    ctrl.IType = PMETIS_ITYPE;
+    ctrl.RType = PMETIS_RTYPE;
+    ctrl.dbglvl = PMETIS_DBGLVL;
+  }
+  else {
+    ctrl.CType = options[OPTION_CTYPE];
+    ctrl.IType = options[OPTION_ITYPE];
+    ctrl.RType = options[OPTION_RTYPE];
+    ctrl.dbglvl = options[OPTION_DBGLVL];
+  }
+  ctrl.optype = OP_PMETIS;
+  ctrl.CoarsenTo = 100;
+
+  ctrl.nmaxvwgt = 1.5/(1.0*ctrl.CoarsenTo);
+
+  InitRandom(-1);
+
+  AllocateWorkSpace(&ctrl, &graph, *nparts);
+
+  IFSET(ctrl.dbglvl, DBG_TIME, InitTimers(&ctrl));
+  IFSET(ctrl.dbglvl, DBG_TIME, starttimer(ctrl.TotalTmr));
+
+  *edgecut = MCMlevelRecursiveBisection(&ctrl, &graph, *nparts, part, 1.000, 0);
+
+  IFSET(ctrl.dbglvl, DBG_TIME, stoptimer(ctrl.TotalTmr));
+  IFSET(ctrl.dbglvl, DBG_TIME, PrintTimers(&ctrl));
+
+  FreeWorkSpace(&ctrl, &graph);
+
+}
+
+
+/*************************************************************************
+* This function is the entry point for PWMETIS that accepts exact weights
+* for the target partitions
+**************************************************************************/
+void METIS_mCHPartGraphRecursiveInternal(int *nvtxs, int *ncon, idxtype *xadj, idxtype *adjncy, 
+       float *nvwgt, idxtype *adjwgt, int *nparts, float *ubvec, int *options, int *edgecut, 
+       idxtype *part)
+{
+  int i, j;
+  GraphType graph;
+  CtrlType ctrl;
+  float *myubvec;
+
+  SetUpGraph2(&graph, *nvtxs, *ncon, xadj, adjncy, nvwgt, adjwgt);
+
+  if (options[0] == 0) {  /* Use the default parameters */
+    ctrl.CType = PMETIS_CTYPE;
+    ctrl.IType = PMETIS_ITYPE;
+    ctrl.RType = PMETIS_RTYPE;
+    ctrl.dbglvl = PMETIS_DBGLVL;
+  }
+  else {
+    ctrl.CType = options[OPTION_CTYPE];
+    ctrl.IType = options[OPTION_ITYPE];
+    ctrl.RType = options[OPTION_RTYPE];
+    ctrl.dbglvl = options[OPTION_DBGLVL];
+  }
+  ctrl.optype = OP_PMETIS;
+  ctrl.CoarsenTo = 100;
+
+  ctrl.nmaxvwgt = 1.5/(1.0*ctrl.CoarsenTo);
+
+  myubvec = fmalloc(*ncon, "PWMETIS: mytpwgts");
+  scopy(*ncon, ubvec, myubvec);
+
+  InitRandom(-1);
+
+  AllocateWorkSpace(&ctrl, &graph, *nparts);
+
+  IFSET(ctrl.dbglvl, DBG_TIME, InitTimers(&ctrl));
+  IFSET(ctrl.dbglvl, DBG_TIME, starttimer(ctrl.TotalTmr));
+
+  *edgecut = MCHMlevelRecursiveBisection(&ctrl, &graph, *nparts, part, myubvec, 0);
+
+  IFSET(ctrl.dbglvl, DBG_TIME, stoptimer(ctrl.TotalTmr));
+  IFSET(ctrl.dbglvl, DBG_TIME, PrintTimers(&ctrl));
+
+  FreeWorkSpace(&ctrl, &graph);
+  GKfree(&myubvec, LTERM);
+
+}
+
+
+
+
+/*************************************************************************
+* This function takes a graph and produces a bisection of it
+**************************************************************************/
+int MCMlevelRecursiveBisection(CtrlType *ctrl, GraphType *graph, int nparts, idxtype *part, 
+       float ubfactor, int fpart)
+{
+  int i, j, nvtxs, ncon, cut;
+  idxtype *label, *where;
+  GraphType lgraph, rgraph;
+  float tpwgts[2];
+
+  nvtxs = graph->nvtxs;
+  if (nvtxs == 0) {
+    printf("\t***Cannot bisect a graph with 0 vertices!\n\t***You are trying to partition a graph into too many parts!\n");
+    return 0;
+  }
+
+  /* Determine the weights of the partitions */
+  tpwgts[0] = 1.0*(nparts>>1)/(1.0*nparts);
+  tpwgts[1] = 1.0 - tpwgts[0];
+
+  MCMlevelEdgeBisection(ctrl, graph, tpwgts, ubfactor);
+  cut = graph->mincut;
+
+  label = graph->label;
+  where = graph->where;
+  for (i=0; i<nvtxs; i++)
+    part[label[i]] = where[i] + fpart;
+
+  if (nparts > 2) 
+    SplitGraphPart(ctrl, graph, &lgraph, &rgraph);
+
+  /* Free the memory of the top level graph */
+  GKfree(&graph->gdata, &graph->nvwgt, &graph->rdata, &graph->npwgts, &graph->label, LTERM);
+
+
+  /* Do the recursive call */
+  if (nparts > 3) {
+    cut += MCMlevelRecursiveBisection(ctrl, &lgraph, nparts/2, part, ubfactor, fpart);
+    cut += MCMlevelRecursiveBisection(ctrl, &rgraph, nparts-nparts/2, part, ubfactor, fpart+nparts/2);
+  }
+  else if (nparts == 3) {
+    cut += MCMlevelRecursiveBisection(ctrl, &rgraph, nparts-nparts/2, part, ubfactor, fpart+nparts/2);
+    GKfree(&lgraph.gdata, &lgraph.nvwgt, &lgraph.label, LTERM);
+  }
+
+  return cut;
+
+}
+
+
+
+/*************************************************************************
+* This function takes a graph and produces a bisection of it
+**************************************************************************/
+int MCHMlevelRecursiveBisection(CtrlType *ctrl, GraphType *graph, int nparts, idxtype *part, 
+      float *ubvec, int fpart)
+{
+  int i, j, nvtxs, ncon, cut;
+  idxtype *label, *where;
+  GraphType lgraph, rgraph;
+  float tpwgts[2], *npwgts, *lubvec, *rubvec;
+
+  lubvec = rubvec = NULL;
+
+  nvtxs = graph->nvtxs;
+  ncon = graph->ncon;
+  if (nvtxs == 0) {
+    printf("\t***Cannot bisect a graph with 0 vertices!\n\t***You are trying to partition a graph into too many parts!\n");
+    return 0;
+  }
+
+  /* Determine the weights of the partitions */
+  tpwgts[0] = 1.0*(nparts>>1)/(1.0*nparts);
+  tpwgts[1] = 1.0 - tpwgts[0];
+
+  /* For now, relax at the coarsest level only */
+  if (nparts == 2)
+    MCHMlevelEdgeBisection(ctrl, graph, tpwgts, ubvec);
+  else
+    MCMlevelEdgeBisection(ctrl, graph, tpwgts, 1.000);
+  cut = graph->mincut;
+
+  label = graph->label;
+  where = graph->where;
+  for (i=0; i<nvtxs; i++)
+    part[label[i]] = where[i] + fpart;
+
+  if (nparts > 2) {
+    /* Adjust the ubvecs before the split */
+    npwgts = graph->npwgts;
+    lubvec = fmalloc(ncon, "MCHMlevelRecursiveBisection");
+    rubvec = fmalloc(ncon, "MCHMlevelRecursiveBisection");
+
+    for (i=0; i<ncon; i++) {
+      lubvec[i] = ubvec[i]*tpwgts[0]/npwgts[i];
+      lubvec[i] = amax(lubvec[i], 1.01);
+
+      rubvec[i] = ubvec[i]*tpwgts[1]/npwgts[ncon+i];
+      rubvec[i] = amax(rubvec[i], 1.01);
+    }
+
+    SplitGraphPart(ctrl, graph, &lgraph, &rgraph);
+  }
+
+  /* Free the memory of the top level graph */
+  GKfree(&graph->gdata, &graph->nvwgt, &graph->rdata, &graph->npwgts, &graph->label, LTERM);
+
+
+  /* Do the recursive call */
+  if (nparts > 3) {
+    cut += MCHMlevelRecursiveBisection(ctrl, &lgraph, nparts/2, part, lubvec, fpart);
+    cut += MCHMlevelRecursiveBisection(ctrl, &rgraph, nparts-nparts/2, part, rubvec, fpart+nparts/2);
+  }
+  else if (nparts == 3) {
+    cut += MCHMlevelRecursiveBisection(ctrl, &rgraph, nparts-nparts/2, part, rubvec, fpart+nparts/2);
+    GKfree(&lgraph.gdata, &lgraph.nvwgt, &lgraph.label, LTERM);
+  }
+
+  GKfree(&lubvec, &rubvec, LTERM);
+
+  return cut;
+
+}
+
+
+
+
+/*************************************************************************
+* This function performs multilevel bisection
+**************************************************************************/
+void MCMlevelEdgeBisection(CtrlType *ctrl, GraphType *graph, float *tpwgts, float ubfactor)
+{
+  GraphType *cgraph;
+
+  cgraph = MCCoarsen2Way(ctrl, graph);
+
+  MocInit2WayPartition(ctrl, cgraph, tpwgts, ubfactor);
+
+  MocRefine2Way(ctrl, graph, cgraph, tpwgts, ubfactor); 
+
+}
+
+
+
+/*************************************************************************
+* This function performs multilevel bisection
+**************************************************************************/
+void MCHMlevelEdgeBisection(CtrlType *ctrl, GraphType *graph, float *tpwgts, float *ubvec)
+{
+  int i;
+  GraphType *cgraph;
+
+/*
+  for (i=0; i<graph->ncon; i++)
+    printf("%.4f ", ubvec[i]);
+  printf("\n");
+*/
+
+  cgraph = MCCoarsen2Way(ctrl, graph);
+
+  MocInit2WayPartition2(ctrl, cgraph, tpwgts, ubvec);
+
+  MocRefine2Way2(ctrl, graph, cgraph, tpwgts, ubvec); 
+
+}
+
+
diff --git a/contrib/Metis/mrefine.c b/contrib/Metis/mrefine.c
new file mode 100644
index 0000000000..7d5b8e0b6e
--- /dev/null
+++ b/contrib/Metis/mrefine.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * refine.c
+ *
+ * This file contains the driving routines for multilevel refinement
+ *
+ * Started 7/24/97
+ * George
+ *
+ * $Id: mrefine.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function is the entry point of refinement
+**************************************************************************/
+void MocRefine2Way(CtrlType *ctrl, GraphType *orggraph, GraphType *graph, float *tpwgts, float ubfactor)
+{
+  int i;
+  float tubvec[MAXNCON];
+
+  for (i=0; i<graph->ncon; i++)
+    tubvec[i] = 1.0;
+
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->UncoarsenTmr));
+
+  /* Compute the parameters of the coarsest graph */
+  MocCompute2WayPartitionParams(ctrl, graph);
+
+  for (;;) {
+    ASSERT(CheckBnd(graph));
+
+    IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->RefTmr));
+    switch (ctrl->RType) {
+      case RTYPE_FM:
+        MocBalance2Way(ctrl, graph, tpwgts, 1.03);
+        MocFM_2WayEdgeRefine(ctrl, graph, tpwgts, 8); 
+        break;
+      case 2:
+        MocBalance2Way(ctrl, graph, tpwgts, 1.03);
+        MocFM_2WayEdgeRefine2(ctrl, graph, tpwgts, tubvec, 8); 
+        break;
+      default:
+        errexit("Unknown refinement type: %d\n", ctrl->RType);
+    }
+    IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->RefTmr));
+
+    if (graph == orggraph)
+      break;
+
+    graph = graph->finer;
+    IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->ProjectTmr));
+    MocProject2WayPartition(ctrl, graph);
+    IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->ProjectTmr));
+  }
+
+  MocBalance2Way(ctrl, graph, tpwgts, 1.01);
+  MocFM_2WayEdgeRefine(ctrl, graph, tpwgts, 8); 
+
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->UncoarsenTmr));
+}
+
+
+/*************************************************************************
+* This function allocates memory for 2-way edge refinement
+**************************************************************************/
+void MocAllocate2WayPartitionMemory(CtrlType *ctrl, GraphType *graph)
+{
+  int nvtxs, ncon;
+
+  nvtxs = graph->nvtxs;
+  ncon = graph->ncon;
+
+  graph->rdata = idxmalloc(5*nvtxs, "Allocate2WayPartitionMemory: rdata");
+  graph->where		= graph->rdata;
+  graph->id		= graph->rdata + nvtxs;
+  graph->ed		= graph->rdata + 2*nvtxs;
+  graph->bndptr		= graph->rdata + 3*nvtxs;
+  graph->bndind		= graph->rdata + 4*nvtxs;
+
+  graph->npwgts 	= fmalloc(2*ncon, "npwgts");
+}
+
+
+/*************************************************************************
+* This function computes the initial id/ed 
+**************************************************************************/
+void MocCompute2WayPartitionParams(CtrlType *ctrl, GraphType *graph)
+{
+  int i, j, k, l, nvtxs, ncon, nbnd, mincut;
+  idxtype *xadj, *adjncy, *adjwgt;
+  float *nvwgt, *npwgts;
+  idxtype *id, *ed, *where;
+  idxtype *bndptr, *bndind;
+  int me, other;
+
+  nvtxs = graph->nvtxs;
+  ncon = graph->ncon;
+  xadj = graph->xadj;
+  nvwgt = graph->nvwgt;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  where = graph->where;
+  npwgts = sset(2*ncon, 0.0, graph->npwgts);
+  id = idxset(nvtxs, 0, graph->id);
+  ed = idxset(nvtxs, 0, graph->ed);
+  bndptr = idxset(nvtxs, -1, graph->bndptr);
+  bndind = graph->bndind;
+
+
+  /*------------------------------------------------------------
+  / Compute now the id/ed degrees
+  /------------------------------------------------------------*/
+  nbnd = mincut = 0;
+  for (i=0; i<nvtxs; i++) {
+    ASSERT(where[i] >= 0 && where[i] <= 1);
+    me = where[i];
+    saxpy(ncon, 1.0, nvwgt+i*ncon, 1, npwgts+me*ncon, 1);
+
+    for (j=xadj[i]; j<xadj[i+1]; j++) {
+      if (me == where[adjncy[j]])
+        id[i] += adjwgt[j];
+      else
+        ed[i] += adjwgt[j];
+    }
+
+    if (ed[i] > 0 || xadj[i] == xadj[i+1]) {
+      mincut += ed[i];
+      bndptr[i] = nbnd;
+      bndind[nbnd++] = i;
+    }
+  }
+
+  graph->mincut = mincut/2;
+  graph->nbnd = nbnd;
+
+}
+
+
+
+/*************************************************************************
+* This function projects a partition, and at the same time computes the
+* parameters for refinement.
+**************************************************************************/
+void MocProject2WayPartition(CtrlType *ctrl, GraphType *graph)
+{
+  int i, j, k, nvtxs, nbnd, me;
+  idxtype *xadj, *adjncy, *adjwgt, *adjwgtsum;
+  idxtype *cmap, *where, *id, *ed, *bndptr, *bndind;
+  idxtype *cwhere, *cid, *ced, *cbndptr;
+  GraphType *cgraph;
+
+  cgraph = graph->coarser;
+  cwhere = cgraph->where;
+  cid = cgraph->id;
+  ced = cgraph->ed;
+  cbndptr = cgraph->bndptr;
+
+  nvtxs = graph->nvtxs;
+  cmap = graph->cmap;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+  adjwgtsum = graph->adjwgtsum;
+
+  MocAllocate2WayPartitionMemory(ctrl, graph);
+
+  where = graph->where;
+  id = idxset(nvtxs, 0, graph->id);
+  ed = idxset(nvtxs, 0, graph->ed);
+  bndptr = idxset(nvtxs, -1, graph->bndptr);
+  bndind = graph->bndind;
+
+
+  /* Go through and project partition and compute id/ed for the nodes */
+  for (i=0; i<nvtxs; i++) {
+    k = cmap[i];
+    where[i] = cwhere[k];
+    cmap[i] = cbndptr[k];
+  }
+
+  for (nbnd=0, i=0; i<nvtxs; i++) {
+    me = where[i];
+
+    id[i] = adjwgtsum[i];
+
+    if (xadj[i] == xadj[i+1]) {
+      bndptr[i] = nbnd;
+      bndind[nbnd++] = i;
+    }
+    else {
+      if (cmap[i] != -1) { /* If it is an interface node. Note that cmap[i] = cbndptr[cmap[i]] */
+        for (j=xadj[i]; j<xadj[i+1]; j++) {
+          if (me != where[adjncy[j]])
+            ed[i] += adjwgt[j];
+        }
+        id[i] -= ed[i];
+
+        if (ed[i] > 0 || xadj[i] == xadj[i+1]) {
+          bndptr[i] = nbnd;
+          bndind[nbnd++] = i;
+        }
+      }
+    }
+  }
+
+  graph->mincut = cgraph->mincut;
+  graph->nbnd = nbnd;
+  scopy(2*graph->ncon, cgraph->npwgts, graph->npwgts);
+
+  FreeGraph(graph->coarser);
+  graph->coarser = NULL;
+
+}
+
diff --git a/contrib/Metis/mrefine2.c b/contrib/Metis/mrefine2.c
new file mode 100644
index 0000000000..ed5ff9f140
--- /dev/null
+++ b/contrib/Metis/mrefine2.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * mrefine2.c
+ *
+ * This file contains the driving routines for multilevel refinement
+ *
+ * Started 7/24/97
+ * George
+ *
+ * $Id: mrefine2.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function is the entry point of refinement
+**************************************************************************/
+void MocRefine2Way2(CtrlType *ctrl, GraphType *orggraph, GraphType *graph, float *tpwgts, 
+       float *ubvec)
+{
+
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->UncoarsenTmr));
+
+  /* Compute the parameters of the coarsest graph */
+  MocCompute2WayPartitionParams(ctrl, graph);
+
+  for (;;) {
+    ASSERT(CheckBnd(graph));
+
+    IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->RefTmr));
+    switch (ctrl->RType) {
+      case RTYPE_FM:
+        MocBalance2Way2(ctrl, graph, tpwgts, ubvec);
+        MocFM_2WayEdgeRefine2(ctrl, graph, tpwgts, ubvec, 8); 
+        break;
+      default:
+        errexit("Unknown refinement type: %d\n", ctrl->RType);
+    }
+    IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->RefTmr));
+
+    if (graph == orggraph)
+      break;
+
+    graph = graph->finer;
+    IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->ProjectTmr));
+    MocProject2WayPartition(ctrl, graph);
+    IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->ProjectTmr));
+  }
+
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->UncoarsenTmr));
+}
+
+
diff --git a/contrib/Metis/mutil.c b/contrib/Metis/mutil.c
new file mode 100644
index 0000000000..7a6d5267b1
--- /dev/null
+++ b/contrib/Metis/mutil.c
@@ -0,0 +1,101 @@
+/*
+ * mutil.c 
+ *
+ * This file contains various utility functions for the MOC portion of the
+ * code
+ *
+ * Started 2/15/98
+ * George
+ *
+ * $Id: mutil.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function checks if the vertex weights of two vertices are below 
+* a given set of values
+**************************************************************************/
+int AreAllVwgtsBelow(int ncon, float alpha, float *vwgt1, float beta, float *vwgt2, float limit)
+{
+  int i;
+
+  for (i=0; i<ncon; i++)
+    if (alpha*vwgt1[i] + beta*vwgt2[i] > limit)
+      return 0;
+
+  return 1;
+}
+
+
+/*************************************************************************
+* This function checks if the vertex weights of two vertices are below 
+* a given set of values
+**************************************************************************/
+int AreAnyVwgtsBelow(int ncon, float alpha, float *vwgt1, float beta, float *vwgt2, float limit)
+{
+  int i;
+
+  for (i=0; i<ncon; i++)
+    if (alpha*vwgt1[i] + beta*vwgt2[i] < limit)
+      return 1;
+
+  return 0;
+}
+
+
+
+/*************************************************************************
+* This function checks if the vertex weights of two vertices are above 
+* a given set of values
+**************************************************************************/
+int AreAllVwgtsAbove(int ncon, float alpha, float *vwgt1, float beta, float *vwgt2, float limit)
+{
+  int i;
+
+  for (i=0; i<ncon; i++)
+    if (alpha*vwgt1[i] + beta*vwgt2[i] < limit)
+      return 0;
+
+  return 1;
+}
+
+
+/*************************************************************************
+* This function computes the load imbalance over all the constrains
+* For now assume that we just want balanced partitionings
+**************************************************************************/ 
+float ComputeLoadImbalance(int ncon, int nparts, float *npwgts, float *tpwgts)
+{
+  int i, j;
+  float max, lb=0.0;
+
+  for (i=0; i<ncon; i++) {
+    max = 0.0;
+    for (j=0; j<nparts; j++) {
+      if (npwgts[j*ncon+i] > max)
+        max = npwgts[j*ncon+i];
+    }
+    if (max*nparts > lb)
+      lb = max*nparts;
+  }
+
+  return lb;
+}
+
+/*************************************************************************
+* This function checks if the vertex weights of two vertices are below 
+* a given set of values
+**************************************************************************/
+int AreAllBelow(int ncon, float *v1, float *v2)
+{
+  int i;
+
+  for (i=0; i<ncon; i++)
+    if (v1[i] > v2[i])
+      return 0;
+
+  return 1;
+}
diff --git a/contrib/Metis/myqsort.c b/contrib/Metis/myqsort.c
new file mode 100644
index 0000000000..c5b756cfdf
--- /dev/null
+++ b/contrib/Metis/myqsort.c
@@ -0,0 +1,547 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * myqsort.c
+ * 
+ * This file contains a fast idxtype increasing qsort algorithm.
+ * Addopted from TeX
+ * 
+ * Started 10/18/96
+ * George
+ * 
+ * $Id: myqsort.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ */
+
+#include <metis.h>			/* only for type declarations */
+
+#define		THRESH		1	/* threshold for insertion */
+#define		MTHRESH		6	/* threshold for median */
+
+
+
+
+static void siqst(idxtype *, idxtype *);
+static void iiqst(int *, int *);
+static void keyiqst(KeyValueType *, KeyValueType *);
+static void keyvaliqst(KeyValueType *, KeyValueType *);
+
+
+/*************************************************************************
+* Entry point of idxtype increasing sort
+**************************************************************************/
+void iidxsort(int n, idxtype *base)
+{
+  register idxtype *i;
+  register idxtype *j;
+  register idxtype *lo;
+  register idxtype *hi;
+  register idxtype *min;
+  register idxtype c;
+  idxtype *max;
+
+  if (n <= 1)
+    return;
+
+  max = base + n;
+
+  if (n >= THRESH) {
+    siqst(base, max);
+    hi = base + THRESH;
+  }
+  else 
+    hi = max;
+
+  for (j = lo = base; lo++ < hi;) {
+    if (*j > *lo)
+      j = lo;
+  }
+  if (j != base) { /* swap j into place */
+    c = *base;
+    *base = *j;
+    *j = c;
+  }
+
+  for (min = base; (hi = min += 1) < max;) {
+    while (*(--hi) > *min);
+    if ((hi += 1) != min) {
+      for (lo = min + 1; --lo >= min;) {
+	c = *lo;
+	for (i = j = lo; (j -= 1) >= hi; i = j)
+	   *i = *j;
+	*i = c;
+      }
+    }
+  }
+}
+
+static void siqst(idxtype *base, idxtype *max)
+{
+  register idxtype *i;
+  register idxtype *j;
+  register idxtype *jj;
+  register idxtype *mid;
+  register int ii;
+  register idxtype c;
+  idxtype *tmp;
+  int lo;
+  int hi;
+
+  lo = max - base;		/* number of elements as idxtype */
+  do {
+    mid = base + ((unsigned) lo>>1);
+    if (lo >= MTHRESH) {
+      j = (*base > *mid ? base : mid);
+      tmp = max - 1;
+      if (*j > *tmp) {
+        j = (j == base ? mid : base); /* switch to first loser */
+        if (*j < *tmp)
+          j = tmp;
+      }
+
+      if (j != mid) {  /* SWAP */ 
+        c = *mid;
+        *mid = *j;
+        *j = c;
+      }
+    }
+
+    /* Semi-standard quicksort partitioning/swapping */
+    for (i = base, j = max - 1;;) {
+      while (i < mid && *i <= *mid)
+        i++;
+      while (j > mid) {
+        if (*mid <= *j) {
+          j--;
+          continue;
+        }
+        tmp = i + 1;	/* value of i after swap */
+        if (i == mid) 	/* j <-> mid, new mid is j */
+          mid = jj = j;
+        else 		/* i <-> j */
+          jj = j--;
+        goto swap;
+      }
+
+      if (i == mid) 
+	break;
+      else {		/* i <-> mid, new mid is i */
+        jj = mid;
+        tmp = mid = i;	/* value of i after swap */
+        j--;
+      }
+swap:
+      c = *i;
+      *i = *jj;
+      *jj = c;
+      i = tmp;
+    }
+
+    i = (j = mid) + 1;
+    if ((lo = j - base) <= (hi = max - i)) {
+      if (lo >= THRESH)
+        siqst(base, j);
+      base = i;
+      lo = hi;
+    }
+    else {
+      if (hi >= THRESH)
+        siqst(i, max);
+      max = j;
+    }
+  } while (lo >= THRESH);
+}
+
+
+
+
+
+/*************************************************************************
+* Entry point of int increasing sort
+**************************************************************************/
+void iintsort(int n, int *base)
+{
+  register int *i;
+  register int *j;
+  register int *lo;
+  register int *hi;
+  register int *min;
+  register int c;
+  int *max;
+
+  if (n <= 1)
+    return;
+
+  max = base + n;
+
+  if (n >= THRESH) {
+    iiqst(base, max);
+    hi = base + THRESH;
+  }
+  else 
+    hi = max;
+
+  for (j = lo = base; lo++ < hi;) {
+    if (*j > *lo)
+      j = lo;
+  }
+  if (j != base) { /* swap j into place */
+    c = *base;
+    *base = *j;
+    *j = c;
+  }
+
+  for (min = base; (hi = min += 1) < max;) {
+    while (*(--hi) > *min);
+    if ((hi += 1) != min) {
+      for (lo = min + 1; --lo >= min;) {
+	c = *lo;
+	for (i = j = lo; (j -= 1) >= hi; i = j)
+	   *i = *j;
+	*i = c;
+      }
+    }
+  }
+}
+
+
+static void iiqst(int *base, int *max)
+{
+  register int *i;
+  register int *j;
+  register int *jj;
+  register int *mid;
+  register int ii;
+  register int c;
+  int *tmp;
+  int lo;
+  int hi;
+
+  lo = max - base;		/* number of elements as ints */
+  do {
+    mid = base + ((unsigned) lo>>1);
+    if (lo >= MTHRESH) {
+      j = (*base > *mid ? base : mid);
+      tmp = max - 1;
+      if (*j > *tmp) {
+        j = (j == base ? mid : base); /* switch to first loser */
+        if (*j < *tmp)
+          j = tmp;
+      }
+
+      if (j != mid) {  /* SWAP */ 
+        c = *mid;
+        *mid = *j;
+        *j = c;
+      }
+    }
+
+    /* Semi-standard quicksort partitioning/swapping */
+    for (i = base, j = max - 1;;) {
+      while (i < mid && *i <= *mid)
+        i++;
+      while (j > mid) {
+        if (*mid <= *j) {
+          j--;
+          continue;
+        }
+        tmp = i + 1;	/* value of i after swap */
+        if (i == mid) 	/* j <-> mid, new mid is j */
+          mid = jj = j;
+        else 		/* i <-> j */
+          jj = j--;
+        goto swap;
+      }
+
+      if (i == mid) 
+	break;
+      else {		/* i <-> mid, new mid is i */
+        jj = mid;
+        tmp = mid = i;	/* value of i after swap */
+        j--;
+      }
+swap:
+      c = *i;
+      *i = *jj;
+      *jj = c;
+      i = tmp;
+    }
+
+    i = (j = mid) + 1;
+    if ((lo = j - base) <= (hi = max - i)) {
+      if (lo >= THRESH)
+        iiqst(base, j);
+      base = i;
+      lo = hi;
+    }
+    else {
+      if (hi >= THRESH)
+        iiqst(i, max);
+      max = j;
+    }
+  } while (lo >= THRESH);
+}
+
+
+
+
+
+/*************************************************************************
+* Entry point of KeyVal increasing sort, ONLY key part
+**************************************************************************/
+void ikeysort(int n, KeyValueType *base)
+{
+  register KeyValueType *i;
+  register KeyValueType *j;
+  register KeyValueType *lo;
+  register KeyValueType *hi;
+  register KeyValueType *min;
+  register KeyValueType c;
+  KeyValueType *max;
+
+  if (n <= 1)
+    return;
+
+  max = base + n;
+
+  if (n >= THRESH) {
+    keyiqst(base, max);
+    hi = base + THRESH;
+  }
+  else 
+    hi = max;
+
+  for (j = lo = base; lo++ < hi;) {
+    if (j->key > lo->key)
+      j = lo;
+  }
+  if (j != base) { /* swap j into place */
+    c = *base;
+    *base = *j;
+    *j = c;
+  }
+
+  for (min = base; (hi = min += 1) < max;) {
+    while ((--hi)->key > min->key);
+    if ((hi += 1) != min) {
+      for (lo = min + 1; --lo >= min;) {
+	c = *lo;
+	for (i = j = lo; (j -= 1) >= hi; i = j)
+	   *i = *j;
+	*i = c;
+      }
+    }
+  }
+
+  /* Sanity check */
+  { 
+    int i;
+    for (i=0; i<n-1; i++)
+      if (base[i].key > base[i+1].key)
+        printf("Something went wrong!\n");
+  }
+}
+
+
+static void keyiqst(KeyValueType *base, KeyValueType *max)
+{
+  register KeyValueType *i;
+  register KeyValueType *j;
+  register KeyValueType *jj;
+  register KeyValueType *mid;
+  register KeyValueType c;
+  KeyValueType *tmp;
+  int lo;
+  int hi;
+
+  lo = (max - base)>>1;		/* number of elements as KeyValueType */
+  do {
+    mid = base + ((unsigned) lo>>1);
+    if (lo >= MTHRESH) {
+      j = (base->key > mid->key ? base : mid);
+      tmp = max - 1;
+      if (j->key > tmp->key) {
+        j = (j == base ? mid : base); /* switch to first loser */
+        if (j->key < tmp->key)
+          j = tmp;
+      }
+
+      if (j != mid) {  /* SWAP */ 
+        c = *mid;
+        *mid = *j;
+        *j = c;
+      }
+    }
+
+    /* Semi-standard quicksort partitioning/swapping */
+    for (i = base, j = max - 1;;) {
+      while (i < mid && i->key <= mid->key)
+        i++;
+      while (j > mid) {
+        if (mid->key <= j->key) {
+          j--;
+          continue;
+        }
+        tmp = i + 1;	/* value of i after swap */
+        if (i == mid) 	/* j <-> mid, new mid is j */
+          mid = jj = j;
+        else 		/* i <-> j */
+          jj = j--;
+        goto swap;
+      }
+
+      if (i == mid) 
+	break;
+      else {		/* i <-> mid, new mid is i */
+        jj = mid;
+        tmp = mid = i;	/* value of i after swap */
+        j--;
+      }
+swap:
+      c = *i;
+      *i = *jj;
+      *jj = c;
+      i = tmp;
+    }
+
+    i = (j = mid) + 1;
+    if ((lo = (j - base)>>1) <= (hi = (max - i)>>1)) {
+      if (lo >= THRESH)
+        keyiqst(base, j);
+      base = i;
+      lo = hi;
+    }
+    else {
+      if (hi >= THRESH)
+        keyiqst(i, max);
+      max = j;
+    }
+  } while (lo >= THRESH);
+}
+
+
+
+
+/*************************************************************************
+* Entry point of KeyVal increasing sort, BOTH key and val part
+**************************************************************************/
+void ikeyvalsort(int n, KeyValueType *base)
+{
+  register KeyValueType *i;
+  register KeyValueType *j;
+  register KeyValueType *lo;
+  register KeyValueType *hi;
+  register KeyValueType *min;
+  register KeyValueType c;
+  KeyValueType *max;
+
+  if (n <= 1)
+    return;
+
+  max = base + n;
+
+  if (n >= THRESH) {
+    keyvaliqst(base, max);
+    hi = base + THRESH;
+  }
+  else 
+    hi = max;
+
+  for (j = lo = base; lo++ < hi;) {
+    if ((j->key > lo->key) || (j->key == lo->key && j->val > lo->val))
+      j = lo;
+  }
+  if (j != base) { /* swap j into place */
+    c = *base;
+    *base = *j;
+    *j = c;
+  }
+
+  for (min = base; (hi = min += 1) < max;) {
+    while ((--hi)->key > min->key || (hi->key == min->key && hi->val > min->val));
+    if ((hi += 1) != min) {
+      for (lo = min + 1; --lo >= min;) {
+	c = *lo;
+	for (i = j = lo; (j -= 1) >= hi; i = j)
+	   *i = *j;
+	*i = c;
+      }
+    }
+  }
+}
+
+
+static void keyvaliqst(KeyValueType *base, KeyValueType *max)
+{
+  register KeyValueType *i;
+  register KeyValueType *j;
+  register KeyValueType *jj;
+  register KeyValueType *mid;
+  register KeyValueType c;
+  KeyValueType *tmp;
+  int lo;
+  int hi;
+
+  lo = (max - base)>>1;		/* number of elements as KeyValueType */
+  do {
+    mid = base + ((unsigned) lo>>1);
+    if (lo >= MTHRESH) {
+      j = (base->key > mid->key || (base->key == mid->key && base->val > mid->val) ? base : mid);
+      tmp = max - 1;
+      if (j->key > tmp->key || (j->key == tmp->key && j->val > tmp->val)) {
+        j = (j == base ? mid : base); /* switch to first loser */
+        if (j->key < tmp->key || (j->key == tmp->key && j->val < tmp->val))
+          j = tmp;
+      }
+
+      if (j != mid) {  /* SWAP */ 
+        c = *mid;
+        *mid = *j;
+        *j = c;
+      }
+    }
+
+    /* Semi-standard quicksort partitioning/swapping */
+    for (i = base, j = max - 1;;) {
+      while (i < mid && (i->key < mid->key || (i->key == mid->key && i->val <= mid->val)))
+        i++;
+      while (j > mid) {
+        if (mid->key < j->key || (mid->key == j->key && mid->val <= j->val)) {
+          j--;
+          continue;
+        }
+        tmp = i + 1;	/* value of i after swap */
+        if (i == mid) 	/* j <-> mid, new mid is j */
+          mid = jj = j;
+        else 		/* i <-> j */
+          jj = j--;
+        goto swap;
+      }
+
+      if (i == mid) 
+	break;
+      else {		/* i <-> mid, new mid is i */
+        jj = mid;
+        tmp = mid = i;	/* value of i after swap */
+        j--;
+      }
+swap:
+      c = *i;
+      *i = *jj;
+      *jj = c;
+      i = tmp;
+    }
+
+    i = (j = mid) + 1;
+    if ((lo = (j - base)>>1) <= (hi = (max - i)>>1)) {
+      if (lo >= THRESH)
+        keyvaliqst(base, j);
+      base = i;
+      lo = hi;
+    }
+    else {
+      if (hi >= THRESH)
+        keyvaliqst(i, max);
+      max = j;
+    }
+  } while (lo >= THRESH);
+}
diff --git a/contrib/Metis/ometis.c b/contrib/Metis/ometis.c
new file mode 100644
index 0000000000..7be8a7a01a
--- /dev/null
+++ b/contrib/Metis/ometis.c
@@ -0,0 +1,764 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * ometis.c
+ *
+ * This file contains the top level routines for the multilevel recursive
+ * bisection algorithm PMETIS.
+ *
+ * Started 7/24/97
+ * George
+ *
+ * $Id: ometis.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function is the entry point for OEMETIS
+**************************************************************************/
+void METIS_EdgeND(int *nvtxs, idxtype *xadj, idxtype *adjncy, int *numflag, int *options, 
+                  idxtype *perm, idxtype *iperm) 
+{
+  int i, j;
+  GraphType graph;
+  CtrlType ctrl;
+
+  if (*numflag == 1)
+    Change2CNumbering(*nvtxs, xadj, adjncy);
+
+  SetUpGraph(&graph, OP_OEMETIS, *nvtxs, 1, xadj, adjncy, NULL, NULL, 0);
+
+  if (options[0] == 0) {  /* Use the default parameters */
+    ctrl.CType = OEMETIS_CTYPE;
+    ctrl.IType = OEMETIS_ITYPE;
+    ctrl.RType = OEMETIS_RTYPE;
+    ctrl.dbglvl = OEMETIS_DBGLVL;
+  }
+  else {
+    ctrl.CType = options[OPTION_CTYPE];
+    ctrl.IType = options[OPTION_ITYPE];
+    ctrl.RType = options[OPTION_RTYPE];
+    ctrl.dbglvl = options[OPTION_DBGLVL];
+  }
+  ctrl.oflags  = 0;
+  ctrl.pfactor = -1;
+  ctrl.nseps   = 1;
+
+  ctrl.optype = OP_OEMETIS;
+  ctrl.CoarsenTo = 20;
+  ctrl.maxvwgt = 1.5*(idxsum(*nvtxs, graph.vwgt)/ctrl.CoarsenTo);
+
+  InitRandom(-1);
+
+  AllocateWorkSpace(&ctrl, &graph, 2);
+
+  IFSET(ctrl.dbglvl, DBG_TIME, InitTimers(&ctrl));
+  IFSET(ctrl.dbglvl, DBG_TIME, starttimer(ctrl.TotalTmr));
+
+  MlevelNestedDissection(&ctrl, &graph, iperm, ORDER_UNBALANCE_FRACTION, *nvtxs);
+
+  IFSET(ctrl.dbglvl, DBG_TIME, stoptimer(ctrl.TotalTmr));
+  IFSET(ctrl.dbglvl, DBG_TIME, PrintTimers(&ctrl));
+
+  for (i=0; i<*nvtxs; i++)
+    perm[iperm[i]] = i;
+
+  FreeWorkSpace(&ctrl, &graph);
+
+  if (*numflag == 1)
+    Change2FNumberingOrder(*nvtxs, xadj, adjncy, perm, iperm);
+}
+
+
+/*************************************************************************
+* This function is the entry point for ONCMETIS
+**************************************************************************/
+void METIS_NodeND(int *nvtxs, idxtype *xadj, idxtype *adjncy, int *numflag, int *options, 
+                  idxtype *perm, idxtype *iperm) 
+{
+  int i, ii, j, l, wflag, nflag;
+  GraphType graph;
+  CtrlType ctrl;
+  idxtype *cptr, *cind, *piperm;
+
+  if (*numflag == 1)
+    Change2CNumbering(*nvtxs, xadj, adjncy);
+
+  if (options[0] == 0) {  /* Use the default parameters */
+    ctrl.CType   = ONMETIS_CTYPE;
+    ctrl.IType   = ONMETIS_ITYPE;
+    ctrl.RType   = ONMETIS_RTYPE;
+    ctrl.dbglvl  = ONMETIS_DBGLVL;
+    ctrl.oflags  = ONMETIS_OFLAGS;
+    ctrl.pfactor = ONMETIS_PFACTOR;
+    ctrl.nseps   = ONMETIS_NSEPS;
+  }
+  else {
+    ctrl.CType   = options[OPTION_CTYPE];
+    ctrl.IType   = options[OPTION_ITYPE];
+    ctrl.RType   = options[OPTION_RTYPE];
+    ctrl.dbglvl  = options[OPTION_DBGLVL];
+    ctrl.oflags  = options[OPTION_OFLAGS];
+    ctrl.pfactor = options[OPTION_PFACTOR];
+    ctrl.nseps   = options[OPTION_NSEPS];
+  }
+  if (ctrl.nseps < 1)
+    ctrl.nseps = 1;
+
+  ctrl.optype = OP_ONMETIS;
+  ctrl.CoarsenTo = 100;
+
+  IFSET(ctrl.dbglvl, DBG_TIME, InitTimers(&ctrl));
+  IFSET(ctrl.dbglvl, DBG_TIME, starttimer(ctrl.TotalTmr));
+
+  InitRandom(-1);
+
+  if (ctrl.pfactor > 0) { 
+    /*============================================================
+    * Prune the dense columns
+    ==============================================================*/
+    piperm = idxmalloc(*nvtxs, "ONMETIS: piperm");
+
+    PruneGraph(&ctrl, &graph, *nvtxs, xadj, adjncy, piperm, (float)(0.1*ctrl.pfactor));
+  }
+  else if (ctrl.oflags&OFLAG_COMPRESS) {
+    /*============================================================
+    * Compress the graph 
+    ==============================================================*/
+    cptr = idxmalloc(*nvtxs+1, "ONMETIS: cptr");
+    cind = idxmalloc(*nvtxs, "ONMETIS: cind");
+
+    CompressGraph(&ctrl, &graph, *nvtxs, xadj, adjncy, cptr, cind);
+
+    if (graph.nvtxs >= COMPRESSION_FRACTION*(*nvtxs)) {
+      ctrl.oflags--; /* We actually performed no compression */
+      GKfree(&cptr, &cind, LTERM);
+    }
+    else if (2*graph.nvtxs < *nvtxs && ctrl.nseps == 1)
+      ctrl.nseps = 2;
+  }
+  else {
+    SetUpGraph(&graph, OP_ONMETIS, *nvtxs, 1, xadj, adjncy, NULL, NULL, 0);
+  }
+
+
+  /*=============================================================
+  * Do the nested dissection ordering 
+  --=============================================================*/
+  ctrl.maxvwgt = 1.5*(idxsum(graph.nvtxs, graph.vwgt)/ctrl.CoarsenTo);
+  AllocateWorkSpace(&ctrl, &graph, 2);
+
+  if (ctrl.oflags&OFLAG_CCMP) 
+    MlevelNestedDissectionCC(&ctrl, &graph, iperm, ORDER_UNBALANCE_FRACTION, graph.nvtxs);
+  else
+    MlevelNestedDissection(&ctrl, &graph, iperm, ORDER_UNBALANCE_FRACTION, graph.nvtxs);
+
+  FreeWorkSpace(&ctrl, &graph);
+
+  if (ctrl.pfactor > 0) { /* Order any prunned vertices */
+    if (graph.nvtxs < *nvtxs) { 
+      idxcopy(graph.nvtxs, iperm, perm);  /* Use perm as an auxiliary array */
+      for (i=0; i<graph.nvtxs; i++)
+        iperm[piperm[i]] = perm[i];
+      for (i=graph.nvtxs; i<*nvtxs; i++)
+        iperm[piperm[i]] = i;
+    }
+
+    GKfree(&piperm, LTERM);
+  }
+  else if (ctrl.oflags&OFLAG_COMPRESS) { /* Uncompress the ordering */
+    if (graph.nvtxs < COMPRESSION_FRACTION*(*nvtxs)) { 
+      /* construct perm from iperm */
+      for (i=0; i<graph.nvtxs; i++)
+        perm[iperm[i]] = i; 
+      for (l=ii=0; ii<graph.nvtxs; ii++) {
+        i = perm[ii];
+        for (j=cptr[i]; j<cptr[i+1]; j++)
+          iperm[cind[j]] = l++;
+      }
+    }
+
+    GKfree(&cptr, &cind, LTERM);
+  }
+
+
+  for (i=0; i<*nvtxs; i++)
+    perm[iperm[i]] = i;
+
+  IFSET(ctrl.dbglvl, DBG_TIME, stoptimer(ctrl.TotalTmr));
+  IFSET(ctrl.dbglvl, DBG_TIME, PrintTimers(&ctrl));
+
+  if (*numflag == 1)
+    Change2FNumberingOrder(*nvtxs, xadj, adjncy, perm, iperm);
+
+}
+
+
+/*************************************************************************
+* This function is the entry point for ONWMETIS. It requires weights on the
+* vertices. It is for the case that the matrix has been pre-compressed.
+**************************************************************************/
+void METIS_NodeWND(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, int *numflag, 
+                   int *options, idxtype *perm, idxtype *iperm) 
+{
+  int i, j, tvwgt;
+  GraphType graph;
+  CtrlType ctrl;
+
+  if (*numflag == 1)
+    Change2CNumbering(*nvtxs, xadj, adjncy);
+
+  SetUpGraph(&graph, OP_ONMETIS, *nvtxs, 1, xadj, adjncy, vwgt, NULL, 2);
+
+  if (options[0] == 0) {  /* Use the default parameters */
+    ctrl.CType = ONMETIS_CTYPE;
+    ctrl.IType = ONMETIS_ITYPE;
+    ctrl.RType = ONMETIS_RTYPE;
+    ctrl.dbglvl = ONMETIS_DBGLVL;
+  }
+  else {
+    ctrl.CType = options[OPTION_CTYPE];
+    ctrl.IType = options[OPTION_ITYPE];
+    ctrl.RType = options[OPTION_RTYPE];
+    ctrl.dbglvl = options[OPTION_DBGLVL];
+  }
+
+  ctrl.oflags  = OFLAG_COMPRESS;
+  ctrl.pfactor = 0;
+  ctrl.nseps = 2;
+  ctrl.optype = OP_ONMETIS;
+  ctrl.CoarsenTo = 100;
+  ctrl.maxvwgt = 1.5*(idxsum(*nvtxs, graph.vwgt)/ctrl.CoarsenTo);
+
+  InitRandom(-1);
+
+  AllocateWorkSpace(&ctrl, &graph, 2);
+
+  IFSET(ctrl.dbglvl, DBG_TIME, InitTimers(&ctrl));
+  IFSET(ctrl.dbglvl, DBG_TIME, starttimer(ctrl.TotalTmr));
+
+  MlevelNestedDissection(&ctrl, &graph, iperm, ORDER_UNBALANCE_FRACTION, *nvtxs);
+
+  IFSET(ctrl.dbglvl, DBG_TIME, stoptimer(ctrl.TotalTmr));
+  IFSET(ctrl.dbglvl, DBG_TIME, PrintTimers(&ctrl));
+
+  for (i=0; i<*nvtxs; i++)
+    perm[iperm[i]] = i;
+
+  FreeWorkSpace(&ctrl, &graph);
+
+  if (*numflag == 1)
+    Change2FNumberingOrder(*nvtxs, xadj, adjncy, perm, iperm);
+}
+
+
+
+
+/*************************************************************************
+* This function takes a graph and produces a bisection of it
+**************************************************************************/
+void MlevelNestedDissection(CtrlType *ctrl, GraphType *graph, idxtype *order, float ubfactor, int lastvtx)
+{
+  int i, j, nvtxs, nbnd, tvwgt, tpwgts2[2];
+  idxtype *label, *bndind;
+  GraphType lgraph, rgraph;
+
+  nvtxs = graph->nvtxs;
+
+  /* Determine the weights of the partitions */
+  tvwgt = idxsum(nvtxs, graph->vwgt);
+  tpwgts2[0] = tvwgt/2;
+  tpwgts2[1] = tvwgt-tpwgts2[0];
+
+  switch (ctrl->optype) {
+    case OP_OEMETIS:
+      MlevelEdgeBisection(ctrl, graph, tpwgts2, ubfactor);
+
+      IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->SepTmr));
+      ConstructMinCoverSeparator(ctrl, graph, ubfactor);
+      IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->SepTmr));
+
+      break;
+    case OP_ONMETIS:
+      MlevelNodeBisectionMultiple(ctrl, graph, tpwgts2, ubfactor);
+
+      IFSET(ctrl->dbglvl, DBG_SEPINFO, printf("Nvtxs: %6d, [%6d %6d %6d]\n", graph->nvtxs, graph->pwgts[0], graph->pwgts[1], graph->pwgts[2]));
+
+      break;
+  }
+
+  /* Order the nodes in the separator */
+  nbnd = graph->nbnd;
+  bndind = graph->bndind;
+  label = graph->label;
+  for (i=0; i<nbnd; i++) 
+    order[label[bndind[i]]] = --lastvtx;
+
+  SplitGraphOrder(ctrl, graph, &lgraph, &rgraph);
+
+  /* Free the memory of the top level graph */
+  GKfree(&graph->gdata, &graph->rdata, &graph->label, LTERM);
+
+  if (rgraph.nvtxs > MMDSWITCH) 
+    MlevelNestedDissection(ctrl, &rgraph, order, ubfactor, lastvtx);
+  else {
+    MMDOrder(ctrl, &rgraph, order, lastvtx); 
+    GKfree(&rgraph.gdata, &rgraph.rdata, &rgraph.label, LTERM);
+  }
+  if (lgraph.nvtxs > MMDSWITCH) 
+    MlevelNestedDissection(ctrl, &lgraph, order, ubfactor, lastvtx-rgraph.nvtxs);
+  else {
+    MMDOrder(ctrl, &lgraph, order, lastvtx-rgraph.nvtxs); 
+    GKfree(&lgraph.gdata, &lgraph.rdata, &lgraph.label, LTERM);
+  }
+}
+
+
+/*************************************************************************
+* This function takes a graph and produces a bisection of it
+**************************************************************************/
+void MlevelNestedDissectionCC(CtrlType *ctrl, GraphType *graph, idxtype *order, float ubfactor, int lastvtx)
+{
+  int i, j, nvtxs, nbnd, tvwgt, tpwgts2[2], nsgraphs, ncmps, rnvtxs;
+  idxtype *label, *bndind;
+  idxtype *cptr, *cind;
+  GraphType *sgraphs;
+
+  nvtxs = graph->nvtxs;
+
+  /* Determine the weights of the partitions */
+  tvwgt = idxsum(nvtxs, graph->vwgt);
+  tpwgts2[0] = tvwgt/2;
+  tpwgts2[1] = tvwgt-tpwgts2[0];
+
+  MlevelNodeBisectionMultiple(ctrl, graph, tpwgts2, ubfactor);
+  IFSET(ctrl->dbglvl, DBG_SEPINFO, printf("Nvtxs: %6d, [%6d %6d %6d]\n", graph->nvtxs, graph->pwgts[0], graph->pwgts[1], graph->pwgts[2]));
+
+  /* Order the nodes in the separator */
+  nbnd = graph->nbnd;
+  bndind = graph->bndind;
+  label = graph->label;
+  for (i=0; i<nbnd; i++) 
+    order[label[bndind[i]]] = --lastvtx;
+
+  cptr = idxmalloc(nvtxs, "MlevelNestedDissectionCC: cptr");
+  cind = idxmalloc(nvtxs, "MlevelNestedDissectionCC: cind");
+  ncmps = FindComponents(ctrl, graph, cptr, cind);
+
+/*
+  if (ncmps > 2)
+    printf("[%5d] has %3d components\n", nvtxs, ncmps);
+*/
+
+  sgraphs = (GraphType *)GKmalloc(ncmps*sizeof(GraphType), "MlevelNestedDissectionCC: sgraphs");
+
+  nsgraphs = SplitGraphOrderCC(ctrl, graph, sgraphs, ncmps, cptr, cind);
+
+  GKfree(&cptr, &cind, LTERM);
+
+  /* Free the memory of the top level graph */
+  GKfree(&graph->gdata, &graph->rdata, &graph->label, LTERM);
+
+  /* Go and process the subgraphs */
+  for (rnvtxs=i=0; i<nsgraphs; i++) {
+    if (sgraphs[i].adjwgt == NULL) {
+      MMDOrder(ctrl, sgraphs+i, order, lastvtx-rnvtxs);
+      GKfree(&sgraphs[i].gdata, &sgraphs[i].label, LTERM);
+    }
+    else {
+      MlevelNestedDissectionCC(ctrl, sgraphs+i, order, ubfactor, lastvtx-rnvtxs);
+    }
+    rnvtxs += sgraphs[i].nvtxs;
+  }
+
+  free(sgraphs);
+}
+
+
+
+/*************************************************************************
+* This function performs multilevel bisection. It performs multiple 
+* bisections and selects the best.
+**************************************************************************/
+void MlevelNodeBisectionMultiple(CtrlType *ctrl, GraphType *graph, int *tpwgts, float ubfactor)
+{
+  int i, nvtxs, cnvtxs, mincut, tmp;
+  GraphType *cgraph; 
+  idxtype *bestwhere;
+
+  if (ctrl->nseps == 1 || graph->nvtxs < (ctrl->oflags&OFLAG_COMPRESS ? 1000 : 2000)) {
+    MlevelNodeBisection(ctrl, graph, tpwgts, ubfactor);
+    return;
+  }
+
+  nvtxs = graph->nvtxs;
+
+  if (ctrl->oflags&OFLAG_COMPRESS) { /* Multiple separators at the original graph */
+    bestwhere = idxmalloc(nvtxs, "MlevelNodeBisection2: bestwhere");
+    mincut = nvtxs;
+
+    for (i=ctrl->nseps; i>0; i--) {
+      MlevelNodeBisection(ctrl, graph, tpwgts, ubfactor);
+
+      /* printf("%5d ", cgraph->mincut); */
+
+      if (graph->mincut < mincut) {
+        mincut = graph->mincut;
+        idxcopy(nvtxs, graph->where, bestwhere);
+      }
+
+      GKfree(&graph->rdata, LTERM);
+    
+      if (mincut == 0)
+        break;
+    }
+    /* printf("[%5d]\n", mincut); */
+
+    Allocate2WayNodePartitionMemory(ctrl, graph);
+    idxcopy(nvtxs, bestwhere, graph->where);
+    free(bestwhere);
+
+    Compute2WayNodePartitionParams(ctrl, graph);
+  }
+  else {  /* Coarsen it a bit */
+    ctrl->CoarsenTo = nvtxs-1;
+
+    cgraph = Coarsen2Way(ctrl, graph);
+
+    cnvtxs = cgraph->nvtxs;
+
+    bestwhere = idxmalloc(cnvtxs, "MlevelNodeBisection2: bestwhere");
+    mincut = nvtxs;
+
+    for (i=ctrl->nseps; i>0; i--) {
+      ctrl->CType += 20; /* This is a hack. Look at coarsen.c */
+      MlevelNodeBisection(ctrl, cgraph, tpwgts, ubfactor);
+
+      /* printf("%5d ", cgraph->mincut); */
+
+      if (cgraph->mincut < mincut) {
+        mincut = cgraph->mincut;
+        idxcopy(cnvtxs, cgraph->where, bestwhere);
+      }
+
+      GKfree(&cgraph->rdata, LTERM);
+    
+      if (mincut == 0)
+        break;
+    }
+    /* printf("[%5d]\n", mincut); */
+
+    Allocate2WayNodePartitionMemory(ctrl, cgraph);
+    idxcopy(cnvtxs, bestwhere, cgraph->where);
+    free(bestwhere);
+
+    Compute2WayNodePartitionParams(ctrl, cgraph);
+
+    Refine2WayNode(ctrl, graph, cgraph, ubfactor);
+  }
+
+}
+
+/*************************************************************************
+* This function performs multilevel bisection
+**************************************************************************/
+void MlevelNodeBisection(CtrlType *ctrl, GraphType *graph, int *tpwgts, float ubfactor)
+{
+  GraphType *cgraph;
+
+  ctrl->CoarsenTo = graph->nvtxs/8;
+  if (ctrl->CoarsenTo > 100)
+    ctrl->CoarsenTo = 100;
+  else if (ctrl->CoarsenTo < 40)
+    ctrl->CoarsenTo = 40;
+  ctrl->maxvwgt = 1.5*((tpwgts[0]+tpwgts[1])/ctrl->CoarsenTo);
+
+  cgraph = Coarsen2Way(ctrl, graph);
+
+  switch (ctrl->IType) {
+    case IPART_GGPKL:
+      Init2WayPartition(ctrl, cgraph, tpwgts, ubfactor);
+
+      IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->SepTmr));
+
+      Compute2WayPartitionParams(ctrl, cgraph);
+      ConstructSeparator(ctrl, cgraph, ubfactor);
+
+      IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->SepTmr));
+      break;
+    case IPART_GGPKLNODE:
+      InitSeparator(ctrl, cgraph, ubfactor);
+      break;
+  }
+
+  Refine2WayNode(ctrl, graph, cgraph, ubfactor);
+
+}
+
+
+
+
+/*************************************************************************
+* This function takes a graph and a bisection and splits it into two graphs.
+* This function relies on the fact that adjwgt is all equal to 1.
+**************************************************************************/
+void SplitGraphOrder(CtrlType *ctrl, GraphType *graph, GraphType *lgraph, GraphType *rgraph)
+{
+  int i, ii, j, k, l, istart, iend, mypart, nvtxs, snvtxs[3], snedges[3];
+  idxtype *xadj, *vwgt, *adjncy, *adjwgt, *adjwgtsum, *label, *where, *bndptr, *bndind;
+  idxtype *sxadj[2], *svwgt[2], *sadjncy[2], *sadjwgt[2], *sadjwgtsum[2], *slabel[2];
+  idxtype *rename;
+  idxtype *auxadjncy, *auxadjwgt;
+
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->SplitTmr));
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  vwgt = graph->vwgt;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+  adjwgtsum = graph->adjwgtsum;
+  label = graph->label;
+  where = graph->where;
+  bndptr = graph->bndptr;
+  bndind = graph->bndind;
+  ASSERT(bndptr != NULL);
+
+  rename = idxwspacemalloc(ctrl, nvtxs);
+  
+  snvtxs[0] = snvtxs[1] = snvtxs[2] = snedges[0] = snedges[1] = snedges[2] = 0;
+  for (i=0; i<nvtxs; i++) {
+    k = where[i];
+    rename[i] = snvtxs[k]++;
+    snedges[k] += xadj[i+1]-xadj[i];
+  }
+
+  SetUpSplitGraph(graph, lgraph, snvtxs[0], snedges[0]);
+  sxadj[0] = lgraph->xadj;
+  svwgt[0] = lgraph->vwgt;
+  sadjwgtsum[0] = lgraph->adjwgtsum;
+  sadjncy[0] = lgraph->adjncy; 
+  sadjwgt[0] = lgraph->adjwgt; 
+  slabel[0] = lgraph->label;
+
+  SetUpSplitGraph(graph, rgraph, snvtxs[1], snedges[1]);
+  sxadj[1] = rgraph->xadj;
+  svwgt[1] = rgraph->vwgt;
+  sadjwgtsum[1] = rgraph->adjwgtsum;
+  sadjncy[1] = rgraph->adjncy; 
+  sadjwgt[1] = rgraph->adjwgt; 
+  slabel[1] = rgraph->label;
+
+  /* Go and use bndptr to also mark the boundary nodes in the two partitions */
+  for (ii=0; ii<graph->nbnd; ii++) {
+    i = bndind[ii];
+    for (j=xadj[i]; j<xadj[i+1]; j++)
+      bndptr[adjncy[j]] = 1;
+  }
+
+  snvtxs[0] = snvtxs[1] = snedges[0] = snedges[1] = 0;
+  sxadj[0][0] = sxadj[1][0] = 0;
+  for (i=0; i<nvtxs; i++) {
+    if ((mypart = where[i]) == 2)
+      continue;
+
+    istart = xadj[i];
+    iend = xadj[i+1];
+    if (bndptr[i] == -1) { /* This is an interior vertex */
+      auxadjncy = sadjncy[mypart] + snedges[mypart] - istart;
+      for(j=istart; j<iend; j++) 
+        auxadjncy[j] = adjncy[j];
+      snedges[mypart] += iend-istart;
+    }
+    else {
+      auxadjncy = sadjncy[mypart];
+      l = snedges[mypart];
+      for (j=istart; j<iend; j++) {
+        k = adjncy[j];
+        if (where[k] == mypart) 
+          auxadjncy[l++] = k;
+      }
+      snedges[mypart] = l;
+    }
+
+    svwgt[mypart][snvtxs[mypart]] = vwgt[i];
+    sadjwgtsum[mypart][snvtxs[mypart]] = snedges[mypart]-sxadj[mypart][snvtxs[mypart]];
+    slabel[mypart][snvtxs[mypart]] = label[i];
+    sxadj[mypart][++snvtxs[mypart]] = snedges[mypart];
+  }
+
+  for (mypart=0; mypart<2; mypart++) {
+    iend = snedges[mypart];
+    idxset(iend, 1, sadjwgt[mypart]);
+
+    auxadjncy = sadjncy[mypart];
+    for (i=0; i<iend; i++) 
+      auxadjncy[i] = rename[auxadjncy[i]];
+  }
+
+  lgraph->nvtxs = snvtxs[0];
+  lgraph->nedges = snedges[0];
+  rgraph->nvtxs = snvtxs[1];
+  rgraph->nedges = snedges[1];
+
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->SplitTmr));
+
+  idxwspacefree(ctrl, nvtxs);
+
+}
+
+/*************************************************************************
+* This function uses MMD to order the graph. The vertices are numbered
+* from lastvtx downwards
+**************************************************************************/
+void MMDOrder(CtrlType *ctrl, GraphType *graph, idxtype *order, int lastvtx)
+{
+  int i, j, k, nvtxs, nofsub, firstvtx;
+  idxtype *xadj, *adjncy, *label;
+  idxtype *perm, *iperm, *head, *qsize, *list, *marker;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+
+  /* Relabel the vertices so that it starts from 1 */
+  k = xadj[nvtxs];
+  for (i=0; i<k; i++)
+    adjncy[i]++;
+  for (i=0; i<nvtxs+1; i++)
+    xadj[i]++;
+
+  perm = idxmalloc(6*(nvtxs+5), "MMDOrder: perm");
+  iperm = perm + nvtxs + 5;
+  head = iperm + nvtxs + 5;
+  qsize = head + nvtxs + 5;
+  list = qsize + nvtxs + 5;
+  marker = list + nvtxs + 5;
+
+  genmmd(nvtxs, xadj, adjncy, iperm, perm, 1, head, qsize, list, marker, MAXIDX, &nofsub);
+
+  label = graph->label;
+  firstvtx = lastvtx-nvtxs;
+  for (i=0; i<nvtxs; i++)
+    order[label[i]] = firstvtx+iperm[i]-1;
+
+  free(perm);
+
+  /* Relabel the vertices so that it starts from 0 */
+  for (i=0; i<nvtxs+1; i++)
+    xadj[i]--;
+  k = xadj[nvtxs];
+  for (i=0; i<k; i++)
+    adjncy[i]--;
+}
+
+
+/*************************************************************************
+* This function takes a graph and a bisection and splits it into two graphs.
+* It relies on the fact that adjwgt is all set to 1.
+**************************************************************************/
+int SplitGraphOrderCC(CtrlType *ctrl, GraphType *graph, GraphType *sgraphs, int ncmps, idxtype *cptr, idxtype *cind)
+{
+  int i, ii, iii, j, k, l, istart, iend, mypart, nvtxs, snvtxs, snedges;
+  idxtype *xadj, *vwgt, *adjncy, *adjwgt, *adjwgtsum, *label, *where, *bndptr, *bndind;
+  idxtype *sxadj, *svwgt, *sadjncy, *sadjwgt, *sadjwgtsum, *slabel;
+  idxtype *rename;
+  idxtype *auxadjncy, *auxadjwgt;
+
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->SplitTmr));
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  vwgt = graph->vwgt;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+  adjwgtsum = graph->adjwgtsum;
+  label = graph->label;
+  where = graph->where;
+  bndptr = graph->bndptr;
+  bndind = graph->bndind;
+  ASSERT(bndptr != NULL);
+
+  /* Go and use bndptr to also mark the boundary nodes in the two partitions */
+  for (ii=0; ii<graph->nbnd; ii++) {
+    i = bndind[ii];
+    for (j=xadj[i]; j<xadj[i+1]; j++)
+      bndptr[adjncy[j]] = 1;
+  }
+
+  rename = idxwspacemalloc(ctrl, nvtxs);
+  
+  /* Go and split the graph a component at a time */
+  for (iii=0; iii<ncmps; iii++) {
+    RandomPermute(cptr[iii+1]-cptr[iii], cind+cptr[iii], 0);
+    snvtxs = snedges = 0;
+    for (j=cptr[iii]; j<cptr[iii+1]; j++) {
+      i = cind[j];
+      rename[i] = snvtxs++;
+      snedges += xadj[i+1]-xadj[i];
+    }
+
+    SetUpSplitGraph(graph, sgraphs+iii, snvtxs, snedges);
+    sxadj = sgraphs[iii].xadj;
+    svwgt = sgraphs[iii].vwgt;
+    sadjwgtsum = sgraphs[iii].adjwgtsum;
+    sadjncy = sgraphs[iii].adjncy;
+    sadjwgt = sgraphs[iii].adjwgt;
+    slabel = sgraphs[iii].label;
+
+    snvtxs = snedges = sxadj[0] = 0;
+    for (ii=cptr[iii]; ii<cptr[iii+1]; ii++) {
+      i = cind[ii];
+
+      istart = xadj[i];
+      iend = xadj[i+1];
+      if (bndptr[i] == -1) { /* This is an interior vertex */
+        auxadjncy = sadjncy + snedges - istart;
+        auxadjwgt = sadjwgt + snedges - istart;
+        for(j=istart; j<iend; j++) 
+          auxadjncy[j] = adjncy[j];
+        snedges += iend-istart;
+      }
+      else {
+        l = snedges;
+        for (j=istart; j<iend; j++) {
+          k = adjncy[j];
+          if (where[k] != 2) 
+            sadjncy[l++] = k;
+        }
+        snedges = l;
+      }
+
+      svwgt[snvtxs] = vwgt[i];
+      sadjwgtsum[snvtxs] = snedges-sxadj[snvtxs];
+      slabel[snvtxs] = label[i];
+      sxadj[++snvtxs] = snedges;
+    }
+
+    idxset(snedges, 1, sadjwgt);
+    for (i=0; i<snedges; i++) 
+      sadjncy[i] = rename[sadjncy[i]];
+
+    sgraphs[iii].nvtxs = snvtxs;
+    sgraphs[iii].nedges = snedges;
+    sgraphs[iii].ncon = 1;
+
+    if (snvtxs < MMDSWITCH)
+      sgraphs[iii].adjwgt = NULL;  /* A marker to call MMD on the driver */
+  }
+
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->SplitTmr));
+
+  idxwspacefree(ctrl, nvtxs);
+
+  return ncmps;
+
+}
+
+
+
+
+
diff --git a/contrib/Metis/parmetis.c b/contrib/Metis/parmetis.c
new file mode 100644
index 0000000000..fde3796737
--- /dev/null
+++ b/contrib/Metis/parmetis.c
@@ -0,0 +1,371 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * parmetis.c
+ *
+ * This file contains top level routines that are used by ParMETIS
+ *
+ * Started 10/14/97
+ * George
+ *
+ * $Id: parmetis.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function is the entry point for KMETIS with seed specification
+* in options[7] 
+**************************************************************************/
+void METIS_PartGraphKway2(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, 
+                         idxtype *adjwgt, int *wgtflag, int *numflag, int *nparts, 
+                         int *options, int *edgecut, idxtype *part)
+{
+  int i;
+  float *tpwgts;
+
+  tpwgts = fmalloc(*nparts, "KMETIS: tpwgts");
+  for (i=0; i<*nparts; i++) 
+    tpwgts[i] = 1.0/(1.0*(*nparts));
+
+  METIS_WPartGraphKway2(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, 
+                       tpwgts, options, edgecut, part);
+
+  free(tpwgts);
+}
+
+
+/*************************************************************************
+* This function is the entry point for KWMETIS with seed specification
+* in options[7] 
+**************************************************************************/
+void METIS_WPartGraphKway2(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, 
+                          idxtype *adjwgt, int *wgtflag, int *numflag, int *nparts, 
+                          float *tpwgts, int *options, int *edgecut, idxtype *part)
+{
+  int i, j;
+  GraphType graph;
+  CtrlType ctrl;
+
+  if (*numflag == 1)
+    Change2CNumbering(*nvtxs, xadj, adjncy);
+
+  SetUpGraph(&graph, OP_KMETIS, *nvtxs, 1, xadj, adjncy, vwgt, adjwgt, *wgtflag);
+
+  if (options[0] == 0) {  /* Use the default parameters */
+    ctrl.CType = KMETIS_CTYPE;
+    ctrl.IType = KMETIS_ITYPE;
+    ctrl.RType = KMETIS_RTYPE;
+    ctrl.dbglvl = KMETIS_DBGLVL;
+  }
+  else {
+    ctrl.CType = options[OPTION_CTYPE];
+    ctrl.IType = options[OPTION_ITYPE];
+    ctrl.RType = options[OPTION_RTYPE];
+    ctrl.dbglvl = options[OPTION_DBGLVL];
+  }
+  ctrl.optype = OP_KMETIS;
+  ctrl.CoarsenTo = 20*(*nparts);
+  ctrl.maxvwgt = 1.5*((graph.vwgt ? idxsum(*nvtxs, graph.vwgt) : (*nvtxs))/ctrl.CoarsenTo);
+
+  InitRandom(options[7]);
+
+  AllocateWorkSpace(&ctrl, &graph, *nparts);
+
+  IFSET(ctrl.dbglvl, DBG_TIME, InitTimers(&ctrl));
+  IFSET(ctrl.dbglvl, DBG_TIME, starttimer(ctrl.TotalTmr));
+
+  *edgecut = MlevelKWayPartitioning(&ctrl, &graph, *nparts, part, tpwgts, 1.03);
+
+  IFSET(ctrl.dbglvl, DBG_TIME, stoptimer(ctrl.TotalTmr));
+  IFSET(ctrl.dbglvl, DBG_TIME, PrintTimers(&ctrl));
+
+  FreeWorkSpace(&ctrl, &graph);
+
+  if (*numflag == 1)
+    Change2FNumbering(*nvtxs, xadj, adjncy, part);
+}
+
+
+/*************************************************************************
+* This function is the entry point for the node ND code for ParMETIS
+**************************************************************************/
+void METIS_NodeNDP(int nvtxs, idxtype *xadj, idxtype *adjncy, int npes, 
+                   int *options, idxtype *perm, idxtype *iperm, idxtype *sizes) 
+{
+  int i, ii, j, l, wflag, nflag;
+  GraphType graph;
+  CtrlType ctrl;
+  idxtype *cptr, *cind;
+
+  if (options[0] == 0) {  /* Use the default parameters */
+    ctrl.CType   = ONMETIS_CTYPE;
+    ctrl.IType   = ONMETIS_ITYPE;
+    ctrl.RType   = ONMETIS_RTYPE;
+    ctrl.dbglvl  = ONMETIS_DBGLVL;
+    ctrl.oflags  = ONMETIS_OFLAGS;
+    ctrl.pfactor = ONMETIS_PFACTOR;
+    ctrl.nseps   = ONMETIS_NSEPS;
+  }
+  else {
+    ctrl.CType   = options[OPTION_CTYPE];
+    ctrl.IType   = options[OPTION_ITYPE];
+    ctrl.RType   = options[OPTION_RTYPE];
+    ctrl.dbglvl  = options[OPTION_DBGLVL];
+    ctrl.oflags  = options[OPTION_OFLAGS];
+    ctrl.pfactor = options[OPTION_PFACTOR];
+    ctrl.nseps   = options[OPTION_NSEPS];
+  }
+  if (ctrl.nseps < 1)
+    ctrl.nseps = 1;
+
+  ctrl.optype = OP_ONMETIS;
+  ctrl.CoarsenTo = 100;
+
+  IFSET(ctrl.dbglvl, DBG_TIME, InitTimers(&ctrl));
+  IFSET(ctrl.dbglvl, DBG_TIME, starttimer(ctrl.TotalTmr));
+
+  InitRandom(-1);
+
+  if (ctrl.oflags&OFLAG_COMPRESS) {
+    /*============================================================
+    * Compress the graph 
+    ==============================================================*/
+    cptr = idxmalloc(nvtxs+1, "ONMETIS: cptr");
+    cind = idxmalloc(nvtxs, "ONMETIS: cind");
+
+    CompressGraph(&ctrl, &graph, nvtxs, xadj, adjncy, cptr, cind);
+
+    if (graph.nvtxs >= COMPRESSION_FRACTION*(nvtxs)) {
+      ctrl.oflags--; /* We actually performed no compression */
+      GKfree(&cptr, &cind, LTERM);
+    }
+    else if (2*graph.nvtxs < nvtxs && ctrl.nseps == 1)
+      ctrl.nseps = 2;
+  }
+  else {
+    SetUpGraph(&graph, OP_ONMETIS, nvtxs, 1, xadj, adjncy, NULL, NULL, 0);
+  }
+
+
+  /*=============================================================
+  * Do the nested dissection ordering 
+  --=============================================================*/
+  ctrl.maxvwgt = 1.5*(idxsum(graph.nvtxs, graph.vwgt)/ctrl.CoarsenTo);
+  AllocateWorkSpace(&ctrl, &graph, 2);
+
+  idxset(2*npes-1, 0, sizes);
+  MlevelNestedDissectionP(&ctrl, &graph, iperm, graph.nvtxs, npes, 0, sizes);
+
+  FreeWorkSpace(&ctrl, &graph);
+
+  if (ctrl.oflags&OFLAG_COMPRESS) { /* Uncompress the ordering */
+    if (graph.nvtxs < COMPRESSION_FRACTION*(nvtxs)) { 
+      /* construct perm from iperm */
+      for (i=0; i<graph.nvtxs; i++)
+        perm[iperm[i]] = i; 
+      for (l=ii=0; ii<graph.nvtxs; ii++) {
+        i = perm[ii];
+        for (j=cptr[i]; j<cptr[i+1]; j++)
+          iperm[cind[j]] = l++;
+      }
+    }
+
+    GKfree(&cptr, &cind, LTERM);
+  }
+
+
+  for (i=0; i<nvtxs; i++)
+    perm[iperm[i]] = i;
+
+  IFSET(ctrl.dbglvl, DBG_TIME, stoptimer(ctrl.TotalTmr));
+  IFSET(ctrl.dbglvl, DBG_TIME, PrintTimers(&ctrl));
+
+}
+
+
+
+/*************************************************************************
+* This function takes a graph and produces a bisection of it
+**************************************************************************/
+void MlevelNestedDissectionP(CtrlType *ctrl, GraphType *graph, idxtype *order, int lastvtx, 
+                             int npes, int cpos, idxtype *sizes)
+{
+  int i, j, nvtxs, nbnd, tvwgt, tpwgts2[2];
+  idxtype *label, *bndind;
+  GraphType lgraph, rgraph;
+  float ubfactor;
+
+  nvtxs = graph->nvtxs;
+
+  if (nvtxs == 0) {
+    GKfree(&graph->gdata, &graph->rdata, &graph->label, LTERM);
+    return;
+  }
+
+  /* Determine the weights of the partitions */
+  tvwgt = idxsum(nvtxs, graph->vwgt);
+  tpwgts2[0] = tvwgt/2;
+  tpwgts2[1] = tvwgt-tpwgts2[0];
+
+  if (cpos >= npes-1) 
+    ubfactor = ORDER_UNBALANCE_FRACTION;
+  else 
+    ubfactor = 1.05;
+
+
+  MlevelNodeBisectionMultiple(ctrl, graph, tpwgts2, ubfactor);
+
+  IFSET(ctrl->dbglvl, DBG_SEPINFO, printf("Nvtxs: %6d, [%6d %6d %6d]\n", graph->nvtxs, graph->pwgts[0], graph->pwgts[1], graph->pwgts[2]));
+
+  if (cpos < npes-1) {
+    sizes[2*npes-2-cpos] = graph->pwgts[2];
+    sizes[2*npes-2-(2*cpos+1)] = graph->pwgts[1];
+    sizes[2*npes-2-(2*cpos+2)] = graph->pwgts[0];
+  }
+
+  /* Order the nodes in the separator */
+  nbnd = graph->nbnd;
+  bndind = graph->bndind;
+  label = graph->label;
+  for (i=0; i<nbnd; i++) 
+    order[label[bndind[i]]] = --lastvtx;
+
+  SplitGraphOrder(ctrl, graph, &lgraph, &rgraph);
+
+  /* Free the memory of the top level graph */
+  GKfree(&graph->gdata, &graph->rdata, &graph->label, LTERM);
+
+  if (rgraph.nvtxs > MMDSWITCH || 2*cpos+1 < npes-1) 
+    MlevelNestedDissectionP(ctrl, &rgraph, order, lastvtx, npes, 2*cpos+1, sizes);
+  else {
+    MMDOrder(ctrl, &rgraph, order, lastvtx); 
+    GKfree(&rgraph.gdata, &rgraph.rdata, &rgraph.label, LTERM);
+  }
+  if (lgraph.nvtxs > MMDSWITCH || 2*cpos+2 < npes-1) 
+    MlevelNestedDissectionP(ctrl, &lgraph, order, lastvtx-rgraph.nvtxs, npes, 2*cpos+2, sizes);
+  else {
+    MMDOrder(ctrl, &lgraph, order, lastvtx-rgraph.nvtxs); 
+    GKfree(&lgraph.gdata, &lgraph.rdata, &lgraph.label, LTERM);
+  }
+}
+
+
+
+
+/*************************************************************************
+* This function is the entry point for ONWMETIS. It requires weights on the
+* vertices. It is for the case that the matrix has been pre-compressed.
+**************************************************************************/
+void METIS_NodeComputeSeparator(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, 
+           idxtype *adjwgt, int *options, int *sepsize, idxtype *part) 
+{
+  int i, j, tvwgt, tpwgts[2];
+  GraphType graph;
+  CtrlType ctrl;
+
+  SetUpGraph(&graph, OP_ONMETIS, *nvtxs, 1, xadj, adjncy, vwgt, adjwgt, 3);
+  tvwgt = idxsum(*nvtxs, graph.vwgt);
+
+  if (options[0] == 0) {  /* Use the default parameters */
+    ctrl.CType = ONMETIS_CTYPE;
+    ctrl.IType = ONMETIS_ITYPE;
+    ctrl.RType = ONMETIS_RTYPE;
+    ctrl.dbglvl = ONMETIS_DBGLVL;
+  }
+  else {
+    ctrl.CType = options[OPTION_CTYPE];
+    ctrl.IType = options[OPTION_ITYPE];
+    ctrl.RType = options[OPTION_RTYPE];
+    ctrl.dbglvl = options[OPTION_DBGLVL];
+  }
+
+  ctrl.oflags  = 0;
+  ctrl.pfactor = 0;
+  ctrl.nseps = 1;
+  ctrl.optype = OP_ONMETIS;
+  ctrl.CoarsenTo = amin(100, *nvtxs-1);
+  ctrl.maxvwgt = 1.5*tvwgt/ctrl.CoarsenTo;
+
+  InitRandom(options[7]);
+
+  AllocateWorkSpace(&ctrl, &graph, 2);
+
+  /*============================================================
+   * Perform the bisection
+   *============================================================*/ 
+  tpwgts[0] = tvwgt/2;
+  tpwgts[1] = tvwgt-tpwgts[0];
+
+  MlevelNodeBisectionMultiple(&ctrl, &graph, tpwgts, 1.05);
+
+  *sepsize = graph.pwgts[2];
+  idxcopy(*nvtxs, graph.where, part);
+
+  GKfree(&graph.gdata, &graph.rdata, &graph.label, LTERM);
+
+
+  FreeWorkSpace(&ctrl, &graph);
+
+}
+
+
+
+/*************************************************************************
+* This function is the entry point for ONWMETIS. It requires weights on the
+* vertices. It is for the case that the matrix has been pre-compressed.
+**************************************************************************/
+void METIS_EdgeComputeSeparator(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, 
+           idxtype *adjwgt, int *options, int *sepsize, idxtype *part) 
+{
+  int i, j, tvwgt, tpwgts[2];
+  GraphType graph;
+  CtrlType ctrl;
+
+  SetUpGraph(&graph, OP_ONMETIS, *nvtxs, 1, xadj, adjncy, vwgt, adjwgt, 3);
+  tvwgt = idxsum(*nvtxs, graph.vwgt);
+
+  if (options[0] == 0) {  /* Use the default parameters */
+    ctrl.CType = ONMETIS_CTYPE;
+    ctrl.IType = ONMETIS_ITYPE;
+    ctrl.RType = ONMETIS_RTYPE;
+    ctrl.dbglvl = ONMETIS_DBGLVL;
+  }
+  else {
+    ctrl.CType = options[OPTION_CTYPE];
+    ctrl.IType = options[OPTION_ITYPE];
+    ctrl.RType = options[OPTION_RTYPE];
+    ctrl.dbglvl = options[OPTION_DBGLVL];
+  }
+
+  ctrl.oflags  = 0;
+  ctrl.pfactor = 0;
+  ctrl.nseps = 1;
+  ctrl.optype = OP_OEMETIS;
+  ctrl.CoarsenTo = amin(100, *nvtxs-1);
+  ctrl.maxvwgt = 1.5*tvwgt/ctrl.CoarsenTo;
+
+  InitRandom(options[7]);
+
+  AllocateWorkSpace(&ctrl, &graph, 2);
+
+  /*============================================================
+   * Perform the bisection
+   *============================================================*/ 
+  tpwgts[0] = tvwgt/2;
+  tpwgts[1] = tvwgt-tpwgts[0];
+
+  MlevelEdgeBisection(&ctrl, &graph, tpwgts, 1.05);
+  ConstructMinCoverSeparator(&ctrl, &graph, 1.05);
+
+  *sepsize = graph.pwgts[2];
+  idxcopy(*nvtxs, graph.where, part);
+
+  GKfree(&graph.gdata, &graph.rdata, &graph.label, LTERM);
+
+
+  FreeWorkSpace(&ctrl, &graph);
+
+}
diff --git a/contrib/Metis/pmetis.c b/contrib/Metis/pmetis.c
new file mode 100644
index 0000000000..76184117de
--- /dev/null
+++ b/contrib/Metis/pmetis.c
@@ -0,0 +1,341 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * pmetis.c
+ *
+ * This file contains the top level routines for the multilevel recursive
+ * bisection algorithm PMETIS.
+ *
+ * Started 7/24/97
+ * George
+ *
+ * $Id: pmetis.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function is the entry point for PMETIS
+**************************************************************************/
+void METIS_PartGraphRecursive(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, 
+                              idxtype *adjwgt, int *wgtflag, int *numflag, int *nparts, 
+                              int *options, int *edgecut, idxtype *part)
+{
+  int i;
+  float *tpwgts;
+
+  tpwgts = fmalloc(*nparts, "KMETIS: tpwgts");
+  for (i=0; i<*nparts; i++) 
+    tpwgts[i] = 1.0/(1.0*(*nparts));
+
+  METIS_WPartGraphRecursive(nvtxs, xadj, adjncy, vwgt, adjwgt, wgtflag, numflag, nparts, 
+                            tpwgts, options, edgecut, part);
+
+  free(tpwgts);
+}
+
+
+
+/*************************************************************************
+* This function is the entry point for PWMETIS that accepts exact weights
+* for the target partitions
+**************************************************************************/
+void METIS_WPartGraphRecursive(int *nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt, 
+                               idxtype *adjwgt, int *wgtflag, int *numflag, int *nparts, 
+                               float *tpwgts, int *options, int *edgecut, idxtype *part)
+{
+  int i, j;
+  GraphType graph;
+  CtrlType ctrl;
+  float *mytpwgts;
+
+  if (*numflag == 1)
+    Change2CNumbering(*nvtxs, xadj, adjncy);
+
+  SetUpGraph(&graph, OP_PMETIS, *nvtxs, 1, xadj, adjncy, vwgt, adjwgt, *wgtflag);
+
+  if (options[0] == 0) {  /* Use the default parameters */
+    ctrl.CType = PMETIS_CTYPE;
+    ctrl.IType = PMETIS_ITYPE;
+    ctrl.RType = PMETIS_RTYPE;
+    ctrl.dbglvl = PMETIS_DBGLVL;
+  }
+  else {
+    ctrl.CType = options[OPTION_CTYPE];
+    ctrl.IType = options[OPTION_ITYPE];
+    ctrl.RType = options[OPTION_RTYPE];
+    ctrl.dbglvl = options[OPTION_DBGLVL];
+  }
+  ctrl.optype = OP_PMETIS;
+  ctrl.CoarsenTo = 20;
+  ctrl.maxvwgt = 1.5*(idxsum(*nvtxs, graph.vwgt)/ctrl.CoarsenTo);
+
+  mytpwgts = fmalloc(*nparts, "PWMETIS: mytpwgts");
+  for (i=0; i<*nparts; i++) 
+    mytpwgts[i] = tpwgts[i];
+
+  InitRandom(-1);
+
+  AllocateWorkSpace(&ctrl, &graph, *nparts);
+
+  IFSET(ctrl.dbglvl, DBG_TIME, InitTimers(&ctrl));
+  IFSET(ctrl.dbglvl, DBG_TIME, starttimer(ctrl.TotalTmr));
+
+  *edgecut = MlevelRecursiveBisection(&ctrl, &graph, *nparts, part, mytpwgts, 1.000, 0);
+
+  IFSET(ctrl.dbglvl, DBG_TIME, stoptimer(ctrl.TotalTmr));
+  IFSET(ctrl.dbglvl, DBG_TIME, PrintTimers(&ctrl));
+
+  FreeWorkSpace(&ctrl, &graph);
+  free(mytpwgts);
+
+  if (*numflag == 1)
+    Change2FNumbering(*nvtxs, xadj, adjncy, part);
+}
+
+
+
+/*************************************************************************
+* This function takes a graph and produces a bisection of it
+**************************************************************************/
+int MlevelRecursiveBisection(CtrlType *ctrl, GraphType *graph, int nparts, idxtype *part, float *tpwgts, float ubfactor, int fpart)
+{
+  int i, j, nvtxs, cut, tvwgt, tpwgts2[2];
+  idxtype *label, *where;
+  GraphType lgraph, rgraph;
+  float wsum;
+
+  nvtxs = graph->nvtxs;
+  if (nvtxs == 0) {
+    printf("\t***Cannot bisect a graph with 0 vertices!\n\t***You are trying to partition a graph into too many parts!\n");
+    return 0;
+  }
+
+  /* Determine the weights of the partitions */
+  tvwgt = idxsum(nvtxs, graph->vwgt);
+  tpwgts2[0] = tvwgt*ssum(nparts/2, tpwgts);
+  tpwgts2[1] = tvwgt-tpwgts2[0];
+
+  MlevelEdgeBisection(ctrl, graph, tpwgts2, ubfactor);
+  cut = graph->mincut;
+
+  /* printf("%5d %5d %5d [%5d %f]\n", tpwgts2[0], tpwgts2[1], cut, tvwgt, ssum(nparts/2, tpwgts));*/
+
+  label = graph->label;
+  where = graph->where;
+  for (i=0; i<nvtxs; i++)
+    part[label[i]] = where[i] + fpart;
+
+  if (nparts > 2) {
+    SplitGraphPart(ctrl, graph, &lgraph, &rgraph);
+    /* printf("%d %d\n", lgraph.nvtxs, rgraph.nvtxs); */
+  }
+
+
+  /* Free the memory of the top level graph */
+  GKfree(&graph->gdata, &graph->rdata, &graph->label, LTERM);
+
+  /* Scale the fractions in the tpwgts according to the true weight */
+  wsum = ssum(nparts/2, tpwgts);
+  sscale(nparts/2, 1.0/wsum, tpwgts);
+  sscale(nparts-nparts/2, 1.0/(1.0-wsum), tpwgts+nparts/2);
+  /*
+  for (i=0; i<nparts; i++)
+    printf("%5.3f ", tpwgts[i]);
+  printf("[%5.3f]\n", wsum);
+  */
+
+  /* Do the recursive call */
+  if (nparts > 3) {
+    cut += MlevelRecursiveBisection(ctrl, &lgraph, nparts/2, part, tpwgts, ubfactor, fpart);
+    cut += MlevelRecursiveBisection(ctrl, &rgraph, nparts-nparts/2, part, tpwgts+nparts/2, ubfactor, fpart+nparts/2);
+  }
+  else if (nparts == 3) {
+    cut += MlevelRecursiveBisection(ctrl, &rgraph, nparts-nparts/2, part, tpwgts+nparts/2, ubfactor, fpart+nparts/2);
+    GKfree(&lgraph.gdata, &lgraph.label, LTERM);
+  }
+
+  return cut;
+
+}
+
+
+/*************************************************************************
+* This function performs multilevel bisection
+**************************************************************************/
+void MlevelEdgeBisection(CtrlType *ctrl, GraphType *graph, int *tpwgts, float ubfactor)
+{
+  GraphType *cgraph;
+
+  cgraph = Coarsen2Way(ctrl, graph);
+
+  Init2WayPartition(ctrl, cgraph, tpwgts, ubfactor);
+
+  Refine2Way(ctrl, graph, cgraph, tpwgts, ubfactor);
+
+/*
+  IsConnectedSubdomain(ctrl, graph, 0);
+  IsConnectedSubdomain(ctrl, graph, 1);
+*/
+}
+
+
+
+
+/*************************************************************************
+* This function takes a graph and a bisection and splits it into two graphs.
+**************************************************************************/
+void SplitGraphPart(CtrlType *ctrl, GraphType *graph, GraphType *lgraph, GraphType *rgraph)
+{
+  int i, j, k, kk, l, istart, iend, mypart, nvtxs, ncon, snvtxs[2], snedges[2], sum;
+  idxtype *xadj, *vwgt, *adjncy, *adjwgt, *adjwgtsum, *label, *where, *bndptr;
+  idxtype *sxadj[2], *svwgt[2], *sadjncy[2], *sadjwgt[2], *sadjwgtsum[2], *slabel[2];
+  idxtype *rename;
+  idxtype *auxadjncy, *auxadjwgt;
+  float *nvwgt, *snvwgt[2], *npwgts;
+
+
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->SplitTmr));
+
+  nvtxs = graph->nvtxs;
+  ncon = graph->ncon;
+  xadj = graph->xadj;
+  vwgt = graph->vwgt;
+  nvwgt = graph->nvwgt;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+  adjwgtsum = graph->adjwgtsum;
+  label = graph->label;
+  where = graph->where;
+  bndptr = graph->bndptr;
+  npwgts = graph->npwgts;
+
+  ASSERT(bndptr != NULL);
+
+  rename = idxwspacemalloc(ctrl, nvtxs);
+  
+  snvtxs[0] = snvtxs[1] = snedges[0] = snedges[1] = 0;
+  for (i=0; i<nvtxs; i++) {
+    k = where[i];
+    rename[i] = snvtxs[k]++;
+    snedges[k] += xadj[i+1]-xadj[i];
+  }
+
+  SetUpSplitGraph(graph, lgraph, snvtxs[0], snedges[0]);
+  sxadj[0] = lgraph->xadj;
+  svwgt[0] = lgraph->vwgt;
+  snvwgt[0] = lgraph->nvwgt;
+  sadjwgtsum[0] = lgraph->adjwgtsum;
+  sadjncy[0] = lgraph->adjncy; 	
+  sadjwgt[0] = lgraph->adjwgt; 
+  slabel[0] = lgraph->label;
+
+  SetUpSplitGraph(graph, rgraph, snvtxs[1], snedges[1]);
+  sxadj[1] = rgraph->xadj;
+  svwgt[1] = rgraph->vwgt;
+  snvwgt[1] = rgraph->nvwgt;
+  sadjwgtsum[1] = rgraph->adjwgtsum;
+  sadjncy[1] = rgraph->adjncy; 	
+  sadjwgt[1] = rgraph->adjwgt; 
+  slabel[1] = rgraph->label;
+
+  snvtxs[0] = snvtxs[1] = snedges[0] = snedges[1] = 0;
+  sxadj[0][0] = sxadj[1][0] = 0;
+  for (i=0; i<nvtxs; i++) {
+    mypart = where[i];
+    sum = adjwgtsum[i];
+
+    istart = xadj[i];
+    iend = xadj[i+1];
+    if (bndptr[i] == -1) { /* This is an interior vertex */
+      auxadjncy = sadjncy[mypart] + snedges[mypart] - istart;
+      auxadjwgt = sadjwgt[mypart] + snedges[mypart] - istart;
+      for(j=istart; j<iend; j++) {
+        auxadjncy[j] = adjncy[j];
+        auxadjwgt[j] = adjwgt[j]; 
+      }
+      snedges[mypart] += iend-istart;
+    }
+    else {
+      auxadjncy = sadjncy[mypart];
+      auxadjwgt = sadjwgt[mypart];
+      l = snedges[mypart];
+      for (j=istart; j<iend; j++) {
+        k = adjncy[j];
+        if (where[k] == mypart) {
+          auxadjncy[l] = k;
+          auxadjwgt[l++] = adjwgt[j]; 
+        }
+        else {
+          sum -= adjwgt[j];
+        }
+      }
+      snedges[mypart] = l;
+    }
+
+    if (ncon == 1)
+      svwgt[mypart][snvtxs[mypart]] = vwgt[i];
+    else {
+      for (kk=0; kk<ncon; kk++)
+        snvwgt[mypart][snvtxs[mypart]*ncon+kk] = nvwgt[i*ncon+kk]/npwgts[mypart*ncon+kk];
+    }
+
+    sadjwgtsum[mypart][snvtxs[mypart]] = sum;
+    slabel[mypart][snvtxs[mypart]] = label[i];
+    sxadj[mypart][++snvtxs[mypart]] = snedges[mypart];
+  }
+
+  for (mypart=0; mypart<2; mypart++) {
+    iend = sxadj[mypart][snvtxs[mypart]];
+    auxadjncy = sadjncy[mypart];
+    for (i=0; i<iend; i++) 
+      auxadjncy[i] = rename[auxadjncy[i]];
+  }
+
+  lgraph->nedges = snedges[0];
+  rgraph->nedges = snedges[1];
+
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->SplitTmr));
+
+  idxwspacefree(ctrl, nvtxs);
+}
+
+
+/*************************************************************************
+* Setup the various arrays for the splitted graph
+**************************************************************************/
+void SetUpSplitGraph(GraphType *graph, GraphType *sgraph, int snvtxs, int snedges)
+{
+  InitGraph(sgraph);
+  sgraph->nvtxs = snvtxs;
+  sgraph->nedges = snedges;
+  sgraph->ncon = graph->ncon;
+
+  /* Allocate memory for the splitted graph */
+  if (graph->ncon == 1) {
+    sgraph->gdata = idxmalloc(4*snvtxs+1 + 2*snedges, "SetUpSplitGraph: gdata");
+
+    sgraph->xadj        = sgraph->gdata;
+    sgraph->vwgt        = sgraph->gdata + snvtxs+1;
+    sgraph->adjwgtsum   = sgraph->gdata + 2*snvtxs+1;
+    sgraph->cmap        = sgraph->gdata + 3*snvtxs+1;
+    sgraph->adjncy      = sgraph->gdata + 4*snvtxs+1;
+    sgraph->adjwgt      = sgraph->gdata + 4*snvtxs+1 + snedges;
+  }
+  else {
+    sgraph->gdata = idxmalloc(3*snvtxs+1 + 2*snedges, "SetUpSplitGraph: gdata");
+
+    sgraph->xadj        = sgraph->gdata;
+    sgraph->adjwgtsum   = sgraph->gdata + snvtxs+1;
+    sgraph->cmap        = sgraph->gdata + 2*snvtxs+1;
+    sgraph->adjncy      = sgraph->gdata + 3*snvtxs+1;
+    sgraph->adjwgt      = sgraph->gdata + 3*snvtxs+1 + snedges;
+
+    sgraph->nvwgt       = fmalloc(graph->ncon*snvtxs, "SetUpSplitGraph: nvwgt");
+  }
+
+  sgraph->label	= idxmalloc(snvtxs, "SetUpSplitGraph: sgraph->label");
+}
+
diff --git a/contrib/Metis/pqueue.c b/contrib/Metis/pqueue.c
new file mode 100644
index 0000000000..0f2b3d460c
--- /dev/null
+++ b/contrib/Metis/pqueue.c
@@ -0,0 +1,579 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * pqueue.c
+ *
+ * This file contains functions for manipulating the bucket list
+ * representation of the gains associated with each vertex in a graph.
+ * These functions are used by the refinement algorithms
+ *
+ * Started 9/2/94
+ * George
+ *
+ * $Id: pqueue.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function initializes the data structures of the priority queue
+**************************************************************************/
+void PQueueInit(CtrlType *ctrl, PQueueType *queue, int maxnodes, int maxgain)
+{
+  int i, j, ncore;
+
+  queue->nnodes = 0;
+  queue->maxnodes = maxnodes;
+
+  queue->buckets = NULL;
+  queue->nodes = NULL;
+  queue->heap = NULL;
+  queue->locator = NULL;
+
+  if (maxgain > PLUS_GAINSPAN || maxnodes < 500)
+    queue->type = 2;
+  else
+    queue->type = 1;
+
+  if (queue->type == 1) {
+    queue->pgainspan = amin(PLUS_GAINSPAN, maxgain);
+    queue->ngainspan = amin(NEG_GAINSPAN, maxgain);
+
+    j = queue->ngainspan+queue->pgainspan+1;
+
+    ncore = 2 + (sizeof(ListNodeType)/sizeof(idxtype))*maxnodes + (sizeof(ListNodeType *)/sizeof(idxtype))*j;
+
+    if (WspaceAvail(ctrl) > ncore) {
+      queue->nodes = (ListNodeType *)idxwspacemalloc(ctrl, (sizeof(ListNodeType)/sizeof(idxtype))*maxnodes);
+      queue->buckets = (ListNodeType **)idxwspacemalloc(ctrl, (sizeof(ListNodeType *)/sizeof(idxtype))*j);
+      queue->mustfree = 0;
+    }
+    else { /* Not enough memory in the wspace, allocate it */
+      queue->nodes = (ListNodeType *)idxmalloc((sizeof(ListNodeType)/sizeof(idxtype))*maxnodes, "PQueueInit: queue->nodes");
+      queue->buckets = (ListNodeType **)idxmalloc((sizeof(ListNodeType *)/sizeof(idxtype))*j, "PQueueInit: queue->buckets");
+      queue->mustfree = 1;
+    }
+
+    for (i=0; i<maxnodes; i++) 
+      queue->nodes[i].id = i;
+
+    for (i=0; i<j; i++)
+      queue->buckets[i] = NULL;
+
+    queue->buckets += queue->ngainspan;  /* Advance buckets by the ngainspan proper indexing */
+    queue->maxgain = -queue->ngainspan;
+  }
+  else {
+    queue->heap = (KeyValueType *)idxwspacemalloc(ctrl, (sizeof(KeyValueType)/sizeof(idxtype))*maxnodes);
+    queue->locator = idxwspacemalloc(ctrl, maxnodes);
+    idxset(maxnodes, -1, queue->locator);
+  }
+
+}
+
+
+/*************************************************************************
+* This function resets the buckets
+**************************************************************************/
+void PQueueReset(PQueueType *queue)
+{
+  int i, j;
+  queue->nnodes = 0;
+
+  if (queue->type == 1) {
+    queue->maxgain = -queue->ngainspan;
+
+    j = queue->ngainspan+queue->pgainspan+1;
+    queue->buckets -= queue->ngainspan;  
+    for (i=0; i<j; i++)
+      queue->buckets[i] = NULL;
+    queue->buckets += queue->ngainspan;  
+  }
+  else {
+    idxset(queue->maxnodes, -1, queue->locator);
+  }
+
+}
+
+
+/*************************************************************************
+* This function frees the buckets
+**************************************************************************/
+void PQueueFree(CtrlType *ctrl, PQueueType *queue)
+{
+
+  if (queue->type == 1) {
+    if (queue->mustfree) {
+      queue->buckets -= queue->ngainspan;  
+      GKfree(&queue->nodes, &queue->buckets, LTERM);
+    } 
+    else {
+      idxwspacefree(ctrl, sizeof(ListNodeType *)*(queue->ngainspan+queue->pgainspan+1)/sizeof(idxtype));
+      idxwspacefree(ctrl, sizeof(ListNodeType)*queue->maxnodes/sizeof(idxtype));
+    }
+  }
+  else {
+    idxwspacefree(ctrl, sizeof(KeyValueType)*queue->maxnodes/sizeof(idxtype));
+    idxwspacefree(ctrl, queue->maxnodes);
+  }
+
+  queue->maxnodes = 0;
+}
+
+
+/*************************************************************************
+* This function returns the number of nodes in the queue
+**************************************************************************/
+int PQueueGetSize(PQueueType *queue)
+{
+  return queue->nnodes;
+}
+
+
+/*************************************************************************
+* This function adds a node of certain gain into a partition
+**************************************************************************/
+int PQueueInsert(PQueueType *queue, int node, int gain)
+{
+  int i, j, k;
+  idxtype *locator;
+  ListNodeType *newnode;
+  KeyValueType *heap;
+
+  if (queue->type == 1) {
+    ASSERT(gain >= -queue->ngainspan && gain <= queue->pgainspan);
+
+    /* Allocate and add the node */
+    queue->nnodes++;
+    newnode = queue->nodes + node;
+
+    /* Attach this node in the doubly-linked list */
+    newnode->next = queue->buckets[gain];
+    newnode->prev = NULL;
+    if (newnode->next != NULL)
+      newnode->next->prev = newnode;
+    queue->buckets[gain] = newnode;
+
+    if (queue->maxgain < gain)
+      queue->maxgain = gain;
+  }
+  else {
+    ASSERT(CheckHeap(queue));
+
+    heap = queue->heap;
+    locator = queue->locator;
+
+    ASSERT(locator[node] == -1);
+
+    i = queue->nnodes++;
+    while (i > 0) {
+      j = (i-1)/2;
+      if (heap[j].key < gain) {
+        heap[i] = heap[j];
+        locator[heap[i].val] = i;
+        i = j;
+      }
+      else 
+        break;
+    }
+    ASSERT(i >= 0);
+    heap[i].key = gain;
+    heap[i].val = node;
+    locator[node] = i;
+
+    ASSERT(CheckHeap(queue));
+  }
+
+  return 0;
+}
+
+
+/*************************************************************************
+* This function deletes a node from a partition and reinserts it with
+* an updated gain
+**************************************************************************/
+int PQueueDelete(PQueueType *queue, int node, int gain)
+{
+  int i, j, newgain, oldgain;
+  idxtype *locator;
+  ListNodeType *newnode, **buckets;
+  KeyValueType *heap;
+
+  if (queue->type == 1) {
+    ASSERT(gain >= -queue->ngainspan && gain <= queue->pgainspan);
+    ASSERT(queue->nnodes > 0);
+
+    buckets = queue->buckets;
+    queue->nnodes--;
+    newnode = queue->nodes+node;
+
+    /* Remove newnode from the doubly-linked list */
+    if (newnode->prev != NULL)
+      newnode->prev->next = newnode->next;
+    else
+      buckets[gain] = newnode->next;
+    if (newnode->next != NULL)
+      newnode->next->prev = newnode->prev;
+
+    if (buckets[gain] == NULL && gain == queue->maxgain) {
+      if (queue->nnodes == 0) 
+        queue->maxgain = -queue->ngainspan;
+      else 
+        for (; buckets[queue->maxgain]==NULL; queue->maxgain--);
+    }
+  }
+  else { /* Heap Priority Queue */
+    heap = queue->heap;
+    locator = queue->locator;
+
+    ASSERT(locator[node] != -1);
+    ASSERT(heap[locator[node]].val == node);
+
+    ASSERT(CheckHeap(queue));
+
+    i = locator[node];
+    locator[node] = -1;
+
+    if (--queue->nnodes > 0 && heap[queue->nnodes].val != node) {
+      node = heap[queue->nnodes].val;
+      newgain = heap[queue->nnodes].key;
+      oldgain = heap[i].key;
+
+      if (oldgain < newgain) { /* Filter-up */
+        while (i > 0) {
+          j = (i-1)>>1;
+          if (heap[j].key < newgain) {
+            heap[i] = heap[j];
+            locator[heap[i].val] = i;
+            i = j;
+          }
+          else 
+            break;
+        }
+      }
+      else { /* Filter down */
+        while ((j=2*i+1) < queue->nnodes) {
+          if (heap[j].key > newgain) {
+            if (j+1 < queue->nnodes && heap[j+1].key > heap[j].key)
+              j = j+1;
+            heap[i] = heap[j];
+            locator[heap[i].val] = i;
+            i = j;
+          }
+          else if (j+1 < queue->nnodes && heap[j+1].key > newgain) {
+            j = j+1;
+            heap[i] = heap[j];
+            locator[heap[i].val] = i;
+            i = j;
+          }
+          else
+            break;
+        }
+      }
+
+      heap[i].key = newgain;
+      heap[i].val = node;
+      locator[node] = i;
+    }
+
+    ASSERT(CheckHeap(queue));
+  }
+
+  return 0;
+}
+
+
+
+/*************************************************************************
+* This function deletes a node from a partition and reinserts it with
+* an updated gain
+**************************************************************************/
+int PQueueUpdate(PQueueType *queue, int node, int oldgain, int newgain)
+{
+  int i, j;
+  idxtype *locator;
+  ListNodeType *newnode;
+  KeyValueType *heap;
+
+  if (oldgain == newgain) 
+    return 0;
+
+  if (queue->type == 1) {
+    /* First delete the node and then insert it */
+    PQueueDelete(queue, node, oldgain);
+    return PQueueInsert(queue, node, newgain);
+  }
+  else { /* Heap Priority Queue */
+    heap = queue->heap;
+    locator = queue->locator;
+
+    ASSERT(locator[node] != -1);
+    ASSERT(heap[locator[node]].val == node);
+    ASSERT(heap[locator[node]].key == oldgain);
+    ASSERT(CheckHeap(queue));
+
+    i = locator[node];
+
+    if (oldgain < newgain) { /* Filter-up */
+      while (i > 0) {
+        j = (i-1)>>1;
+        if (heap[j].key < newgain) {
+          heap[i] = heap[j];
+          locator[heap[i].val] = i;
+          i = j;
+        }
+        else 
+          break;
+      }
+    }
+    else { /* Filter down */
+      while ((j=2*i+1) < queue->nnodes) {
+        if (heap[j].key > newgain) {
+          if (j+1 < queue->nnodes && heap[j+1].key > heap[j].key)
+            j = j+1;
+          heap[i] = heap[j];
+          locator[heap[i].val] = i;
+          i = j;
+        }
+        else if (j+1 < queue->nnodes && heap[j+1].key > newgain) {
+          j = j+1;
+          heap[i] = heap[j];
+          locator[heap[i].val] = i;
+          i = j;
+        }
+        else
+          break;
+      }
+    }
+
+    heap[i].key = newgain;
+    heap[i].val = node;
+    locator[node] = i;
+
+    ASSERT(CheckHeap(queue));
+  }
+
+  return 0;
+}
+
+
+
+/*************************************************************************
+* This function deletes a node from a partition and reinserts it with
+* an updated gain
+**************************************************************************/
+void PQueueUpdateUp(PQueueType *queue, int node, int oldgain, int newgain)
+{
+  int i, j;
+  idxtype *locator;
+  ListNodeType *newnode, **buckets;
+  KeyValueType *heap;
+
+  if (oldgain == newgain) 
+    return;
+
+  if (queue->type == 1) {
+    ASSERT(oldgain >= -queue->ngainspan && oldgain <= queue->pgainspan);
+    ASSERT(newgain >= -queue->ngainspan && newgain <= queue->pgainspan);
+    ASSERT(queue->nnodes > 0);
+
+    buckets = queue->buckets;
+    newnode = queue->nodes+node;
+
+    /* First delete the node */
+    if (newnode->prev != NULL)
+      newnode->prev->next = newnode->next;
+    else
+      buckets[oldgain] = newnode->next;
+    if (newnode->next != NULL)
+      newnode->next->prev = newnode->prev;
+
+    /* Attach this node in the doubly-linked list */
+    newnode->next = buckets[newgain];
+    newnode->prev = NULL;
+    if (newnode->next != NULL)
+      newnode->next->prev = newnode;
+    buckets[newgain] = newnode;
+
+    if (queue->maxgain < newgain)
+      queue->maxgain = newgain;
+  }
+  else { /* Heap Priority Queue */
+    heap = queue->heap;
+    locator = queue->locator;
+
+    ASSERT(locator[node] != -1);
+    ASSERT(heap[locator[node]].val == node);
+    ASSERT(heap[locator[node]].key == oldgain);
+    ASSERT(CheckHeap(queue));
+
+
+    /* Here we are just filtering up since the newgain is greater than the oldgain */
+    i = locator[node];
+    while (i > 0) {
+      j = (i-1)>>1;
+      if (heap[j].key < newgain) {
+        heap[i] = heap[j];
+        locator[heap[i].val] = i;
+        i = j;
+      }
+      else 
+        break;
+    }
+
+    heap[i].key = newgain;
+    heap[i].val = node;
+    locator[node] = i;
+
+    ASSERT(CheckHeap(queue));
+  }
+
+}
+
+
+/*************************************************************************
+* This function returns the vertex with the largest gain from a partition
+* and removes the node from the bucket list
+**************************************************************************/
+int PQueueGetMax(PQueueType *queue)
+{
+  int vtx, i, j, gain, node;
+  idxtype *locator;
+  ListNodeType *tptr;
+  KeyValueType *heap;
+
+  if (queue->nnodes == 0)
+    return -1;
+
+  queue->nnodes--;
+
+  if (queue->type == 1) {
+    tptr = queue->buckets[queue->maxgain];
+    queue->buckets[queue->maxgain] = tptr->next;
+    if (tptr->next != NULL) {
+      tptr->next->prev = NULL;
+    }
+    else {
+      if (queue->nnodes == 0) {
+        queue->maxgain = -queue->ngainspan;
+      }
+      else 
+        for (; queue->buckets[queue->maxgain]==NULL; queue->maxgain--);
+    }
+
+    return tptr->id;
+  }
+  else {
+    heap = queue->heap;
+    locator = queue->locator;
+
+    vtx = heap[0].val;
+    locator[vtx] = -1;
+
+    if ((i = queue->nnodes) > 0) {
+      gain = heap[i].key;
+      node = heap[i].val;
+      i = 0;
+      while ((j=2*i+1) < queue->nnodes) {
+        if (heap[j].key > gain) {
+          if (j+1 < queue->nnodes && heap[j+1].key > heap[j].key)
+            j = j+1;
+          heap[i] = heap[j];
+          locator[heap[i].val] = i;
+          i = j;
+        }
+        else if (j+1 < queue->nnodes && heap[j+1].key > gain) {
+          j = j+1;
+          heap[i] = heap[j];
+          locator[heap[i].val] = i;
+          i = j;
+        }
+        else
+          break;
+      }
+
+      heap[i].key = gain;
+      heap[i].val = node;
+      locator[node] = i;
+    }
+
+    ASSERT(CheckHeap(queue));
+    return vtx;
+  }
+}
+      
+
+/*************************************************************************
+* This function returns the vertex with the largest gain from a partition
+**************************************************************************/
+int PQueueSeeMax(PQueueType *queue)
+{
+  int vtx;
+
+  if (queue->nnodes == 0)
+    return -1;
+
+  if (queue->type == 1) 
+    vtx = queue->buckets[queue->maxgain]->id;
+  else
+    vtx = queue->heap[0].val;
+
+  return vtx;
+}
+      
+
+/*************************************************************************
+* This function returns the vertex with the largest gain from a partition
+**************************************************************************/
+int PQueueGetKey(PQueueType *queue)
+{
+  int key;
+
+  if (queue->nnodes == 0)
+    return -1;
+
+  if (queue->type == 1) 
+    key = queue->maxgain;
+  else
+    key = queue->heap[0].key;
+
+  return key;
+}
+      
+
+
+
+/*************************************************************************
+* This functions checks the consistency of the heap
+**************************************************************************/
+int CheckHeap(PQueueType *queue)
+{
+  int i, j, nnodes;
+  idxtype *locator;
+  KeyValueType *heap;
+
+  heap = queue->heap;
+  locator = queue->locator;
+  nnodes = queue->nnodes;
+
+  if (nnodes == 0)
+    return 1;
+
+  ASSERT(locator[heap[0].val] == 0);
+  for (i=1; i<nnodes; i++) {
+    ASSERTP(locator[heap[i].val] == i, ("%d %d %d %d\n", nnodes, i, heap[i].val, locator[heap[i].val])); 
+    ASSERTP(heap[i].key <= heap[(i-1)/2].key, ("%d %d %d %d %d\n", i, (i-1)/2, nnodes, heap[i].key, heap[(i-1)/2].key));
+  }
+  for (i=1; i<nnodes; i++)
+    ASSERT(heap[i].key <= heap[0].key);
+
+  for (j=i=0; i<queue->maxnodes; i++) {
+    if (locator[i] != -1)
+      j++;
+  }
+  ASSERTP(j == nnodes, ("%d %d\n", j, nnodes));
+
+  return 1;
+}
diff --git a/contrib/Metis/proto.h b/contrib/Metis/proto.h
new file mode 100644
index 0000000000..b459916582
--- /dev/null
+++ b/contrib/Metis/proto.h
@@ -0,0 +1,505 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * proto.h
+ *
+ * This file contains header files
+ *
+ * Started 10/19/95
+ * George
+ *
+ * $Id: proto.h,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ *
+ */
+
+/* balance.c */
+void Balance2Way(CtrlType *, GraphType *, int *, float);
+void Bnd2WayBalance(CtrlType *, GraphType *, int *);
+void General2WayBalance(CtrlType *, GraphType *, int *);
+
+/* bucketsort.c */
+void BucketSortKeysInc(int, int, idxtype *, idxtype *, idxtype *);
+
+/* ccgraph.c */
+void CreateCoarseGraph(CtrlType *, GraphType *, int, idxtype *, idxtype *);
+void CreateCoarseGraphNoMask(CtrlType *, GraphType *, int, idxtype *, idxtype *);
+void CreateCoarseGraph_NVW(CtrlType *, GraphType *, int, idxtype *, idxtype *);
+GraphType *SetUpCoarseGraph(GraphType *, int, int);
+void ReAdjustMemory(GraphType *, GraphType *, int);
+
+/* coarsen.c */
+GraphType *Coarsen2Way(CtrlType *, GraphType *);
+
+/* compress.c */
+void CompressGraph(CtrlType *, GraphType *, int, idxtype *, idxtype *, idxtype *, idxtype *);
+void PruneGraph(CtrlType *, GraphType *, int, idxtype *, idxtype *, idxtype *, float);
+
+/* debug.c */
+int ComputeCut(GraphType *, idxtype *);
+int CheckBnd(GraphType *);
+int CheckBnd2(GraphType *);
+int CheckNodeBnd(GraphType *, int);
+int CheckRInfo(RInfoType *);
+int CheckNodePartitionParams(GraphType *);
+int IsSeparable(GraphType *);
+
+/* estmem.c */
+void METIS_EstimateMemory(int *, idxtype *, idxtype *, int *, int *, int *);
+void EstimateCFraction(int, idxtype *, idxtype *, float *, float *);
+int ComputeCoarseGraphSize(int, idxtype *, idxtype *, int, idxtype *, idxtype *, idxtype *);
+
+/* fm.c */
+void FM_2WayEdgeRefine(CtrlType *, GraphType *, int *, int);
+
+/* fortran.c */
+void Change2CNumbering(int, idxtype *, idxtype *);
+void Change2FNumbering(int, idxtype *, idxtype *, idxtype *);
+void Change2FNumbering2(int, idxtype *, idxtype *);
+void Change2FNumberingOrder(int, idxtype *, idxtype *, idxtype *, idxtype *);
+void ChangeMesh2CNumbering(int, idxtype *);
+void ChangeMesh2FNumbering(int, idxtype *, int, idxtype *, idxtype *);
+void ChangeMesh2FNumbering2(int, idxtype *, int, int, idxtype *, idxtype *);
+
+/* frename.c */
+void METIS_PARTGRAPHRECURSIVE(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, int *, int *, idxtype *); 
+void metis_partgraphrecursive(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, int *, int *, idxtype *); 
+void metis_partgraphrecursive_(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, int *, int *, idxtype *); 
+void metis_partgraphrecursive__(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, int *, int *, idxtype *); 
+void METIS_WPARTGRAPHRECURSIVE(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, float *, int *, int *, idxtype *); 
+void metis_wpartgraphrecursive(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, float *, int *, int *, idxtype *); 
+void metis_wpartgraphrecursive_(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, float *, int *, int *, idxtype *); 
+void metis_wpartgraphrecursive__(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, float *, int *, int *, idxtype *); 
+void METIS_PARTGRAPHKWAY(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, int *, int *, idxtype *); 
+void metis_partgraphkway(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, int *, int *, idxtype *); 
+void metis_partgraphkway_(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, int *, int *, idxtype *); 
+void metis_partgraphkway__(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, int *, int *, idxtype *); 
+void METIS_WPARTGRAPHKWAY(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, float *, int *, int *, idxtype *); 
+void metis_wpartgraphkway(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, float *, int *, int *, idxtype *); 
+void metis_wpartgraphkway_(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, float *, int *, int *, idxtype *); 
+void metis_wpartgraphkway__(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, float *, int *, int *, idxtype *); 
+void METIS_EDGEND(int *, idxtype *, idxtype *, int *, int *, idxtype *, idxtype *); 
+void metis_edgend(int *, idxtype *, idxtype *, int *, int *, idxtype *, idxtype *); 
+void metis_edgend_(int *, idxtype *, idxtype *, int *, int *, idxtype *, idxtype *); 
+void metis_edgend__(int *, idxtype *, idxtype *, int *, int *, idxtype *, idxtype *); 
+void METIS_NODEND(int *, idxtype *, idxtype *, int *, int *, idxtype *, idxtype *); 
+void metis_nodend(int *, idxtype *, idxtype *, int *, int *, idxtype *, idxtype *); 
+void metis_nodend_(int *, idxtype *, idxtype *, int *, int *, idxtype *, idxtype *); 
+void metis_nodend__(int *, idxtype *, idxtype *, int *, int *, idxtype *, idxtype *); 
+void METIS_NODEWND(int *, idxtype *, idxtype *, idxtype *, int *, int *, idxtype *, idxtype *); 
+void metis_nodewnd(int *, idxtype *, idxtype *, idxtype *, int *, int *, idxtype *, idxtype *); 
+void metis_nodewnd_(int *, idxtype *, idxtype *, idxtype *, int *, int *, idxtype *, idxtype *); 
+void metis_nodewnd__(int *, idxtype *, idxtype *, idxtype *, int *, int *, idxtype *, idxtype *); 
+void METIS_PARTMESHNODAL(int *, int *, idxtype *, int *, int *, int *, int *, idxtype *, idxtype *);
+void metis_partmeshnodal(int *, int *, idxtype *, int *, int *, int *, int *, idxtype *, idxtype *);
+void metis_partmeshnodal_(int *, int *, idxtype *, int *, int *, int *, int *, idxtype *, idxtype *);
+void metis_partmeshnodal__(int *, int *, idxtype *, int *, int *, int *, int *, idxtype *, idxtype *);
+void METIS_PARTMESHDUAL(int *, int *, idxtype *, int *, int *, int *, int *, idxtype *, idxtype *);
+void metis_partmeshdual(int *, int *, idxtype *, int *, int *, int *, int *, idxtype *, idxtype *);
+void metis_partmeshdual_(int *, int *, idxtype *, int *, int *, int *, int *, idxtype *, idxtype *);
+void metis_partmeshdual__(int *, int *, idxtype *, int *, int *, int *, int *, idxtype *, idxtype *);
+void METIS_MESHTONODAL(int *, int *, idxtype *, int *, int *, idxtype *, idxtype *);
+void metis_meshtonodal(int *, int *, idxtype *, int *, int *, idxtype *, idxtype *);
+void metis_meshtonodal_(int *, int *, idxtype *, int *, int *, idxtype *, idxtype *);
+void metis_meshtonodal__(int *, int *, idxtype *, int *, int *, idxtype *, idxtype *);
+void METIS_MESHTODUAL(int *, int *, idxtype *, int *, int *, idxtype *, idxtype *);
+void metis_meshtodual(int *, int *, idxtype *, int *, int *, idxtype *, idxtype *);
+void metis_meshtodual_(int *, int *, idxtype *, int *, int *, idxtype *, idxtype *);
+void metis_meshtodual__(int *, int *, idxtype *, int *, int *, idxtype *, idxtype *);
+void METIS_ESTIMATEMEMORY(int *, idxtype *, idxtype *, int *, int *, int *);
+void metis_estimatememory(int *, idxtype *, idxtype *, int *, int *, int *);
+void metis_estimatememory_(int *, idxtype *, idxtype *, int *, int *, int *);
+void metis_estimatememory__(int *, idxtype *, idxtype *, int *, int *, int *);
+void METIS_MCPARTGRAPHRECURSIVE(int *, int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, int *, int *, idxtype *);
+void metis_mcpartgraphrecursive(int *, int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, int *, int *, idxtype *);
+void metis_mcpartgraphrecursive_(int *, int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, int *, int *, idxtype *);
+void metis_mcpartgraphrecursive__(int *, int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, int *, int *, idxtype *);
+void METIS_MCPARTGRAPHKWAY(int *, int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, float *, int *, int *, idxtype *);
+void metis_mcpartgraphkway(int *, int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, float *, int *, int *, idxtype *);
+void metis_mcpartgraphkway_(int *, int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, float *, int *, int *, idxtype *);
+void metis_mcpartgraphkway__(int *, int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, float *, int *, int *, idxtype *);
+void METIS_PARTGRAPHVKWAY(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, int *, int *, idxtype *);
+void metis_partgraphvkway(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, int *, int *, idxtype *);
+void metis_partgraphvkway_(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, int *, int *, idxtype *);
+void metis_partgraphvkway__(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, int *, int *, idxtype *);
+void METIS_WPARTGRAPHVKWAY(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, float *, int *, int *, idxtype *);
+void metis_wpartgraphvkway(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, float *, int *, int *, idxtype *);
+void metis_wpartgraphvkway_(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, float *, int *, int *, idxtype *);
+void metis_wpartgraphvkway__(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, float *, int *, int *, idxtype *);
+
+/* graph.c */
+void SetUpGraph(GraphType *, int, int, int, idxtype *, idxtype *, idxtype *, idxtype *, int);
+void SetUpGraphKway(GraphType *, int, idxtype *, idxtype *);
+void SetUpGraph2(GraphType *, int, int, idxtype *, idxtype *, float *, idxtype *);
+void VolSetUpGraph(GraphType *, int, int, int, idxtype *, idxtype *, idxtype *, idxtype *, int);
+void RandomizeGraph(GraphType *);
+int IsConnectedSubdomain(CtrlType *, GraphType *, int, int);
+int IsConnected(CtrlType *, GraphType *, int);
+int IsConnected2(GraphType *, int);
+int FindComponents(CtrlType *, GraphType *, idxtype *, idxtype *);
+
+/* initpart.c */
+void Init2WayPartition(CtrlType *, GraphType *, int *, float);
+void InitSeparator(CtrlType *, GraphType *, float);
+void GrowBisection(CtrlType *, GraphType *, int *, float);
+void GrowBisectionNode(CtrlType *, GraphType *, float);
+void RandomBisection(CtrlType *, GraphType *, int *, float);
+
+/* kmetis.c */
+void METIS_PartGraphKway(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, int *, int *, idxtype *); 
+void METIS_WPartGraphKway(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, float *, int *, int *, idxtype *); 
+int MlevelKWayPartitioning(CtrlType *, GraphType *, int, idxtype *, float *, float);
+
+/* kvmetis.c */
+void METIS_PartGraphVKway(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, int *, int *, idxtype *);
+void METIS_WPartGraphVKway(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, float *, int *, int *, idxtype *);
+int MlevelVolKWayPartitioning(CtrlType *, GraphType *, int, idxtype *, float *, float);
+
+/* kwayfm.c */
+void Random_KWayEdgeRefine(CtrlType *, GraphType *, int, float *, float, int, int);
+void Greedy_KWayEdgeRefine(CtrlType *, GraphType *, int, float *, float, int);
+void Greedy_KWayEdgeBalance(CtrlType *, GraphType *, int, float *, float, int);
+
+/* kwayrefine.c */
+void RefineKWay(CtrlType *, GraphType *, GraphType *, int, float *, float);
+void AllocateKWayPartitionMemory(CtrlType *, GraphType *, int);
+void ComputeKWayPartitionParams(CtrlType *, GraphType *, int);
+void ProjectKWayPartition(CtrlType *, GraphType *, int);
+int IsBalanced(idxtype *, int, float *, float);
+void ComputeKWayBoundary(CtrlType *, GraphType *, int);
+void ComputeKWayBalanceBoundary(CtrlType *, GraphType *, int);
+
+/* kwayvolfm.c */
+void Random_KWayVolRefine(CtrlType *, GraphType *, int, float *, float, int, int);
+void Random_KWayVolRefineMConn(CtrlType *, GraphType *, int, float *, float, int, int);
+void Greedy_KWayVolBalance(CtrlType *, GraphType *, int, float *, float, int);
+void Greedy_KWayVolBalanceMConn(CtrlType *, GraphType *, int, float *, float, int);
+void KWayVolUpdate(CtrlType *, GraphType *, int, int, int, idxtype *, idxtype *, idxtype *);
+void ComputeKWayVolume(GraphType *, int, idxtype *, idxtype *, idxtype *);
+int ComputeVolume(GraphType *, idxtype *);
+void CheckVolKWayPartitionParams(CtrlType *, GraphType *, int);
+void ComputeVolSubDomainGraph(GraphType *, int, idxtype *, idxtype *);
+void EliminateVolSubDomainEdges(CtrlType *, GraphType *, int, float *);
+void EliminateVolComponents(CtrlType *, GraphType *, int, float *, float);
+
+/* kwayvolrefine.c */
+void RefineVolKWay(CtrlType *, GraphType *, GraphType *, int, float *, float);
+void AllocateVolKWayPartitionMemory(CtrlType *, GraphType *, int);
+void ComputeVolKWayPartitionParams(CtrlType *, GraphType *, int);
+void ComputeKWayVolGains(CtrlType *, GraphType *, int);
+void ProjectVolKWayPartition(CtrlType *, GraphType *, int);
+void ComputeVolKWayBoundary(CtrlType *, GraphType *, int);
+void ComputeVolKWayBalanceBoundary(CtrlType *, GraphType *, int);
+
+/* match.c */
+void Match_RM(CtrlType *, GraphType *);
+void Match_RM_NVW(CtrlType *, GraphType *);
+void Match_HEM(CtrlType *, GraphType *);
+void Match_SHEM(CtrlType *, GraphType *);
+
+/* mbalance.c */
+void MocBalance2Way(CtrlType *, GraphType *, float *, float);
+void MocGeneral2WayBalance(CtrlType *, GraphType *, float *, float);
+
+/* mbalance2.c */
+void MocBalance2Way2(CtrlType *, GraphType *, float *, float *);
+void MocGeneral2WayBalance2(CtrlType *, GraphType *, float *, float *);
+void SelectQueue3(int, float *, float *, int *, int *, PQueueType [MAXNCON][2], float *);
+
+/* mcoarsen.c */
+GraphType *MCCoarsen2Way(CtrlType *, GraphType *);
+
+/* memory.c */
+void AllocateWorkSpace(CtrlType *, GraphType *, int);
+void FreeWorkSpace(CtrlType *, GraphType *);
+int WspaceAvail(CtrlType *);
+idxtype *idxwspacemalloc(CtrlType *, int);
+void idxwspacefree(CtrlType *, int);
+float *fwspacemalloc(CtrlType *, int);
+void fwspacefree(CtrlType *, int);
+GraphType *CreateGraph(void);
+void InitGraph(GraphType *);
+void FreeGraph(GraphType *);
+
+/* mesh.c */
+void METIS_MeshToDual(int *, int *, idxtype *, int *, int *, idxtype *, idxtype *);
+void METIS_MeshToNodal(int *, int *, idxtype *, int *, int *, idxtype *, idxtype *);
+void GENDUALMETIS(int, int, int, idxtype *, idxtype *, idxtype *adjncy);
+void TRINODALMETIS(int, int, idxtype *, idxtype *, idxtype *adjncy);
+void TETNODALMETIS(int, int, idxtype *, idxtype *, idxtype *adjncy);
+void HEXNODALMETIS(int, int, idxtype *, idxtype *, idxtype *adjncy);
+void QUADNODALMETIS(int, int, idxtype *, idxtype *, idxtype *adjncy);
+
+/* meshpart.c */
+void METIS_PartMeshNodal(int *, int *, idxtype *, int *, int *, int *, int *, idxtype *, idxtype *);
+void METIS_PartMeshDual(int *, int *, idxtype *, int *, int *, int *, int *, idxtype *, idxtype *);
+
+/* mfm.c */
+void MocFM_2WayEdgeRefine(CtrlType *, GraphType *, float *, int);
+void SelectQueue(int, float *, float *, int *, int *, PQueueType [MAXNCON][2]);
+int BetterBalance(int, float *, float *, float *);
+float Compute2WayHLoadImbalance(int, float *, float *);
+void Compute2WayHLoadImbalanceVec(int, float *, float *, float *);
+
+/* mfm2.c */
+void MocFM_2WayEdgeRefine2(CtrlType *, GraphType *, float *, float *, int);
+void SelectQueue2(int, float *, float *, int *, int *, PQueueType [MAXNCON][2], float *);
+int IsBetter2wayBalance(int, float *, float *, float *);
+
+/* mincover.o */
+void MinCover(idxtype *, idxtype *, int, int, idxtype *, int *);
+int MinCover_Augment(idxtype *, idxtype *, int, idxtype *, idxtype *, idxtype *, int);
+void MinCover_Decompose(idxtype *, idxtype *, int, int, idxtype *, idxtype *, int *);
+void MinCover_ColDFS(idxtype *, idxtype *, int, idxtype *, idxtype *, int);
+void MinCover_RowDFS(idxtype *, idxtype *, int, idxtype *, idxtype *, int);
+
+/* minitpart.c */
+void MocInit2WayPartition(CtrlType *, GraphType *, float *, float);
+void MocGrowBisection(CtrlType *, GraphType *, float *, float);
+void MocRandomBisection(CtrlType *, GraphType *, float *, float);
+void MocInit2WayBalance(CtrlType *, GraphType *, float *);
+int SelectQueueoneWay(int, float *, float *, int, PQueueType [MAXNCON][2]);
+
+/* minitpart2.c */
+void MocInit2WayPartition2(CtrlType *, GraphType *, float *, float *);
+void MocGrowBisection2(CtrlType *, GraphType *, float *, float *);
+void MocGrowBisectionNew2(CtrlType *, GraphType *, float *, float *);
+void MocInit2WayBalance2(CtrlType *, GraphType *, float *, float *);
+int SelectQueueOneWay2(int, float *, PQueueType [MAXNCON][2], float *);
+
+/* mkmetis.c */
+void METIS_mCPartGraphKway(int *, int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, float *, int *, int *, idxtype *);
+int MCMlevelKWayPartitioning(CtrlType *, GraphType *, int, idxtype *, float *);
+
+/* mkwayfmh.c */
+void MCRandom_KWayEdgeRefineHorizontal(CtrlType *, GraphType *, int, float *, int);
+void MCGreedy_KWayEdgeBalanceHorizontal(CtrlType *, GraphType *, int, float *, int);
+int AreAllHVwgtsBelow(int, float, float *, float, float *, float *);
+int AreAllHVwgtsAbove(int, float, float *, float, float *, float *);
+void ComputeHKWayLoadImbalance(int, int, float *, float *);
+int MocIsHBalanced(int, int, float *, float *);
+int IsHBalanceBetterFT(int, int, float *, float *, float *, float *);
+int IsHBalanceBetterTT(int, int, float *, float *, float *, float *);
+
+/* mkwayrefine.c */
+void MocRefineKWayHorizontal(CtrlType *, GraphType *, GraphType *, int, float *);
+void MocAllocateKWayPartitionMemory(CtrlType *, GraphType *, int);
+void MocComputeKWayPartitionParams(CtrlType *, GraphType *, int);
+void MocProjectKWayPartition(CtrlType *, GraphType *, int);
+void MocComputeKWayBalanceBoundary(CtrlType *, GraphType *, int);
+
+/* mmatch.c */
+void MCMatch_RM(CtrlType *, GraphType *);
+void MCMatch_HEM(CtrlType *, GraphType *);
+void MCMatch_SHEM(CtrlType *, GraphType *);
+void MCMatch_SHEBM(CtrlType *, GraphType *, int);
+void MCMatch_SBHEM(CtrlType *, GraphType *, int);
+float BetterVBalance(int, int, float *, float *, float *);
+int AreAllVwgtsBelowFast(int, float *, float *, float);
+
+/* mmd.c */
+void genmmd(int, idxtype *, idxtype *, idxtype *, idxtype *, int , idxtype *, idxtype *, idxtype *, idxtype *, int, int *);
+void mmdelm(int, idxtype *xadj, idxtype *, idxtype *, idxtype *, idxtype *, idxtype *, idxtype *, idxtype *, int, int);
+int  mmdint(int, idxtype *xadj, idxtype *, idxtype *, idxtype *, idxtype *, idxtype *, idxtype *, idxtype *);
+void mmdnum(int, idxtype *, idxtype *, idxtype *);
+void mmdupd(int, int, idxtype *, idxtype *, int, int *, idxtype *, idxtype *, idxtype *, idxtype *, idxtype *, idxtype *, int, int *tag);
+
+/* mpmetis.c */
+void METIS_mCPartGraphRecursive(int *, int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, int *, int *, idxtype *);
+void METIS_mCHPartGraphRecursive(int *, int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, float *, int *, int *, idxtype *);
+void METIS_mCPartGraphRecursiveInternal(int *, int *, idxtype *, idxtype *, float *, idxtype *, int *, int *, int *, idxtype *);
+void METIS_mCHPartGraphRecursiveInternal(int *, int *, idxtype *, idxtype *, float *, idxtype *, int *, float *, int *, int *, idxtype *);
+int MCMlevelRecursiveBisection(CtrlType *, GraphType *, int, idxtype *, float, int);
+int MCHMlevelRecursiveBisection(CtrlType *, GraphType *, int, idxtype *, float *, int);
+void MCMlevelEdgeBisection(CtrlType *, GraphType *, float *, float);
+void MCHMlevelEdgeBisection(CtrlType *, GraphType *, float *, float *);
+
+/* mrefine.c */
+void MocRefine2Way(CtrlType *, GraphType *, GraphType *, float *, float);
+void MocAllocate2WayPartitionMemory(CtrlType *, GraphType *);
+void MocCompute2WayPartitionParams(CtrlType *, GraphType *);
+void MocProject2WayPartition(CtrlType *, GraphType *);
+
+/* mrefine2.c */
+void MocRefine2Way2(CtrlType *, GraphType *, GraphType *, float *, float *);
+
+/* mutil.c */
+int AreAllVwgtsBelow(int, float, float *, float, float *, float);
+int AreAnyVwgtsBelow(int, float, float *, float, float *, float);
+int AreAllVwgtsAbove(int, float, float *, float, float *, float);
+float ComputeLoadImbalance(int, int, float *, float *);
+int AreAllBelow(int, float *, float *);
+
+/* myqsort.c */
+void iidxsort(int, idxtype *);
+void iintsort(int, int *);
+void ikeysort(int, KeyValueType *);
+void ikeyvalsort(int, KeyValueType *);
+
+/* ometis.c */
+void METIS_EdgeND(int *, idxtype *, idxtype *, int *, int *, idxtype *, idxtype *); 
+void METIS_NodeND(int *, idxtype *, idxtype *, int *, int *, idxtype *, idxtype *); 
+void METIS_NodeWND(int *, idxtype *, idxtype *, idxtype *, int *, int *, idxtype *, idxtype *); 
+void MlevelNestedDissection(CtrlType *, GraphType *, idxtype *, float, int);
+void MlevelNestedDissectionCC(CtrlType *, GraphType *, idxtype *, float, int);
+void MlevelNodeBisectionMultiple(CtrlType *, GraphType *, int *, float);
+void MlevelNodeBisection(CtrlType *, GraphType *, int *, float);
+void SplitGraphOrder(CtrlType *, GraphType *, GraphType *, GraphType *);
+void MMDOrder(CtrlType *, GraphType *, idxtype *, int);
+int SplitGraphOrderCC(CtrlType *, GraphType *, GraphType *, int, idxtype *, idxtype *);
+
+/* parmetis.c */
+void METIS_PartGraphKway2(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, int *, int *, idxtype *); 
+void METIS_WPartGraphKway2(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, float *, int *, int *, idxtype *); 
+void METIS_NodeNDP(int, idxtype *, idxtype *, int, int *, idxtype *, idxtype *, idxtype *);
+void MlevelNestedDissectionP(CtrlType *, GraphType *, idxtype *, int, int, int, idxtype *);
+void METIS_NodeComputeSeparator(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, idxtype *); 
+void METIS_EdgeComputeSeparator(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, idxtype *); 
+
+/* pmetis.c */
+void METIS_PartGraphRecursive(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, int *, int *, idxtype *); 
+void METIS_WPartGraphRecursive(int *, idxtype *, idxtype *, idxtype *, idxtype *, int *, int *, int *, float *, int *, int *, idxtype *); 
+int MlevelRecursiveBisection(CtrlType *, GraphType *, int, idxtype *, float *, float, int);
+void MlevelEdgeBisection(CtrlType *, GraphType *, int *, float);
+void SplitGraphPart(CtrlType *, GraphType *, GraphType *, GraphType *);
+void SetUpSplitGraph(GraphType *, GraphType *, int, int);
+
+/* pqueue.c */
+void PQueueInit(CtrlType *ctrl, PQueueType *, int, int);
+void PQueueReset(PQueueType *);
+void PQueueFree(CtrlType *ctrl, PQueueType *);
+int PQueueGetSize(PQueueType *);
+int PQueueInsert(PQueueType *, int, int);
+int PQueueDelete(PQueueType *, int, int);
+int PQueueUpdate(PQueueType *, int, int, int);
+void PQueueUpdateUp(PQueueType *, int, int, int);
+int PQueueGetMax(PQueueType *);
+int PQueueSeeMax(PQueueType *);
+int PQueueGetKey(PQueueType *);
+int CheckHeap(PQueueType *);
+
+/* refine.c */
+void Refine2Way(CtrlType *, GraphType *, GraphType *, int *, float ubfactor);
+void Allocate2WayPartitionMemory(CtrlType *, GraphType *);
+void Compute2WayPartitionParams(CtrlType *, GraphType *);
+void Project2WayPartition(CtrlType *, GraphType *);
+
+/* separator.c */
+void ConstructSeparator(CtrlType *, GraphType *, float);
+void ConstructMinCoverSeparator0(CtrlType *, GraphType *, float);
+void ConstructMinCoverSeparator(CtrlType *, GraphType *, float);
+
+/* sfm.c */
+void FM_2WayNodeRefine(CtrlType *, GraphType *, float, int);
+void FM_2WayNodeRefineEqWgt(CtrlType *, GraphType *, int);
+void FM_2WayNodeRefine_OneSided(CtrlType *, GraphType *, float, int);
+void FM_2WayNodeBalance(CtrlType *, GraphType *, float);
+int ComputeMaxNodeGain(int, idxtype *, idxtype *, idxtype *);
+
+/* srefine.c */
+void Refine2WayNode(CtrlType *, GraphType *, GraphType *, float);
+void Allocate2WayNodePartitionMemory(CtrlType *, GraphType *);
+void Compute2WayNodePartitionParams(CtrlType *, GraphType *);
+void Project2WayNodePartition(CtrlType *, GraphType *);
+
+/* stat.c */
+void ComputePartitionInfo(GraphType *, int, idxtype *);
+void ComputePartitionInfoBipartite(GraphType *, int, idxtype *);
+void ComputePartitionBalance(GraphType *, int, idxtype *, float *);
+float ComputeElementBalance(int, int, idxtype *);
+
+/* subdomains.c */
+void Random_KWayEdgeRefineMConn(CtrlType *, GraphType *, int, float *, float, int, int);
+void Greedy_KWayEdgeBalanceMConn(CtrlType *, GraphType *, int, float *, float, int);
+void PrintSubDomainGraph(GraphType *, int, idxtype *);
+void ComputeSubDomainGraph(GraphType *, int, idxtype *, idxtype *);
+void EliminateSubDomainEdges(CtrlType *, GraphType *, int, float *);
+void MoveGroupMConn(CtrlType *, GraphType *, idxtype *, idxtype *, int, int, int, idxtype *);
+void EliminateComponents(CtrlType *, GraphType *, int, float *, float);
+void MoveGroup(CtrlType *, GraphType *, int, int, int, idxtype *, idxtype *);
+
+/* timing.c */
+void InitTimers(CtrlType *);
+void PrintTimers(CtrlType *);
+double seconds(void);
+
+/* util.c */
+void errexit(char *,...);
+#ifndef DMALLOC
+int *imalloc(int, char *);
+idxtype *idxmalloc(int, char *);
+float *fmalloc(int, char *);
+int *ismalloc(int, int, char *);
+idxtype *idxsmalloc(int, idxtype, char *);
+void *GKmalloc(int, char *);
+#endif
+/*void GKfree(void **,...); */
+int *iset(int n, int val, int *x);
+idxtype *idxset(int n, idxtype val, idxtype *x);
+float *sset(int n, float val, float *x);
+int iamax(int, int *);
+int idxamax(int, idxtype *);
+int idxamax_strd(int, idxtype *, int);
+int samax(int, float *);
+int samax2(int, float *);
+int idxamin(int, idxtype *);
+int samin(int, float *);
+int idxsum(int, idxtype *);
+int idxsum_strd(int, idxtype *, int);
+void idxadd(int, idxtype *, idxtype *);
+int charsum(int, char *);
+int isum(int, int *);
+float ssum(int, float *);
+float ssum_strd(int n, float *x, int);
+void sscale(int n, float, float *x);
+float snorm2(int, float *);
+float sdot(int n, float *, float *);
+void saxpy(int, float, float *, int, float *, int);
+void RandomPermute(int, idxtype *, int);
+double drand48();
+void srand48(long);
+int ispow2(int);
+void InitRandom(int);
+//int log2(int);
+
+
+
+
+
+
+
+
+
+
+/***************************************************************
+* Programs Directory
+****************************************************************/
+
+/* io.c */
+void ReadGraph(GraphType *, char *, int *);
+void WritePartition(char *, idxtype *, int, int);
+void WriteMeshPartition(char *, int, int, idxtype *, int, idxtype *);
+void WritePermutation(char *, idxtype *, int);
+int CheckGraph(GraphType *);
+idxtype *ReadMesh(char *, int *, int *, int *);
+void WriteGraph(char *, int, idxtype *, idxtype *);
+
+/* smbfactor.c */
+void ComputeFillIn(GraphType *, idxtype *);
+idxtype ComputeFillIn2(GraphType *, idxtype *);
+int smbfct(int, idxtype *, idxtype *, idxtype *, idxtype *, idxtype *, int *, idxtype *, idxtype *, int *);
+
+
+/***************************************************************
+* Test Directory
+****************************************************************/
+void Test_PartGraph(int, idxtype *, idxtype *);
+int VerifyPart(int, idxtype *, idxtype *, idxtype *, idxtype *, int, int, idxtype *);
+int VerifyWPart(int, idxtype *, idxtype *, idxtype *, idxtype *, int, float *, int, idxtype *);
+void Test_PartGraphV(int, idxtype *, idxtype *);
+int VerifyPartV(int, idxtype *, idxtype *, idxtype *, idxtype *, int, int, idxtype *);
+int VerifyWPartV(int, idxtype *, idxtype *, idxtype *, idxtype *, int, float *, int, idxtype *);
+void Test_PartGraphmC(int, idxtype *, idxtype *);
+int VerifyPartmC(int, int, idxtype *, idxtype *, idxtype *, idxtype *, int, float *, int, idxtype *);
+void Test_ND(int, idxtype *, idxtype *);
+int VerifyND(int, idxtype *, idxtype *);
+
diff --git a/contrib/Metis/refine.c b/contrib/Metis/refine.c
new file mode 100644
index 0000000000..3d48683473
--- /dev/null
+++ b/contrib/Metis/refine.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * refine.c
+ *
+ * This file contains the driving routines for multilevel refinement
+ *
+ * Started 7/24/97
+ * George
+ *
+ * $Id: refine.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function is the entry point of refinement
+**************************************************************************/
+void Refine2Way(CtrlType *ctrl, GraphType *orggraph, GraphType *graph, int *tpwgts, float ubfactor)
+{
+
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->UncoarsenTmr));
+
+  /* Compute the parameters of the coarsest graph */
+  Compute2WayPartitionParams(ctrl, graph);
+
+  for (;;) {
+    ASSERT(CheckBnd(graph));
+
+    IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->RefTmr));
+    switch (ctrl->RType) {
+      case 1:
+        Balance2Way(ctrl, graph, tpwgts, ubfactor);
+        FM_2WayEdgeRefine(ctrl, graph, tpwgts, 8); 
+        break;
+      default:
+        errexit("Unknown refinement type: %d\n", ctrl->RType);
+    }
+    IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->RefTmr));
+
+    if (graph == orggraph)
+      break;
+
+    graph = graph->finer;
+    IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->ProjectTmr));
+    Project2WayPartition(ctrl, graph);
+    IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->ProjectTmr));
+  }
+
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->UncoarsenTmr));
+}
+
+
+/*************************************************************************
+* This function allocates memory for 2-way edge refinement
+**************************************************************************/
+void Allocate2WayPartitionMemory(CtrlType *ctrl, GraphType *graph)
+{
+  int nvtxs;
+
+  nvtxs = graph->nvtxs;
+
+  graph->rdata = idxmalloc(5*nvtxs+2, "Allocate2WayPartitionMemory: rdata");
+  graph->pwgts 		= graph->rdata;
+  graph->where		= graph->rdata + 2;
+  graph->id		= graph->rdata + nvtxs + 2;
+  graph->ed		= graph->rdata + 2*nvtxs + 2;
+  graph->bndptr		= graph->rdata + 3*nvtxs + 2;
+  graph->bndind		= graph->rdata + 4*nvtxs + 2;
+}
+
+
+/*************************************************************************
+* This function computes the initial id/ed 
+**************************************************************************/
+void Compute2WayPartitionParams(CtrlType *ctrl, GraphType *graph)
+{
+  int i, j, k, l, nvtxs, nbnd, mincut;
+  idxtype *xadj, *vwgt, *adjncy, *adjwgt, *pwgts;
+  idxtype *id, *ed, *where;
+  idxtype *bndptr, *bndind;
+  int me, other;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  vwgt = graph->vwgt;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  where = graph->where;
+  pwgts = idxset(2, 0, graph->pwgts);
+  id = idxset(nvtxs, 0, graph->id);
+  ed = idxset(nvtxs, 0, graph->ed);
+  bndptr = idxset(nvtxs, -1, graph->bndptr);
+  bndind = graph->bndind;
+
+
+  /*------------------------------------------------------------
+  / Compute now the id/ed degrees
+  /------------------------------------------------------------*/
+  nbnd = mincut = 0;
+  for (i=0; i<nvtxs; i++) {
+    ASSERT(where[i] >= 0 && where[i] <= 1);
+    me = where[i];
+    pwgts[me] += vwgt[i];
+
+    for (j=xadj[i]; j<xadj[i+1]; j++) {
+      if (me == where[adjncy[j]])
+        id[i] += adjwgt[j];
+      else
+        ed[i] += adjwgt[j];
+    }
+
+    if (ed[i] > 0 || xadj[i] == xadj[i+1]) {
+      mincut += ed[i];
+      bndptr[i] = nbnd;
+      bndind[nbnd++] = i;
+    }
+  }
+
+  graph->mincut = mincut/2;
+  graph->nbnd = nbnd;
+
+  ASSERT(pwgts[0]+pwgts[1] == idxsum(nvtxs, vwgt));
+}
+
+
+
+/*************************************************************************
+* This function projects a partition, and at the same time computes the
+* parameters for refinement.
+**************************************************************************/
+void Project2WayPartition(CtrlType *ctrl, GraphType *graph)
+{
+  int i, j, k, nvtxs, nbnd, me;
+  idxtype *xadj, *adjncy, *adjwgt, *adjwgtsum;
+  idxtype *cmap, *where, *id, *ed, *bndptr, *bndind;
+  idxtype *cwhere, *cid, *ced, *cbndptr;
+  GraphType *cgraph;
+
+  cgraph = graph->coarser;
+  cwhere = cgraph->where;
+  cid = cgraph->id;
+  ced = cgraph->ed;
+  cbndptr = cgraph->bndptr;
+
+  nvtxs = graph->nvtxs;
+  cmap = graph->cmap;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+  adjwgtsum = graph->adjwgtsum;
+
+  Allocate2WayPartitionMemory(ctrl, graph);
+
+  where = graph->where;
+  id = idxset(nvtxs, 0, graph->id);
+  ed = idxset(nvtxs, 0, graph->ed);
+  bndptr = idxset(nvtxs, -1, graph->bndptr);
+  bndind = graph->bndind;
+
+
+  /* Go through and project partition and compute id/ed for the nodes */
+  for (i=0; i<nvtxs; i++) {
+    k = cmap[i];
+    where[i] = cwhere[k];
+    cmap[i] = cbndptr[k];
+  }
+
+  for (nbnd=0, i=0; i<nvtxs; i++) {
+    me = where[i];
+
+    id[i] = adjwgtsum[i];
+
+    if (xadj[i] == xadj[i+1]) {
+      bndptr[i] = nbnd;
+      bndind[nbnd++] = i;
+    }
+    else {
+      if (cmap[i] != -1) { /* If it is an interface node. Note that cmap[i] = cbndptr[cmap[i]] */
+        for (j=xadj[i]; j<xadj[i+1]; j++) {
+          if (me != where[adjncy[j]])
+            ed[i] += adjwgt[j];
+        }
+        id[i] -= ed[i];
+
+        if (ed[i] > 0 || xadj[i] == xadj[i+1]) {
+          bndptr[i] = nbnd;
+          bndind[nbnd++] = i;
+        }
+      }
+    }
+  }
+
+  graph->mincut = cgraph->mincut;
+  graph->nbnd = nbnd;
+  idxcopy(2, cgraph->pwgts, graph->pwgts);
+
+  FreeGraph(graph->coarser);
+  graph->coarser = NULL;
+
+}
+
diff --git a/contrib/Metis/rename.h b/contrib/Metis/rename.h
new file mode 100644
index 0000000000..49e01e87ac
--- /dev/null
+++ b/contrib/Metis/rename.h
@@ -0,0 +1,418 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * rename.h
+ *
+ * This file contains header files
+ *
+ * Started 10/2/97
+ * George
+ *
+ * $Id: rename.h,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ *
+ */
+
+/* balance.c */
+#define Balance2Way			__Balance2Way
+#define Bnd2WayBalance			__Bnd2WayBalance
+#define General2WayBalance		__General2WayBalance
+
+
+/* bucketsort.c */
+#define BucketSortKeysInc		__BucketSortKeysInc
+
+
+/* ccgraph.c */
+#define CreateCoarseGraph		__CreateCoarseGraph
+#define CreateCoarseGraphNoMask		__CreateCoarseGraphNoMask
+#define CreateCoarseGraph_NVW 		__CreateCoarseGraph_NVW
+#define SetUpCoarseGraph		__SetUpCoarseGraph
+#define ReAdjustMemory			__ReAdjustMemory
+
+
+/* coarsen.c */
+#define Coarsen2Way			__Coarsen2Way
+
+
+/* compress.c */
+#define CompressGraph			__CompressGraph
+#define PruneGraph			__PruneGraph
+
+
+/* debug.c */
+#define ComputeCut			__ComputeCut
+#define CheckBnd			__CheckBnd
+#define CheckBnd2			__CheckBnd2
+#define CheckNodeBnd			__CheckNodeBnd
+#define CheckRInfo			__CheckRInfo
+#define CheckNodePartitionParams	__CheckNodePartitionParams
+#define IsSeparable			__IsSeparable
+
+
+/* estmem.c */
+#define EstimateCFraction		__EstimateCFraction
+#define ComputeCoarseGraphSize		__ComputeCoarseGraphSize
+
+
+/* fm.c */
+#define FM_2WayEdgeRefine		__FM_2WayEdgeRefine
+
+
+/* fortran.c */
+#define Change2CNumbering		__Change2CNumbering
+#define Change2FNumbering		__Change2FNumbering
+#define Change2FNumbering2		__Change2FNumbering2
+#define Change2FNumberingOrder		__Change2FNumberingOrder
+#define ChangeMesh2CNumbering		__ChangeMesh2CNumbering
+#define ChangeMesh2FNumbering		__ChangeMesh2FNumbering
+#define ChangeMesh2FNumbering2		__ChangeMesh2FNumbering2
+
+
+/* graph.c */
+#define SetUpGraph			__SetUpGraph
+#define SetUpGraphKway 			__SetUpGraphKway
+#define SetUpGraph2			__SetUpGraph2
+#define VolSetUpGraph			__VolSetUpGraph
+#define RandomizeGraph			__RandomizeGraph
+#define IsConnectedSubdomain		__IsConnectedSubdomain
+#define IsConnected			__IsConnected
+#define IsConnected2			__IsConnected2
+#define FindComponents			__FindComponents
+
+
+/* initpart.c */
+#define Init2WayPartition		__Init2WayPartition
+#define InitSeparator			__InitSeparator
+#define GrowBisection			__GrowBisection
+#define GrowBisectionNode		__GrowBisectionNode
+#define RandomBisection			__RandomBisection
+
+
+/* kmetis.c */
+#define MlevelKWayPartitioning		__MlevelKWayPartitioning
+
+
+/* kvmetis.c */
+#define MlevelVolKWayPartitioning	__MlevelVolKWayPartitioning
+
+
+/* kwayfm.c */
+#define Random_KWayEdgeRefine		__Random_KWayEdgeRefine
+#define Greedy_KWayEdgeRefine		__Greedy_KWayEdgeRefine
+#define Greedy_KWayEdgeBalance		__Greedy_KWayEdgeBalance
+
+
+/* kwayrefine.c */
+#define RefineKWay			__RefineKWay
+#define AllocateKWayPartitionMemory	__AllocateKWayPartitionMemory
+#define ComputeKWayPartitionParams	__ComputeKWayPartitionParams
+#define ProjectKWayPartition		__ProjectKWayPartition
+#define IsBalanced			__IsBalanced
+#define ComputeKWayBoundary		__ComputeKWayBoundary
+#define ComputeKWayBalanceBoundary	__ComputeKWayBalanceBoundary
+
+
+/* kwayvolfm.c */
+#define Random_KWayVolRefine		__Random_KWayVolRefine
+#define Random_KWayVolRefineMConn	__Random_KWayVolRefineMConn
+#define Greedy_KWayVolBalance		__Greedy_KWayVolBalance
+#define Greedy_KWayVolBalanceMConn	__Greedy_KWayVolBalanceMConn
+#define KWayVolUpdate			__KWayVolUpdate
+#define ComputeKWayVolume		__ComputeKWayVolume
+#define ComputeVolume			__ComputeVolume
+#define CheckVolKWayPartitionParams	__CheckVolKWayPartitionParams
+#define ComputeVolSubDomainGraph	__ComputeVolSubDomainGraph
+#define EliminateVolSubDomainEdges	__EliminateVolSubDomainEdges
+
+
+/* kwayvolrefine.c */
+#define RefineVolKWay			__RefineVolKWay
+#define AllocateVolKWayPartitionMemory	__AllocateVolKWayPartitionMemory
+#define ComputeVolKWayPartitionParams	__ComputeVolKWayPartitionParams
+#define ComputeKWayVolGains		__ComputeKWayVolGains
+#define ProjectVolKWayPartition		__ProjectVolKWayPartition
+#define ComputeVolKWayBoundary		__ComputeVolKWayBoundary
+#define ComputeVolKWayBalanceBoundary	__ComputeVolKWayBalanceBoundary
+
+
+/* match.c */
+#define Match_RM			__Match_RM
+#define Match_RM_NVW			__Match_RM_NVW
+#define Match_HEM			__Match_HEM
+#define Match_SHEM			__Match_SHEM
+
+
+/* mbalance.c */
+#define MocBalance2Way			__MocBalance2Way
+#define MocGeneral2WayBalance		__MocGeneral2WayBalance
+
+
+/* mbalance2.c */
+#define MocBalance2Way2			__MocBalance2Way2
+#define MocGeneral2WayBalance2		__MocGeneral2WayBalance2
+#define SelectQueue3			__SelectQueue3
+
+
+/* mcoarsen.c */
+#define MCCoarsen2Way			__MCCoarsen2Way
+
+
+/* memory.c */
+#define AllocateWorkSpace		__AllocateWorkSpace
+#define FreeWorkSpace			__FreeWorkSpace
+#define WspaceAvail			__WspaceAvail
+#define idxwspacemalloc			__idxwspacemalloc
+#define idxwspacefree			__idxwspacefree
+#define fwspacemalloc			__fwspacemalloc
+#define CreateGraph			__CreateGraph
+#define InitGraph			__InitGraph
+#define FreeGraph			__FreeGraph
+
+
+/* mesh.c */
+#define TRIDUALMETIS			__TRIDUALMETIS
+#define TETDUALMETIS			__TETDUALMETIS
+#define HEXDUALMETIS			__HEXDUALMETIS
+#define TRINODALMETIS			__TRINODALMETIS
+#define TETNODALMETIS			__TETNODALMETIS
+#define HEXNODALMETIS			__HEXNODALMETIS
+
+
+/* mfm.c */
+#define MocFM_2WayEdgeRefine		__MocFM_2WayEdgeRefine
+#define SelectQueue			__SelectQueue
+#define BetterBalance			__BetterBalance
+#define Compute2WayHLoadImbalance	__Compute2WayHLoadImbalance
+#define Compute2WayHLoadImbalanceVec	__Compute2WayHLoadImbalanceVec
+
+
+/* mfm2.c */
+#define MocFM_2WayEdgeRefine2		__MocFM_2WayEdgeRefine2
+#define SelectQueue2			__SelectQueue2
+#define IsBetter2wayBalance		__IsBetter2wayBalance
+
+
+/* mincover.c */
+#define MinCover			__MinCover
+#define MinCover_Augment		__MinCover_Augment
+#define MinCover_Decompose		__MinCover_Decompose
+#define MinCover_ColDFS			__MinCover_ColDFS
+#define MinCover_RowDFS			__MinCover_RowDFS
+
+
+/* minitpart.c */
+#define MocInit2WayPartition		__MocInit2WayPartition
+#define MocGrowBisection		__MocGrowBisection
+#define MocRandomBisection		__MocRandomBisection
+#define MocInit2WayBalance		__MocInit2WayBalance
+#define SelectQueueoneWay		__SelectQueueoneWay
+
+
+/* minitpart2.c */
+#define MocInit2WayPartition2		__MocInit2WayPartition2
+#define MocGrowBisection2		__MocGrowBisection2
+#define MocGrowBisectionNew2		__MocGrowBisectionNew2
+#define MocInit2WayBalance2		__MocInit2WayBalance2
+#define SelectQueueOneWay2		__SelectQueueOneWay2
+
+
+/* mkmetis.c */
+#define MCMlevelKWayPartitioning	__MCMlevelKWayPartitioning
+
+
+/* mkwayfmh.c */
+#define MCRandom_KWayEdgeRefineHorizontal	__MCRandom_KWayEdgeRefineHorizontal
+#define MCGreedy_KWayEdgeBalanceHorizontal	__MCGreedy_KWayEdgeBalanceHorizontal
+#define AreAllHVwgtsBelow			__AreAllHVwgtsBelow
+#define AreAllHVwgtsAbove			__AreAllHVwgtsAbove
+#define ComputeHKWayLoadImbalance		__ComputeHKWayLoadImbalance
+#define MocIsHBalanced				__MocIsHBalanced
+#define IsHBalanceBetterFT			__IsHBalanceBetterFT
+#define IsHBalanceBetterTT			__IsHBalanceBetterTT
+
+
+/* mkwayrefine.c */
+#define MocRefineKWayHorizontal		__MocRefineKWayHorizontal
+#define MocAllocateKWayPartitionMemory	__MocAllocateKWayPartitionMemory
+#define MocComputeKWayPartitionParams	__MocComputeKWayPartitionParams
+#define MocProjectKWayPartition		__MocProjectKWayPartition
+#define MocComputeKWayBalanceBoundary	__MocComputeKWayBalanceBoundary
+
+
+/* mmatch.c */
+#define MCMatch_RM			__MCMatch_RM
+#define MCMatch_HEM			__MCMatch_HEM
+#define MCMatch_SHEM			__MCMatch_SHEM
+#define MCMatch_SHEBM			__MCMatch_SHEBM
+#define MCMatch_SBHEM			__MCMatch_SBHEM
+#define BetterVBalance			__BetterVBalance
+#define AreAllVwgtsBelowFast		__AreAllVwgtsBelowFast
+
+
+/* mmd.c */
+#define genmmd				__genmmd
+#define mmdelm				__mmdelm
+#define mmdint				__mmdint
+#define mmdnum				__mmdnum
+#define mmdupd				__mmdupd
+
+
+/* mpmetis.c */
+#define MCMlevelRecursiveBisection	__MCMlevelRecursiveBisection
+#define MCHMlevelRecursiveBisection	__MCHMlevelRecursiveBisection
+#define MCMlevelEdgeBisection		__MCMlevelEdgeBisection
+#define MCHMlevelEdgeBisection		__MCHMlevelEdgeBisection
+
+
+/* mrefine.c */
+#define MocRefine2Way			__MocRefine2Way
+#define MocAllocate2WayPartitionMemory	__MocAllocate2WayPartitionMemory
+#define MocCompute2WayPartitionParams	__MocCompute2WayPartitionParams
+#define MocProject2WayPartition		__MocProject2WayPartition
+
+
+/* mrefine2.c */
+#define MocRefine2Way2			__MocRefine2Way2
+
+
+/* mutil.c */
+#define AreAllVwgtsBelow		__AreAllVwgtsBelow
+#define AreAnyVwgtsBelow		__AreAnyVwgtsBelow
+#define AreAllVwgtsAbove		__AreAllVwgtsAbove
+#define ComputeLoadImbalance		__ComputeLoadImbalance
+#define AreAllBelow			__AreAllBelow
+
+
+/* myqsort.c */
+#define iidxsort			__iidxsort
+#define iintsort			__iintsort
+#define ikeysort			__ikeysort
+#define ikeyvalsort			__ikeyvalsort
+
+
+/* ometis.c */
+#define MlevelNestedDissection		__MlevelNestedDissection
+#define MlevelNestedDissectionCC	__MlevelNestedDissectionCC
+#define MlevelNodeBisectionMultiple	__MlevelNodeBisectionMultiple
+#define MlevelNodeBisection		__MlevelNodeBisection
+#define SplitGraphOrder			__SplitGraphOrder
+#define MMDOrder			__MMDOrder
+#define SplitGraphOrderCC		__SplitGraphOrderCC
+
+
+/* parmetis.c */
+#define MlevelNestedDissectionP		__MlevelNestedDissectionP
+
+
+/* pmetis.c */
+#define MlevelRecursiveBisection	__MlevelRecursiveBisection
+#define MlevelEdgeBisection		__MlevelEdgeBisection
+#define SplitGraphPart			__SplitGraphPart
+#define SetUpSplitGraph			__SetUpSplitGraph
+
+
+/* pqueue.c */
+#define PQueueInit			__PQueueInit
+#define PQueueReset			__PQueueReset
+#define PQueueFree			__PQueueFree
+#define PQueueInsert			__PQueueInsert
+#define PQueueDelete			__PQueueDelete
+#define PQueueUpdate			__PQueueUpdate
+#define PQueueUpdateUp			__PQueueUpdateUp
+#define PQueueGetMax			__PQueueGetMax
+#define PQueueSeeMax			__PQueueSeeMax
+#define CheckHeap			__CheckHeap
+
+
+/* refine.c */
+#define Refine2Way			__Refine2Way
+#define Allocate2WayPartitionMemory	__Allocate2WayPartitionMemory
+#define Compute2WayPartitionParams	__Compute2WayPartitionParams
+#define Project2WayPartition		__Project2WayPartition
+
+
+/* separator.c */
+#define ConstructSeparator		__ConstructSeparator
+#define ConstructMinCoverSeparator0	__ConstructMinCoverSeparator0
+#define ConstructMinCoverSeparator	__ConstructMinCoverSeparator
+
+
+/* sfm.c */
+#define FM_2WayNodeRefine		__FM_2WayNodeRefine
+#define FM_2WayNodeRefineEqWgt		__FM_2WayNodeRefineEqWgt
+#define FM_2WayNodeRefine_OneSided	__FM_2WayNodeRefine_OneSided
+#define FM_2WayNodeBalance		__FM_2WayNodeBalance
+#define ComputeMaxNodeGain		__ComputeMaxNodeGain
+
+
+/* srefine.c */
+#define Refine2WayNode			__Refine2WayNode
+#define Allocate2WayNodePartitionMemory	__Allocate2WayNodePartitionMemory
+#define Compute2WayNodePartitionParams	__Compute2WayNodePartitionParams
+#define Project2WayNodePartition	__Project2WayNodePartition
+
+
+/* stat.c */
+#define ComputePartitionInfo		__ComputePartitionInfo
+#define ComputePartitionBalance		__ComputePartitionBalance
+#define ComputeElementBalance		__ComputeElementBalance
+
+
+/* subdomains.c */
+#define Random_KWayEdgeRefineMConn	__Random_KWayEdgeRefineMConn
+#define Greedy_KWayEdgeBalanceMConn	__Greedy_KWayEdgeBalanceMConn
+#define PrintSubDomainGraph		__PrintSubDomainGraph
+#define ComputeSubDomainGraph		__ComputeSubDomainGraph
+#define EliminateSubDomainEdges		__EliminateSubDomainEdges
+#define MoveGroupMConn			__MoveGroupMConn
+#define EliminateComponents		__EliminateComponents
+#define MoveGroup			__MoveGroup
+
+
+/* timing.c */
+#define InitTimers			__InitTimers
+#define PrintTimers			__PrintTimers
+#define seconds				__seconds
+
+
+/* util.c */
+#define errexit				__errexit
+#define GKfree				__GKfree
+#ifndef DMALLOC
+#define imalloc				__imalloc
+#define idxmalloc			__idxmalloc
+#define fmalloc				__fmalloc
+#define ismalloc			__ismalloc
+#define idxsmalloc			__idxsmalloc
+#define GKmalloc			__GKmalloc
+#endif
+#define iset				__iset
+#define idxset				__idxset
+#define sset				__sset
+#define iamax				__iamax
+#define idxamax				__idxamax
+#define idxamax_strd			__idxamax_strd
+#define samax				__samax
+#define samax2				__samax2
+#define idxamin				__idxamin
+#define samin				__samin
+#define idxsum				__idxsum
+#define idxsum_strd			__idxsum_strd
+#define idxadd				__idxadd
+#define charsum				__charsum
+#define isum				__isum
+#define ssum				__ssum
+#define ssum_strd			__ssum_strd
+#define sscale				__sscale
+#define snorm2				__snorm2
+#define sdot				__sdot
+#define saxpy				__saxpy
+#define RandomPermute			__RandomPermute
+#define ispow2				__ispow2
+#define InitRandom			__InitRandom
+#define log2				__log2
+
+
+
+
+
diff --git a/contrib/Metis/separator.c b/contrib/Metis/separator.c
new file mode 100644
index 0000000000..0c81725b8a
--- /dev/null
+++ b/contrib/Metis/separator.c
@@ -0,0 +1,284 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * separator.c
+ *
+ * This file contains code for separator extraction
+ *
+ * Started 8/1/97
+ * George
+ *
+ * $Id: separator.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+/*************************************************************************
+* This function takes a bisection and constructs a minimum weight vertex 
+* separator out of it. It uses the node-based separator refinement for it.
+**************************************************************************/
+void ConstructSeparator(CtrlType *ctrl, GraphType *graph, float ubfactor)
+{
+  int i, j, k, nvtxs, nbnd;
+  idxtype *xadj, *where, *bndind;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  nbnd = graph->nbnd;
+  bndind = graph->bndind;
+
+  where = idxcopy(nvtxs, graph->where, idxwspacemalloc(ctrl, nvtxs));
+
+  /* Put the nodes in the boundary into the separator */
+  for (i=0; i<nbnd; i++) {
+    j = bndind[i];
+    if (xadj[j+1]-xadj[j] > 0)  /* Ignore islands */
+      where[j] = 2;
+  }
+
+  GKfree(&graph->rdata, LTERM);
+  Allocate2WayNodePartitionMemory(ctrl, graph);
+  idxcopy(nvtxs, where, graph->where);
+  idxwspacefree(ctrl, nvtxs);
+
+  ASSERT(IsSeparable(graph));
+
+  Compute2WayNodePartitionParams(ctrl, graph);
+
+  ASSERT(CheckNodePartitionParams(graph));
+
+  FM_2WayNodeRefine(ctrl, graph, ubfactor, 8); 
+
+  ASSERT(IsSeparable(graph));
+}
+
+
+
+/*************************************************************************
+* This function takes a bisection and constructs a minimum weight vertex 
+* separator out of it. It uses an unweighted minimum-cover algorithm
+* followed by node-based separator refinement.
+**************************************************************************/
+void ConstructMinCoverSeparator0(CtrlType *ctrl, GraphType *graph, float ubfactor)
+{
+  int i, ii, j, jj, k, l, nvtxs, nbnd, bnvtxs[3], bnedges[2], csize;
+  idxtype *xadj, *adjncy, *bxadj, *badjncy;
+  idxtype *where, *bndind, *bndptr, *vmap, *ivmap, *cover;
+
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+
+  nbnd = graph->nbnd;
+  bndind = graph->bndind;
+  bndptr = graph->bndptr;
+  where = graph->where;
+
+  vmap = idxwspacemalloc(ctrl, nvtxs);
+  ivmap = idxwspacemalloc(ctrl, nbnd);
+  cover = idxwspacemalloc(ctrl, nbnd);
+
+  if (nbnd > 0) {
+    /* Go through the boundary and determine the sizes of the bipartite graph */
+    bnvtxs[0] = bnvtxs[1] = bnedges[0] = bnedges[1] = 0;
+    for (i=0; i<nbnd; i++) {
+      j = bndind[i];
+      k = where[j];
+      if (xadj[j+1]-xadj[j] > 0) {
+        bnvtxs[k]++;
+        bnedges[k] += xadj[j+1]-xadj[j];
+      }
+    }
+
+    bnvtxs[2] = bnvtxs[0]+bnvtxs[1];
+    bnvtxs[1] = bnvtxs[0];
+    bnvtxs[0] = 0;
+
+    bxadj = idxmalloc(bnvtxs[2]+1, "ConstructMinCoverSeparator: bxadj");
+    badjncy = idxmalloc(bnedges[0]+bnedges[1]+1, "ConstructMinCoverSeparator: badjncy");
+
+    /* Construct the ivmap and vmap */
+    ASSERT(idxset(nvtxs, -1, vmap) == vmap);
+    for (i=0; i<nbnd; i++) {
+      j = bndind[i];
+      k = where[j];
+      if (xadj[j+1]-xadj[j] > 0) {
+        vmap[j] = bnvtxs[k];
+        ivmap[bnvtxs[k]++] = j;
+      }
+    }
+
+    /* OK, go through and put the vertices of each part starting from 0 */
+    bnvtxs[1] = bnvtxs[0];
+    bnvtxs[0] = 0;
+    bxadj[0] = l = 0;
+    for (k=0; k<2; k++) {
+      for (ii=0; ii<nbnd; ii++) {
+        i = bndind[ii];
+        if (where[i] == k && xadj[i] < xadj[i+1]) {
+          for (j=xadj[i]; j<xadj[i+1]; j++) {
+            jj = adjncy[j];
+            if (where[jj] != k) {
+              ASSERT(bndptr[jj] != -1); 
+              ASSERTP(vmap[jj] != -1, ("%d %d %d\n", jj, vmap[jj], graph->bndptr[jj]));
+              badjncy[l++] = vmap[jj];
+            }
+          }
+          bxadj[++bnvtxs[k]] = l;
+        }
+      }
+    }
+
+    ASSERT(l <= bnedges[0]+bnedges[1]);
+
+    MinCover(bxadj, badjncy, bnvtxs[0], bnvtxs[1], cover, &csize);
+
+    IFSET(ctrl->dbglvl, DBG_SEPINFO,
+      printf("Nvtxs: %6d, [%5d %5d], Cut: %6d, SS: [%6d %6d], Cover: %6d\n", nvtxs, graph->pwgts[0], graph->pwgts[1], graph->mincut, bnvtxs[0], bnvtxs[1]-bnvtxs[0], csize));
+
+    for (i=0; i<csize; i++) {
+      j = ivmap[cover[i]];
+      where[j] = 2;
+    }
+
+    GKfree(&bxadj, &badjncy, LTERM);
+
+    for (i=0; i<nbnd; i++)
+      bndptr[bndind[i]] = -1;
+    for (nbnd=i=0; i<nvtxs; i++) {
+      if (where[i] == 2) {
+        bndind[nbnd] = i;
+        bndptr[i] = nbnd++;
+      }
+    }
+  }
+  else {
+    IFSET(ctrl->dbglvl, DBG_SEPINFO,
+      printf("Nvtxs: %6d, [%5d %5d], Cut: %6d, SS: [%6d %6d], Cover: %6d\n", nvtxs, graph->pwgts[0], graph->pwgts[1], graph->mincut, 0, 0, 0));
+  }
+
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, graph->nbnd);
+  idxwspacefree(ctrl, graph->nbnd);
+  graph->nbnd = nbnd;
+
+
+  ASSERT(IsSeparable(graph));
+}
+
+
+
+/*************************************************************************
+* This function takes a bisection and constructs a minimum weight vertex 
+* separator out of it. It uses an unweighted minimum-cover algorithm
+* followed by node-based separator refinement.
+**************************************************************************/
+void ConstructMinCoverSeparator(CtrlType *ctrl, GraphType *graph, float ubfactor)
+{
+  int i, ii, j, jj, k, l, nvtxs, nbnd, bnvtxs[3], bnedges[2], csize;
+  idxtype *xadj, *adjncy, *bxadj, *badjncy;
+  idxtype *where, *bndind, *bndptr, *vmap, *ivmap, *cover;
+
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+
+  nbnd = graph->nbnd;
+  bndind = graph->bndind;
+  bndptr = graph->bndptr;
+  where = graph->where;
+
+  vmap = idxwspacemalloc(ctrl, nvtxs);
+  ivmap = idxwspacemalloc(ctrl, nbnd);
+  cover = idxwspacemalloc(ctrl, nbnd);
+
+  if (nbnd > 0) {
+    /* Go through the boundary and determine the sizes of the bipartite graph */
+    bnvtxs[0] = bnvtxs[1] = bnedges[0] = bnedges[1] = 0;
+    for (i=0; i<nbnd; i++) {
+      j = bndind[i];
+      k = where[j];
+      if (xadj[j+1]-xadj[j] > 0) {
+        bnvtxs[k]++;
+        bnedges[k] += xadj[j+1]-xadj[j];
+      }
+    }
+
+    bnvtxs[2] = bnvtxs[0]+bnvtxs[1];
+    bnvtxs[1] = bnvtxs[0];
+    bnvtxs[0] = 0;
+
+    bxadj = idxmalloc(bnvtxs[2]+1, "ConstructMinCoverSeparator: bxadj");
+    badjncy = idxmalloc(bnedges[0]+bnedges[1]+1, "ConstructMinCoverSeparator: badjncy");
+
+    /* Construct the ivmap and vmap */
+    ASSERT(idxset(nvtxs, -1, vmap) == vmap);
+    for (i=0; i<nbnd; i++) {
+      j = bndind[i];
+      k = where[j];
+      if (xadj[j+1]-xadj[j] > 0) {
+        vmap[j] = bnvtxs[k];
+        ivmap[bnvtxs[k]++] = j;
+      }
+    }
+
+    /* OK, go through and put the vertices of each part starting from 0 */
+    bnvtxs[1] = bnvtxs[0];
+    bnvtxs[0] = 0;
+    bxadj[0] = l = 0;
+    for (k=0; k<2; k++) {
+      for (ii=0; ii<nbnd; ii++) {
+        i = bndind[ii];
+        if (where[i] == k && xadj[i] < xadj[i+1]) {
+          for (j=xadj[i]; j<xadj[i+1]; j++) {
+            jj = adjncy[j];
+            if (where[jj] != k) {
+              ASSERT(bndptr[jj] != -1); 
+              ASSERTP(vmap[jj] != -1, ("%d %d %d\n", jj, vmap[jj], graph->bndptr[jj]));
+              badjncy[l++] = vmap[jj];
+            }
+          }
+          bxadj[++bnvtxs[k]] = l;
+        }
+      }
+    }
+
+    ASSERT(l <= bnedges[0]+bnedges[1]);
+
+    MinCover(bxadj, badjncy, bnvtxs[0], bnvtxs[1], cover, &csize);
+
+    IFSET(ctrl->dbglvl, DBG_SEPINFO,
+      printf("Nvtxs: %6d, [%5d %5d], Cut: %6d, SS: [%6d %6d], Cover: %6d\n", nvtxs, graph->pwgts[0], graph->pwgts[1], graph->mincut, bnvtxs[0], bnvtxs[1]-bnvtxs[0], csize));
+
+    for (i=0; i<csize; i++) {
+      j = ivmap[cover[i]];
+      where[j] = 2;
+    }
+
+    GKfree(&bxadj, &badjncy, LTERM);
+  }
+  else {
+    IFSET(ctrl->dbglvl, DBG_SEPINFO,
+      printf("Nvtxs: %6d, [%5d %5d], Cut: %6d, SS: [%6d %6d], Cover: %6d\n", nvtxs, graph->pwgts[0], graph->pwgts[1], graph->mincut, 0, 0, 0));
+  }
+
+  /* Prepare to refine the vertex separator */
+  idxcopy(nvtxs, graph->where, vmap);
+  GKfree(&graph->rdata, LTERM);
+
+  Allocate2WayNodePartitionMemory(ctrl, graph);
+  idxcopy(nvtxs, vmap, graph->where);
+  idxwspacefree(ctrl, nvtxs+2*graph->nbnd);
+
+  Compute2WayNodePartitionParams(ctrl, graph);
+
+  ASSERT(CheckNodePartitionParams(graph));
+
+  FM_2WayNodeRefine_OneSided(ctrl, graph, ubfactor, 6); 
+
+  ASSERT(IsSeparable(graph));
+}
+
diff --git a/contrib/Metis/sfm.c b/contrib/Metis/sfm.c
new file mode 100644
index 0000000000..40108b87b1
--- /dev/null
+++ b/contrib/Metis/sfm.c
@@ -0,0 +1,1069 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * sfm.c
+ *
+ * This file contains code that implementes an FM-based separator refinement
+ *
+ * Started 8/1/97
+ * George
+ *
+ * $Id: sfm.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function performs a node-based FM refinement 
+**************************************************************************/
+void FM_2WayNodeRefine(CtrlType *ctrl, GraphType *graph, float ubfactor, int npasses)
+{
+  int i, ii, j, k, jj, kk, nvtxs, nbnd, nswaps, nmind;
+  idxtype *xadj, *vwgt, *adjncy, *where, *pwgts, *edegrees, *bndind, *bndptr;
+  idxtype *mptr, *mind, *moved, *swaps, *perm;
+  PQueueType parts[2]; 
+  NRInfoType *rinfo;
+  int higain, oldgain, mincut, initcut, mincutorder;	
+  int pass, to, other, limit;
+  int badmaxpwgt, mindiff, newdiff;
+  int u[2], g[2];
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  vwgt = graph->vwgt;
+
+  bndind = graph->bndind;
+  bndptr = graph->bndptr;
+  where = graph->where;
+  pwgts = graph->pwgts;
+  rinfo = graph->nrinfo;
+
+
+  i = ComputeMaxNodeGain(nvtxs, xadj, adjncy, vwgt);
+  PQueueInit(ctrl, &parts[0], nvtxs, i);
+  PQueueInit(ctrl, &parts[1], nvtxs, i);
+
+  moved = idxwspacemalloc(ctrl, nvtxs);
+  swaps = idxwspacemalloc(ctrl, nvtxs);
+  mptr = idxwspacemalloc(ctrl, nvtxs+1);
+  mind = idxwspacemalloc(ctrl, nvtxs);
+  perm = idxwspacemalloc(ctrl, nvtxs);
+
+  IFSET(ctrl->dbglvl, DBG_REFINE,
+    printf("Partitions: [%6d %6d] Nv-Nb[%6d %6d]. ISep: %6d\n", pwgts[0], pwgts[1], graph->nvtxs, graph->nbnd, graph->mincut));
+
+  badmaxpwgt = (int)(ubfactor*(pwgts[0]+pwgts[1]+pwgts[2])/2);
+
+  for (pass=0; pass<npasses; pass++) {
+    idxset(nvtxs, -1, moved);
+    PQueueReset(&parts[0]);
+    PQueueReset(&parts[1]);
+
+    mincutorder = -1;
+    initcut = mincut = graph->mincut;
+    nbnd = graph->nbnd;
+
+    RandomPermute(nbnd, perm, 1);
+    for (ii=0; ii<nbnd; ii++) {
+      i = bndind[perm[ii]];
+      ASSERT(where[i] == 2);
+      PQueueInsert(&parts[0], i, vwgt[i]-rinfo[i].edegrees[1]);
+      PQueueInsert(&parts[1], i, vwgt[i]-rinfo[i].edegrees[0]);
+    }
+
+    ASSERT(CheckNodeBnd(graph, nbnd));
+    ASSERT(CheckNodePartitionParams(graph));
+
+    limit = (ctrl->oflags&OFLAG_COMPRESS ? amin(5*nbnd, 400) : amin(2*nbnd, 300));
+
+    /******************************************************
+    * Get into the FM loop
+    *******************************************************/
+    mptr[0] = nmind = 0;
+    mindiff = abs(pwgts[0]-pwgts[1]);
+    to = (pwgts[0] < pwgts[1] ? 0 : 1);
+    for (nswaps=0; nswaps<nvtxs; nswaps++) {
+      u[0] = PQueueSeeMax(&parts[0]);  
+      u[1] = PQueueSeeMax(&parts[1]);
+      if (u[0] != -1 && u[1] != -1) {
+        g[0] = vwgt[u[0]]-rinfo[u[0]].edegrees[1];
+        g[1] = vwgt[u[1]]-rinfo[u[1]].edegrees[0];
+
+        to = (g[0] > g[1] ? 0 : (g[0] < g[1] ? 1 : pass%2)); 
+        /* to = (g[0] > g[1] ? 0 : (g[0] < g[1] ? 1 : (pwgts[0] < pwgts[1] ? 0 : 1))); */
+
+        if (pwgts[to]+vwgt[u[to]] > badmaxpwgt) 
+          to = (to+1)%2;
+      }
+      else if (u[0] == -1 && u[1] == -1) {
+        break;
+      }
+      else if (u[0] != -1 && pwgts[0]+vwgt[u[0]] <= badmaxpwgt) {
+        to = 0;
+      }
+      else if (u[1] != -1 && pwgts[1]+vwgt[u[1]] <= badmaxpwgt) {
+        to = 1;
+      }
+      else
+        break;
+
+      other = (to+1)%2;
+
+      higain = PQueueGetMax(&parts[to]);
+      if (moved[higain] == -1) /* Delete if it was in the separator originally */
+        PQueueDelete(&parts[other], higain, vwgt[higain]-rinfo[higain].edegrees[to]);
+
+      ASSERT(bndptr[higain] != -1);
+
+      pwgts[2] -= (vwgt[higain]-rinfo[higain].edegrees[other]);
+
+      newdiff = abs(pwgts[to]+vwgt[higain] - (pwgts[other]-rinfo[higain].edegrees[other]));
+      if (pwgts[2] < mincut || (pwgts[2] == mincut && newdiff < mindiff)) {
+        mincut = pwgts[2];
+        mincutorder = nswaps;
+        mindiff = newdiff;
+      }
+      else {
+        if (nswaps - mincutorder > limit) {
+          pwgts[2] += (vwgt[higain]-rinfo[higain].edegrees[other]);
+          break; /* No further improvement, break out */
+        }
+      }
+
+      BNDDelete(nbnd, bndind, bndptr, higain);
+      pwgts[to] += vwgt[higain];
+      where[higain] = to;
+      moved[higain] = nswaps;
+      swaps[nswaps] = higain;  
+
+
+      /**********************************************************
+      * Update the degrees of the affected nodes
+      ***********************************************************/
+      for (j=xadj[higain]; j<xadj[higain+1]; j++) {
+        k = adjncy[j];
+        if (where[k] == 2) { /* For the in-separator vertices modify their edegree[to] */
+          oldgain = vwgt[k]-rinfo[k].edegrees[to];
+          rinfo[k].edegrees[to] += vwgt[higain];
+          if (moved[k] == -1 || moved[k] == -(2+other))
+            PQueueUpdate(&parts[other], k, oldgain, oldgain-vwgt[higain]);
+        }
+        else if (where[k] == other) { /* This vertex is pulled into the separator */
+          ASSERTP(bndptr[k] == -1, ("%d %d %d\n", k, bndptr[k], where[k]));
+          BNDInsert(nbnd, bndind, bndptr, k);
+
+          mind[nmind++] = k;  /* Keep track for rollback */
+          where[k] = 2;
+          pwgts[other] -= vwgt[k];
+
+          edegrees = rinfo[k].edegrees;
+          edegrees[0] = edegrees[1] = 0;
+          for (jj=xadj[k]; jj<xadj[k+1]; jj++) {
+            kk = adjncy[jj];
+            if (where[kk] != 2) 
+              edegrees[where[kk]] += vwgt[kk];
+            else {
+              oldgain = vwgt[kk]-rinfo[kk].edegrees[other];
+              rinfo[kk].edegrees[other] -= vwgt[k];
+              if (moved[kk] == -1 || moved[kk] == -(2+to))
+                PQueueUpdate(&parts[to], kk, oldgain, oldgain+vwgt[k]);
+            }
+          }
+
+          /* Insert the new vertex into the priority queue. Only one side! */
+          if (moved[k] == -1) {
+            PQueueInsert(&parts[to], k, vwgt[k]-edegrees[other]);
+            moved[k] = -(2+to);
+          }
+        }
+      }
+      mptr[nswaps+1] = nmind;
+
+      IFSET(ctrl->dbglvl, DBG_MOVEINFO,
+            printf("Moved %6d to %3d, Gain: %5d [%5d] [%4d %4d] \t[%5d %5d %5d]\n", higain, to, g[to], g[other], vwgt[u[to]], vwgt[u[other]], pwgts[0], pwgts[1], pwgts[2]));
+
+    }
+
+
+    /****************************************************************
+    * Roll back computation 
+    *****************************************************************/
+    for (nswaps--; nswaps>mincutorder; nswaps--) {
+      higain = swaps[nswaps];
+
+      ASSERT(CheckNodePartitionParams(graph));
+
+      to = where[higain];
+      other = (to+1)%2;
+      INC_DEC(pwgts[2], pwgts[to], vwgt[higain]);
+      where[higain] = 2;
+      BNDInsert(nbnd, bndind, bndptr, higain);
+
+      edegrees = rinfo[higain].edegrees;
+      edegrees[0] = edegrees[1] = 0;
+      for (j=xadj[higain]; j<xadj[higain+1]; j++) {
+        k = adjncy[j];
+        if (where[k] == 2) 
+          rinfo[k].edegrees[to] -= vwgt[higain];
+        else
+          edegrees[where[k]] += vwgt[k];
+      }
+
+      /* Push nodes out of the separator */
+      for (j=mptr[nswaps]; j<mptr[nswaps+1]; j++) {
+        k = mind[j];
+        ASSERT(where[k] == 2);
+        where[k] = other;
+        INC_DEC(pwgts[other], pwgts[2], vwgt[k]);
+        BNDDelete(nbnd, bndind, bndptr, k);
+        for (jj=xadj[k]; jj<xadj[k+1]; jj++) {
+          kk = adjncy[jj];
+          if (where[kk] == 2) 
+            rinfo[kk].edegrees[other] += vwgt[k];
+        }
+      }
+    }
+
+    ASSERT(mincut == pwgts[2]);
+
+    IFSET(ctrl->dbglvl, DBG_REFINE,
+      printf("\tMinimum sep: %6d at %5d, PWGTS: [%6d %6d], NBND: %6d\n", mincut, mincutorder, pwgts[0], pwgts[1], nbnd));
+
+    graph->mincut = mincut;
+    graph->nbnd = nbnd;
+
+    if (mincutorder == -1 || mincut >= initcut)
+      break;
+  }
+
+  PQueueFree(ctrl, &parts[0]);
+  PQueueFree(ctrl, &parts[1]);
+
+  idxwspacefree(ctrl, nvtxs+1);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+}
+
+
+/*************************************************************************
+* This function performs a node-based FM refinement 
+**************************************************************************/
+void FM_2WayNodeRefine2(CtrlType *ctrl, GraphType *graph, float ubfactor, int npasses)
+{
+  int i, ii, j, k, jj, kk, nvtxs, nbnd, nswaps, nmind;
+  idxtype *xadj, *vwgt, *adjncy, *where, *pwgts, *edegrees, *bndind, *bndptr;
+  idxtype *mptr, *mind, *moved, *swaps, *perm;
+  PQueueType parts[2]; 
+  NRInfoType *rinfo;
+  int higain, oldgain, mincut, initcut, mincutorder;	
+  int pass, to, other, limit;
+  int badmaxpwgt, mindiff, newdiff;
+  int u[2], g[2];
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  vwgt = graph->vwgt;
+
+  bndind = graph->bndind;
+  bndptr = graph->bndptr;
+  where = graph->where;
+  pwgts = graph->pwgts;
+  rinfo = graph->nrinfo;
+
+
+  i = ComputeMaxNodeGain(nvtxs, xadj, adjncy, vwgt);
+  PQueueInit(ctrl, &parts[0], nvtxs, i);
+  PQueueInit(ctrl, &parts[1], nvtxs, i);
+
+  moved = idxwspacemalloc(ctrl, nvtxs);
+  swaps = idxwspacemalloc(ctrl, nvtxs);
+  mptr = idxwspacemalloc(ctrl, nvtxs+1);
+  mind = idxwspacemalloc(ctrl, nvtxs);
+  perm = idxwspacemalloc(ctrl, nvtxs);
+
+  IFSET(ctrl->dbglvl, DBG_REFINE,
+    printf("Partitions: [%6d %6d] Nv-Nb[%6d %6d]. ISep: %6d\n", pwgts[0], pwgts[1], graph->nvtxs, graph->nbnd, graph->mincut));
+
+  badmaxpwgt = (int)(ubfactor*(pwgts[0]+pwgts[1]+pwgts[2])/2);
+
+  for (pass=0; pass<npasses; pass++) {
+    idxset(nvtxs, -1, moved);
+    PQueueReset(&parts[0]);
+    PQueueReset(&parts[1]);
+
+    mincutorder = -1;
+    initcut = mincut = graph->mincut;
+    nbnd = graph->nbnd;
+
+    RandomPermute(nbnd, perm, 1);
+    for (ii=0; ii<nbnd; ii++) {
+      i = bndind[perm[ii]];
+      ASSERT(where[i] == 2);
+      PQueueInsert(&parts[0], i, vwgt[i]-rinfo[i].edegrees[1]);
+      PQueueInsert(&parts[1], i, vwgt[i]-rinfo[i].edegrees[0]);
+    }
+
+    ASSERT(CheckNodeBnd(graph, nbnd));
+    ASSERT(CheckNodePartitionParams(graph));
+
+    limit = (ctrl->oflags&OFLAG_COMPRESS ? amin(5*nbnd, 400) : amin(2*nbnd, 300));
+
+    /******************************************************
+    * Get into the FM loop
+    *******************************************************/
+    mptr[0] = nmind = 0;
+    mindiff = abs(pwgts[0]-pwgts[1]);
+    to = (pwgts[0] < pwgts[1] ? 0 : 1);
+    for (nswaps=0; nswaps<nvtxs; nswaps++) {
+      badmaxpwgt = (int)(ubfactor*(pwgts[0]+pwgts[1]+pwgts[2]/2)/2);
+
+      u[0] = PQueueSeeMax(&parts[0]);  
+      u[1] = PQueueSeeMax(&parts[1]);
+      if (u[0] != -1 && u[1] != -1) {
+        g[0] = vwgt[u[0]]-rinfo[u[0]].edegrees[1];
+        g[1] = vwgt[u[1]]-rinfo[u[1]].edegrees[0];
+
+        to = (g[0] > g[1] ? 0 : (g[0] < g[1] ? 1 : pass%2)); 
+        /* to = (g[0] > g[1] ? 0 : (g[0] < g[1] ? 1 : (pwgts[0] < pwgts[1] ? 0 : 1))); */
+
+        if (pwgts[to]+vwgt[u[to]] > badmaxpwgt) 
+          to = (to+1)%2;
+      }
+      else if (u[0] == -1 && u[1] == -1) {
+        break;
+      }
+      else if (u[0] != -1 && pwgts[0]+vwgt[u[0]] <= badmaxpwgt) {
+        to = 0;
+      }
+      else if (u[1] != -1 && pwgts[1]+vwgt[u[1]] <= badmaxpwgt) {
+        to = 1;
+      }
+      else
+        break;
+
+      other = (to+1)%2;
+
+      higain = PQueueGetMax(&parts[to]);
+      if (moved[higain] == -1) /* Delete if it was in the separator originally */
+        PQueueDelete(&parts[other], higain, vwgt[higain]-rinfo[higain].edegrees[to]);
+
+      ASSERT(bndptr[higain] != -1);
+
+      pwgts[2] -= (vwgt[higain]-rinfo[higain].edegrees[other]);
+
+      newdiff = abs(pwgts[to]+vwgt[higain] - (pwgts[other]-rinfo[higain].edegrees[other]));
+      if (pwgts[2] < mincut || (pwgts[2] == mincut && newdiff < mindiff)) {
+        mincut = pwgts[2];
+        mincutorder = nswaps;
+        mindiff = newdiff;
+      }
+      else {
+        if (nswaps - mincutorder > limit) {
+          pwgts[2] += (vwgt[higain]-rinfo[higain].edegrees[other]);
+          break; /* No further improvement, break out */
+        }
+      }
+
+      BNDDelete(nbnd, bndind, bndptr, higain);
+      pwgts[to] += vwgt[higain];
+      where[higain] = to;
+      moved[higain] = nswaps;
+      swaps[nswaps] = higain;  
+
+
+      /**********************************************************
+      * Update the degrees of the affected nodes
+      ***********************************************************/
+      for (j=xadj[higain]; j<xadj[higain+1]; j++) {
+        k = adjncy[j];
+        if (where[k] == 2) { /* For the in-separator vertices modify their edegree[to] */
+          oldgain = vwgt[k]-rinfo[k].edegrees[to];
+          rinfo[k].edegrees[to] += vwgt[higain];
+          if (moved[k] == -1 || moved[k] == -(2+other))
+            PQueueUpdate(&parts[other], k, oldgain, oldgain-vwgt[higain]);
+        }
+        else if (where[k] == other) { /* This vertex is pulled into the separator */
+          ASSERTP(bndptr[k] == -1, ("%d %d %d\n", k, bndptr[k], where[k]));
+          BNDInsert(nbnd, bndind, bndptr, k);
+
+          mind[nmind++] = k;  /* Keep track for rollback */
+          where[k] = 2;
+          pwgts[other] -= vwgt[k];
+
+          edegrees = rinfo[k].edegrees;
+          edegrees[0] = edegrees[1] = 0;
+          for (jj=xadj[k]; jj<xadj[k+1]; jj++) {
+            kk = adjncy[jj];
+            if (where[kk] != 2) 
+              edegrees[where[kk]] += vwgt[kk];
+            else {
+              oldgain = vwgt[kk]-rinfo[kk].edegrees[other];
+              rinfo[kk].edegrees[other] -= vwgt[k];
+              if (moved[kk] == -1 || moved[kk] == -(2+to))
+                PQueueUpdate(&parts[to], kk, oldgain, oldgain+vwgt[k]);
+            }
+          }
+
+          /* Insert the new vertex into the priority queue. Only one side! */
+          if (moved[k] == -1) {
+            PQueueInsert(&parts[to], k, vwgt[k]-edegrees[other]);
+            moved[k] = -(2+to);
+          }
+        }
+      }
+      mptr[nswaps+1] = nmind;
+
+      IFSET(ctrl->dbglvl, DBG_MOVEINFO,
+            printf("Moved %6d to %3d, Gain: %5d [%5d] [%4d %4d] \t[%5d %5d %5d]\n", higain, to, g[to], g[other], vwgt[u[to]], vwgt[u[other]], pwgts[0], pwgts[1], pwgts[2]));
+
+    }
+
+
+    /****************************************************************
+    * Roll back computation 
+    *****************************************************************/
+    for (nswaps--; nswaps>mincutorder; nswaps--) {
+      higain = swaps[nswaps];
+
+      ASSERT(CheckNodePartitionParams(graph));
+
+      to = where[higain];
+      other = (to+1)%2;
+      INC_DEC(pwgts[2], pwgts[to], vwgt[higain]);
+      where[higain] = 2;
+      BNDInsert(nbnd, bndind, bndptr, higain);
+
+      edegrees = rinfo[higain].edegrees;
+      edegrees[0] = edegrees[1] = 0;
+      for (j=xadj[higain]; j<xadj[higain+1]; j++) {
+        k = adjncy[j];
+        if (where[k] == 2) 
+          rinfo[k].edegrees[to] -= vwgt[higain];
+        else
+          edegrees[where[k]] += vwgt[k];
+      }
+
+      /* Push nodes out of the separator */
+      for (j=mptr[nswaps]; j<mptr[nswaps+1]; j++) {
+        k = mind[j];
+        ASSERT(where[k] == 2);
+        where[k] = other;
+        INC_DEC(pwgts[other], pwgts[2], vwgt[k]);
+        BNDDelete(nbnd, bndind, bndptr, k);
+        for (jj=xadj[k]; jj<xadj[k+1]; jj++) {
+          kk = adjncy[jj];
+          if (where[kk] == 2) 
+            rinfo[kk].edegrees[other] += vwgt[k];
+        }
+      }
+    }
+
+    ASSERT(mincut == pwgts[2]);
+
+    IFSET(ctrl->dbglvl, DBG_REFINE,
+      printf("\tMinimum sep: %6d at %5d, PWGTS: [%6d %6d], NBND: %6d\n", mincut, mincutorder, pwgts[0], pwgts[1], nbnd));
+
+    graph->mincut = mincut;
+    graph->nbnd = nbnd;
+
+    if (mincutorder == -1 || mincut >= initcut)
+      break;
+  }
+
+  PQueueFree(ctrl, &parts[0]);
+  PQueueFree(ctrl, &parts[1]);
+
+  idxwspacefree(ctrl, nvtxs+1);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+}
+
+
+/*************************************************************************
+* This function performs a node-based FM refinement 
+**************************************************************************/
+void FM_2WayNodeRefineEqWgt(CtrlType *ctrl, GraphType *graph, int npasses)
+{
+  int i, ii, j, k, jj, kk, nvtxs, nbnd, nswaps, nmind;
+  idxtype *xadj, *vwgt, *adjncy, *where, *pwgts, *edegrees, *bndind, *bndptr;
+  idxtype *mptr, *mind, *moved, *swaps, *perm;
+  PQueueType parts[2]; 
+  NRInfoType *rinfo;
+  int higain, oldgain, mincut, initcut, mincutorder;	
+  int pass, to, other, limit;
+  int mindiff, newdiff;
+  int u[2], g[2];
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  vwgt = graph->vwgt;
+
+  bndind = graph->bndind;
+  bndptr = graph->bndptr;
+  where = graph->where;
+  pwgts = graph->pwgts;
+  rinfo = graph->nrinfo;
+
+
+  i = ComputeMaxNodeGain(nvtxs, xadj, adjncy, vwgt);
+  PQueueInit(ctrl, &parts[0], nvtxs, i);
+  PQueueInit(ctrl, &parts[1], nvtxs, i);
+
+  moved = idxwspacemalloc(ctrl, nvtxs);
+  swaps = idxwspacemalloc(ctrl, nvtxs);
+  mptr = idxwspacemalloc(ctrl, nvtxs+1);
+  mind = idxwspacemalloc(ctrl, nvtxs);
+  perm = idxwspacemalloc(ctrl, nvtxs);
+
+  IFSET(ctrl->dbglvl, DBG_REFINE,
+    printf("Partitions: [%6d %6d] Nv-Nb[%6d %6d]. ISep: %6d\n", pwgts[0], pwgts[1], graph->nvtxs, graph->nbnd, graph->mincut));
+
+  for (pass=0; pass<npasses; pass++) {
+    idxset(nvtxs, -1, moved);
+    PQueueReset(&parts[0]);
+    PQueueReset(&parts[1]);
+
+    mincutorder = -1;
+    initcut = mincut = graph->mincut;
+    nbnd = graph->nbnd;
+
+    RandomPermute(nbnd, perm, 1);
+    for (ii=0; ii<nbnd; ii++) {
+      i = bndind[perm[ii]];
+      ASSERT(where[i] == 2);
+      PQueueInsert(&parts[0], i, vwgt[i]-rinfo[i].edegrees[1]);
+      PQueueInsert(&parts[1], i, vwgt[i]-rinfo[i].edegrees[0]);
+    }
+
+    ASSERT(CheckNodeBnd(graph, nbnd));
+    ASSERT(CheckNodePartitionParams(graph));
+
+    limit = (ctrl->oflags&OFLAG_COMPRESS ? amin(5*nbnd, 400) : amin(2*nbnd, 300));
+
+    /******************************************************
+    * Get into the FM loop
+    *******************************************************/
+    mptr[0] = nmind = 0;
+    mindiff = abs(pwgts[0]-pwgts[1]);
+    to = (pwgts[0] < pwgts[1] ? 0 : 1);
+    for (nswaps=0; nswaps<nvtxs; nswaps++) {
+      to = (pwgts[0] < pwgts[1] ? 0 : 1);
+
+      if (pwgts[0] == pwgts[1]) {
+        u[0] = PQueueSeeMax(&parts[0]);  
+        u[1] = PQueueSeeMax(&parts[1]);
+        if (u[0] != -1 && u[1] != -1) {
+          g[0] = vwgt[u[0]]-rinfo[u[0]].edegrees[1];
+          g[1] = vwgt[u[1]]-rinfo[u[1]].edegrees[0];
+
+          to = (g[0] > g[1] ? 0 : (g[0] < g[1] ? 1 : pass%2)); 
+        }
+      }
+      other = (to+1)%2;
+
+      if ((higain = PQueueGetMax(&parts[to])) == -1)
+        break;
+
+      if (moved[higain] == -1) /* Delete if it was in the separator originally */
+        PQueueDelete(&parts[other], higain, vwgt[higain]-rinfo[higain].edegrees[to]);
+
+      ASSERT(bndptr[higain] != -1);
+
+      pwgts[2] -= (vwgt[higain]-rinfo[higain].edegrees[other]);
+
+      newdiff = abs(pwgts[to]+vwgt[higain] - (pwgts[other]-rinfo[higain].edegrees[other]));
+      if (pwgts[2] < mincut || (pwgts[2] == mincut && newdiff < mindiff)) {
+        mincut = pwgts[2];
+        mincutorder = nswaps;
+        mindiff = newdiff;
+      }
+      else {
+        if (nswaps - mincutorder > limit) {
+          pwgts[2] += (vwgt[higain]-rinfo[higain].edegrees[other]);
+          break; /* No further improvement, break out */
+        }
+      }
+
+      BNDDelete(nbnd, bndind, bndptr, higain);
+      pwgts[to] += vwgt[higain];
+      where[higain] = to;
+      moved[higain] = nswaps;
+      swaps[nswaps] = higain;  
+
+
+      /**********************************************************
+      * Update the degrees of the affected nodes
+      ***********************************************************/
+      for (j=xadj[higain]; j<xadj[higain+1]; j++) {
+        k = adjncy[j];
+        if (where[k] == 2) { /* For the in-separator vertices modify their edegree[to] */
+          oldgain = vwgt[k]-rinfo[k].edegrees[to];
+          rinfo[k].edegrees[to] += vwgt[higain];
+          if (moved[k] == -1 || moved[k] == -(2+other))
+            PQueueUpdate(&parts[other], k, oldgain, oldgain-vwgt[higain]);
+        }
+        else if (where[k] == other) { /* This vertex is pulled into the separator */
+          ASSERTP(bndptr[k] == -1, ("%d %d %d\n", k, bndptr[k], where[k]));
+          BNDInsert(nbnd, bndind, bndptr, k);
+
+          mind[nmind++] = k;  /* Keep track for rollback */
+          where[k] = 2;
+          pwgts[other] -= vwgt[k];
+
+          edegrees = rinfo[k].edegrees;
+          edegrees[0] = edegrees[1] = 0;
+          for (jj=xadj[k]; jj<xadj[k+1]; jj++) {
+            kk = adjncy[jj];
+            if (where[kk] != 2) 
+              edegrees[where[kk]] += vwgt[kk];
+            else {
+              oldgain = vwgt[kk]-rinfo[kk].edegrees[other];
+              rinfo[kk].edegrees[other] -= vwgt[k];
+              if (moved[kk] == -1 || moved[kk] == -(2+to))
+                PQueueUpdate(&parts[to], kk, oldgain, oldgain+vwgt[k]);
+            }
+          }
+
+          /* Insert the new vertex into the priority queue. Only one side! */
+          if (moved[k] == -1) {
+            PQueueInsert(&parts[to], k, vwgt[k]-edegrees[other]);
+            moved[k] = -(2+to);
+          }
+        }
+      }
+      mptr[nswaps+1] = nmind;
+
+      IFSET(ctrl->dbglvl, DBG_MOVEINFO,
+            printf("Moved %6d to %3d, Gain: %5d [%5d] [%4d %4d] \t[%5d %5d %5d]\n", higain, to, g[to], g[other], vwgt[u[to]], vwgt[u[other]], pwgts[0], pwgts[1], pwgts[2]));
+
+    }
+
+
+    /****************************************************************
+    * Roll back computation 
+    *****************************************************************/
+    for (nswaps--; nswaps>mincutorder; nswaps--) {
+      higain = swaps[nswaps];
+
+      ASSERT(CheckNodePartitionParams(graph));
+
+      to = where[higain];
+      other = (to+1)%2;
+      INC_DEC(pwgts[2], pwgts[to], vwgt[higain]);
+      where[higain] = 2;
+      BNDInsert(nbnd, bndind, bndptr, higain);
+
+      edegrees = rinfo[higain].edegrees;
+      edegrees[0] = edegrees[1] = 0;
+      for (j=xadj[higain]; j<xadj[higain+1]; j++) {
+        k = adjncy[j];
+        if (where[k] == 2) 
+          rinfo[k].edegrees[to] -= vwgt[higain];
+        else
+          edegrees[where[k]] += vwgt[k];
+      }
+
+      /* Push nodes out of the separator */
+      for (j=mptr[nswaps]; j<mptr[nswaps+1]; j++) {
+        k = mind[j];
+        ASSERT(where[k] == 2);
+        where[k] = other;
+        INC_DEC(pwgts[other], pwgts[2], vwgt[k]);
+        BNDDelete(nbnd, bndind, bndptr, k);
+        for (jj=xadj[k]; jj<xadj[k+1]; jj++) {
+          kk = adjncy[jj];
+          if (where[kk] == 2) 
+            rinfo[kk].edegrees[other] += vwgt[k];
+        }
+      }
+    }
+
+    ASSERT(mincut == pwgts[2]);
+
+    IFSET(ctrl->dbglvl, DBG_REFINE,
+      printf("\tMinimum sep: %6d at %5d, PWGTS: [%6d %6d], NBND: %6d\n", mincut, mincutorder, pwgts[0], pwgts[1], nbnd));
+
+    graph->mincut = mincut;
+    graph->nbnd = nbnd;
+
+    if (mincutorder == -1 || mincut >= initcut)
+      break;
+  }
+
+  PQueueFree(ctrl, &parts[0]);
+  PQueueFree(ctrl, &parts[1]);
+
+  idxwspacefree(ctrl, nvtxs+1);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+}
+
+
+/*************************************************************************
+* This function performs a node-based FM refinement. This is the 
+* one-way version 
+**************************************************************************/
+void FM_2WayNodeRefine_OneSided(CtrlType *ctrl, GraphType *graph, float ubfactor, int npasses)
+{
+  int i, ii, j, k, jj, kk, nvtxs, nbnd, nswaps, nmind;
+  idxtype *xadj, *vwgt, *adjncy, *where, *pwgts, *edegrees, *bndind, *bndptr;
+  idxtype *mptr, *mind, *swaps, *perm;
+  PQueueType parts; 
+  NRInfoType *rinfo;
+  int higain, oldgain, mincut, initcut, mincutorder;	
+  int pass, to, other, limit;
+  int badmaxpwgt, mindiff, newdiff;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  vwgt = graph->vwgt;
+
+  bndind = graph->bndind;
+  bndptr = graph->bndptr;
+  where = graph->where;
+  pwgts = graph->pwgts;
+  rinfo = graph->nrinfo;
+
+  PQueueInit(ctrl, &parts, nvtxs, ComputeMaxNodeGain(nvtxs, xadj, adjncy, vwgt));
+
+  perm = idxwspacemalloc(ctrl, nvtxs);
+  swaps = idxwspacemalloc(ctrl, nvtxs);
+  mptr = idxwspacemalloc(ctrl, nvtxs);
+  mind = idxwspacemalloc(ctrl, nvtxs+1);
+
+  IFSET(ctrl->dbglvl, DBG_REFINE,
+    printf("Partitions-N1: [%6d %6d] Nv-Nb[%6d %6d]. ISep: %6d\n", pwgts[0], pwgts[1], graph->nvtxs, graph->nbnd, graph->mincut));
+
+  badmaxpwgt = (int)(ubfactor*(pwgts[0]+pwgts[1]+pwgts[2])/2);
+
+  to = (pwgts[0] < pwgts[1] ? 1 : 0);
+  for (pass=0; pass<npasses; pass++) {
+    other = to; 
+    to = (to+1)%2;
+
+    PQueueReset(&parts);
+
+    mincutorder = -1;
+    initcut = mincut = graph->mincut;
+    nbnd = graph->nbnd;
+
+    RandomPermute(nbnd, perm, 1);
+    for (ii=0; ii<nbnd; ii++) {
+      i = bndind[perm[ii]];
+      ASSERT(where[i] == 2);
+      PQueueInsert(&parts, i, vwgt[i]-rinfo[i].edegrees[other]);
+    }
+
+    ASSERT(CheckNodeBnd(graph, nbnd));
+    ASSERT(CheckNodePartitionParams(graph));
+
+    limit = (ctrl->oflags&OFLAG_COMPRESS ? amin(5*nbnd, 400) : amin(2*nbnd, 300));
+
+    /******************************************************
+    * Get into the FM loop
+    *******************************************************/
+    mptr[0] = nmind = 0;
+    mindiff = abs(pwgts[0]-pwgts[1]);
+    for (nswaps=0; nswaps<nvtxs; nswaps++) {
+
+      if ((higain = PQueueGetMax(&parts)) == -1)
+        break;
+
+      ASSERT(bndptr[higain] != -1);
+
+      if (pwgts[to]+vwgt[higain] > badmaxpwgt)
+        break;  /* No point going any further. Balance will be bad */
+
+      pwgts[2] -= (vwgt[higain]-rinfo[higain].edegrees[other]);
+
+      newdiff = abs(pwgts[to]+vwgt[higain] - (pwgts[other]-rinfo[higain].edegrees[other]));
+      if (pwgts[2] < mincut || (pwgts[2] == mincut && newdiff < mindiff)) {
+        mincut = pwgts[2];
+        mincutorder = nswaps;
+        mindiff = newdiff;
+      }
+      else {
+        if (nswaps - mincutorder > limit) {
+          pwgts[2] += (vwgt[higain]-rinfo[higain].edegrees[other]);
+          break; /* No further improvement, break out */
+        }
+      }
+
+      BNDDelete(nbnd, bndind, bndptr, higain);
+      pwgts[to] += vwgt[higain];
+      where[higain] = to;
+      swaps[nswaps] = higain;  
+
+
+      /**********************************************************
+      * Update the degrees of the affected nodes
+      ***********************************************************/
+      for (j=xadj[higain]; j<xadj[higain+1]; j++) {
+        k = adjncy[j];
+        if (where[k] == 2) { /* For the in-separator vertices modify their edegree[to] */
+          rinfo[k].edegrees[to] += vwgt[higain];
+        }
+        else if (where[k] == other) { /* This vertex is pulled into the separator */
+          ASSERTP(bndptr[k] == -1, ("%d %d %d\n", k, bndptr[k], where[k]));
+          BNDInsert(nbnd, bndind, bndptr, k);
+
+          mind[nmind++] = k;  /* Keep track for rollback */
+          where[k] = 2;
+          pwgts[other] -= vwgt[k];
+
+          edegrees = rinfo[k].edegrees;
+          edegrees[0] = edegrees[1] = 0;
+          for (jj=xadj[k]; jj<xadj[k+1]; jj++) {
+            kk = adjncy[jj];
+            if (where[kk] != 2) 
+              edegrees[where[kk]] += vwgt[kk];
+            else {
+              oldgain = vwgt[kk]-rinfo[kk].edegrees[other];
+              rinfo[kk].edegrees[other] -= vwgt[k];
+
+              /* Since the moves are one-sided this vertex has not been moved yet */
+              PQueueUpdateUp(&parts, kk, oldgain, oldgain+vwgt[k]); 
+            }
+          }
+
+          /* Insert the new vertex into the priority queue. Safe due to one-sided moves */
+          PQueueInsert(&parts, k, vwgt[k]-edegrees[other]);
+        }
+      }
+      mptr[nswaps+1] = nmind;
+
+
+      IFSET(ctrl->dbglvl, DBG_MOVEINFO,
+            printf("Moved %6d to %3d, Gain: %5d [%5d] \t[%5d %5d %5d] [%3d %2d]\n", 
+                       higain, to, (vwgt[higain]-rinfo[higain].edegrees[other]), vwgt[higain], pwgts[0], pwgts[1], pwgts[2], nswaps, limit));
+
+    }
+
+
+    /****************************************************************
+    * Roll back computation 
+    *****************************************************************/
+    for (nswaps--; nswaps>mincutorder; nswaps--) {
+      higain = swaps[nswaps];
+
+      ASSERT(CheckNodePartitionParams(graph));
+      ASSERT(where[higain] == to);
+
+      INC_DEC(pwgts[2], pwgts[to], vwgt[higain]);
+      where[higain] = 2;
+      BNDInsert(nbnd, bndind, bndptr, higain);
+
+      edegrees = rinfo[higain].edegrees;
+      edegrees[0] = edegrees[1] = 0;
+      for (j=xadj[higain]; j<xadj[higain+1]; j++) {
+        k = adjncy[j];
+        if (where[k] == 2) 
+          rinfo[k].edegrees[to] -= vwgt[higain];
+        else
+          edegrees[where[k]] += vwgt[k];
+      }
+
+      /* Push nodes out of the separator */
+      for (j=mptr[nswaps]; j<mptr[nswaps+1]; j++) {
+        k = mind[j];
+        ASSERT(where[k] == 2);
+        where[k] = other;
+        INC_DEC(pwgts[other], pwgts[2], vwgt[k]);
+        BNDDelete(nbnd, bndind, bndptr, k);
+        for (jj=xadj[k]; jj<xadj[k+1]; jj++) {
+          kk = adjncy[jj];
+          if (where[kk] == 2) 
+            rinfo[kk].edegrees[other] += vwgt[k];
+        }
+      }
+    }
+
+    ASSERT(mincut == pwgts[2]);
+
+    IFSET(ctrl->dbglvl, DBG_REFINE,
+      printf("\tMinimum sep: %6d at %5d, PWGTS: [%6d %6d], NBND: %6d\n", mincut, mincutorder, pwgts[0], pwgts[1], nbnd));
+
+    graph->mincut = mincut;
+    graph->nbnd = nbnd;
+
+    if (pass%2 == 1 && (mincutorder == -1 || mincut >= initcut))
+      break;
+  }
+
+  PQueueFree(ctrl, &parts);
+
+  idxwspacefree(ctrl, nvtxs+1);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+}
+
+
+
+/*************************************************************************
+* This function performs a node-based FM refinement 
+**************************************************************************/
+void FM_2WayNodeBalance(CtrlType *ctrl, GraphType *graph, float ubfactor)
+{
+  int i, ii, j, k, jj, kk, nvtxs, nbnd, nswaps;
+  idxtype *xadj, *vwgt, *adjncy, *where, *pwgts, *edegrees, *bndind, *bndptr;
+  idxtype *perm, *moved;
+  PQueueType parts; 
+  NRInfoType *rinfo;
+  int higain, oldgain;	
+  int pass, to, other;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  vwgt = graph->vwgt;
+
+  bndind = graph->bndind;
+  bndptr = graph->bndptr;
+  where = graph->where;
+  pwgts = graph->pwgts;
+  rinfo = graph->nrinfo;
+
+  if (abs(pwgts[0]-pwgts[1]) < (int)((ubfactor-1.0)*(pwgts[0]+pwgts[1])))
+    return;
+  if (abs(pwgts[0]-pwgts[1]) < 3*idxsum(nvtxs, vwgt)/nvtxs)
+    return;
+
+  to = (pwgts[0] < pwgts[1] ? 0 : 1); 
+  other = (to+1)%2;
+
+  PQueueInit(ctrl, &parts, nvtxs, ComputeMaxNodeGain(nvtxs, xadj, adjncy, vwgt));
+
+  perm = idxwspacemalloc(ctrl, nvtxs);
+  moved = idxset(nvtxs, -1, idxwspacemalloc(ctrl, nvtxs));
+
+  IFSET(ctrl->dbglvl, DBG_REFINE,
+    printf("Partitions: [%6d %6d] Nv-Nb[%6d %6d]. ISep: %6d [B]\n", pwgts[0], pwgts[1], graph->nvtxs, graph->nbnd, graph->mincut));
+
+  nbnd = graph->nbnd;
+  RandomPermute(nbnd, perm, 1);
+  for (ii=0; ii<nbnd; ii++) {
+    i = bndind[perm[ii]];
+    ASSERT(where[i] == 2);
+    PQueueInsert(&parts, i, vwgt[i]-rinfo[i].edegrees[other]);
+  }
+
+  ASSERT(CheckNodeBnd(graph, nbnd));
+  ASSERT(CheckNodePartitionParams(graph));
+
+  /******************************************************
+  * Get into the FM loop
+  *******************************************************/
+  for (nswaps=0; nswaps<nvtxs; nswaps++) {
+    if ((higain = PQueueGetMax(&parts)) == -1)
+      break;
+
+    moved[higain] = 1;
+
+    if (pwgts[other] - rinfo[higain].edegrees[other] < (pwgts[0]+pwgts[1])/2) 
+      continue;
+#ifdef XXX
+    if (pwgts[other] - rinfo[higain].edegrees[other] < pwgts[to]+vwgt[higain]) 
+      break;
+#endif
+
+    ASSERT(bndptr[higain] != -1);
+
+    pwgts[2] -= (vwgt[higain]-rinfo[higain].edegrees[other]);
+
+    BNDDelete(nbnd, bndind, bndptr, higain);
+    pwgts[to] += vwgt[higain];
+    where[higain] = to;
+
+    IFSET(ctrl->dbglvl, DBG_MOVEINFO,
+          printf("Moved %6d to %3d, Gain: %3d, \t[%5d %5d %5d]\n", higain, to, vwgt[higain]-rinfo[higain].edegrees[other], pwgts[0], pwgts[1], pwgts[2]));
+
+
+    /**********************************************************
+    * Update the degrees of the affected nodes
+    ***********************************************************/
+    for (j=xadj[higain]; j<xadj[higain+1]; j++) {
+      k = adjncy[j];
+      if (where[k] == 2) { /* For the in-separator vertices modify their edegree[to] */
+        rinfo[k].edegrees[to] += vwgt[higain];
+      }
+      else if (where[k] == other) { /* This vertex is pulled into the separator */
+        ASSERTP(bndptr[k] == -1, ("%d %d %d\n", k, bndptr[k], where[k]));
+        BNDInsert(nbnd, bndind, bndptr, k);
+
+        where[k] = 2;
+        pwgts[other] -= vwgt[k];
+
+        edegrees = rinfo[k].edegrees;
+        edegrees[0] = edegrees[1] = 0;
+        for (jj=xadj[k]; jj<xadj[k+1]; jj++) {
+          kk = adjncy[jj];
+          if (where[kk] != 2) 
+            edegrees[where[kk]] += vwgt[kk];
+          else {
+            ASSERT(bndptr[kk] != -1);
+            oldgain = vwgt[kk]-rinfo[kk].edegrees[other];
+            rinfo[kk].edegrees[other] -= vwgt[k];
+
+            if (moved[kk] == -1)
+              PQueueUpdateUp(&parts, kk, oldgain, oldgain+vwgt[k]);
+          }
+        }
+
+        /* Insert the new vertex into the priority queue */
+        PQueueInsert(&parts, k, vwgt[k]-edegrees[other]);
+      }
+    }
+
+    if (pwgts[to] > pwgts[other])
+      break;
+  }
+
+  IFSET(ctrl->dbglvl, DBG_REFINE,
+    printf("\tBalanced sep: %6d at %4d, PWGTS: [%6d %6d], NBND: %6d\n", pwgts[2], nswaps, pwgts[0], pwgts[1], nbnd));
+
+  graph->mincut = pwgts[2];
+  graph->nbnd = nbnd;
+
+
+  PQueueFree(ctrl, &parts);
+
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+}
+
+
+/*************************************************************************
+* This function computes the maximum possible gain for a vertex
+**************************************************************************/
+int ComputeMaxNodeGain(int nvtxs, idxtype *xadj, idxtype *adjncy, idxtype *vwgt)
+{
+  int i, j, k, max;
+
+  max = 0;
+  for (j=xadj[0]; j<xadj[1]; j++)
+    max += vwgt[adjncy[j]];
+
+  for (i=1; i<nvtxs; i++) {
+    for (k=0, j=xadj[i]; j<xadj[i+1]; j++)
+      k += vwgt[adjncy[j]];
+    if (max < k)
+      max = k;
+  }
+
+  return max;
+}
+   
+
diff --git a/contrib/Metis/srefine.c b/contrib/Metis/srefine.c
new file mode 100644
index 0000000000..f0d4584181
--- /dev/null
+++ b/contrib/Metis/srefine.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * srefine.c
+ *
+ * This file contains code for the separator refinement algortihms
+ *
+ * Started 8/1/97
+ * George
+ *
+ * $Id: srefine.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function is the entry point of the separator refinement
+**************************************************************************/
+void Refine2WayNode(CtrlType *ctrl, GraphType *orggraph, GraphType *graph, float ubfactor)
+{
+
+  IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->UncoarsenTmr));
+
+  for (;;) {
+    IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->RefTmr));
+    if (ctrl->RType != 15)
+      FM_2WayNodeBalance(ctrl, graph, ubfactor); 
+
+    switch (ctrl->RType) {
+      case 1:
+        FM_2WayNodeRefine(ctrl, graph, ubfactor, 8); 
+        break;
+      case 2:
+        FM_2WayNodeRefine_OneSided(ctrl, graph, ubfactor, 8); 
+        break;
+      case 3:
+        FM_2WayNodeRefine(ctrl, graph, ubfactor, 8); 
+        FM_2WayNodeRefine_OneSided(ctrl, graph, ubfactor, 8); 
+        break;
+      case 4:
+        FM_2WayNodeRefine_OneSided(ctrl, graph, ubfactor, 8); 
+        FM_2WayNodeRefine(ctrl, graph, ubfactor, 8); 
+        break;
+      case 5:
+        FM_2WayNodeRefineEqWgt(ctrl, graph, 8); 
+        break;
+    }
+    IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->RefTmr));
+
+    if (graph == orggraph) 
+      break;
+
+    graph = graph->finer;
+    IFSET(ctrl->dbglvl, DBG_TIME, starttimer(ctrl->ProjectTmr));
+    Project2WayNodePartition(ctrl, graph);
+    IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->ProjectTmr));
+  }
+
+  IFSET(ctrl->dbglvl, DBG_TIME, stoptimer(ctrl->UncoarsenTmr));
+}
+
+
+/*************************************************************************
+* This function allocates memory for 2-way edge refinement
+**************************************************************************/
+void Allocate2WayNodePartitionMemory(CtrlType *ctrl, GraphType *graph)
+{
+  int nvtxs, pad64;
+
+  nvtxs = graph->nvtxs;
+
+  pad64 = (3*nvtxs+3)%2;
+
+  graph->rdata = idxmalloc(3*nvtxs+3+(sizeof(NRInfoType)/sizeof(idxtype))*nvtxs+pad64, "Allocate2WayPartitionMemory: rdata");
+  graph->pwgts          = graph->rdata;
+  graph->where          = graph->rdata + 3;
+  graph->bndptr         = graph->rdata + nvtxs + 3;
+  graph->bndind         = graph->rdata + 2*nvtxs + 3;
+  graph->nrinfo         = (NRInfoType *)(graph->rdata + 3*nvtxs + 3 + pad64);
+}
+
+
+
+/*************************************************************************
+* This function computes the initial id/ed 
+**************************************************************************/
+void Compute2WayNodePartitionParams(CtrlType *ctrl, GraphType *graph)
+{
+  int i, j, k, l, nvtxs, nbnd;
+  idxtype *xadj, *adjncy, *adjwgt, *vwgt;
+  idxtype *where, *pwgts, *bndind, *bndptr, *edegrees;
+  NRInfoType *rinfo;
+  int me, other;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  vwgt = graph->vwgt;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  where = graph->where;
+  rinfo = graph->nrinfo;
+  pwgts = idxset(3, 0, graph->pwgts);
+  bndind = graph->bndind;
+  bndptr = idxset(nvtxs, -1, graph->bndptr);
+
+
+  /*------------------------------------------------------------
+  / Compute now the separator external degrees
+  /------------------------------------------------------------*/
+  nbnd = 0;
+  for (i=0; i<nvtxs; i++) {
+    me = where[i];
+    pwgts[me] += vwgt[i];
+
+    ASSERT(me >=0 && me <= 2);
+
+    if (me == 2) { /* If it is on the separator do some computations */
+      BNDInsert(nbnd, bndind, bndptr, i);
+
+      edegrees = rinfo[i].edegrees;
+      edegrees[0] = edegrees[1] = 0;
+
+      for (j=xadj[i]; j<xadj[i+1]; j++) {
+        other = where[adjncy[j]];
+        if (other != 2)
+          edegrees[other] += vwgt[adjncy[j]];
+      }
+    }
+  }
+
+  ASSERT(CheckNodeBnd(graph, nbnd));
+
+  graph->mincut = pwgts[2];
+  graph->nbnd = nbnd;
+}
+
+
+/*************************************************************************
+* This function computes the initial id/ed 
+**************************************************************************/
+void Project2WayNodePartition(CtrlType *ctrl, GraphType *graph)
+{
+  int i, j, nvtxs;
+  idxtype *cmap, *where, *cwhere;
+  GraphType *cgraph;
+
+  cgraph = graph->coarser;
+  cwhere = cgraph->where;
+
+  nvtxs = graph->nvtxs;
+  cmap = graph->cmap;
+
+  Allocate2WayNodePartitionMemory(ctrl, graph);
+  where = graph->where;
+  
+  /* Project the partition */
+  for (i=0; i<nvtxs; i++) {
+    where[i] = cwhere[cmap[i]];
+    ASSERTP(where[i] >= 0 && where[i] <= 2, ("%d %d %d %d\n", i, cmap[i], where[i], cwhere[cmap[i]]));
+  }
+
+  FreeGraph(graph->coarser);
+  graph->coarser = NULL;
+
+  Compute2WayNodePartitionParams(ctrl, graph);
+}
diff --git a/contrib/Metis/stat.c b/contrib/Metis/stat.c
new file mode 100644
index 0000000000..fdce921b4a
--- /dev/null
+++ b/contrib/Metis/stat.c
@@ -0,0 +1,287 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * stat.c
+ *
+ * This file computes various statistics
+ *
+ * Started 7/25/97
+ * George
+ *
+ * $Id: stat.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function computes cuts and balance information
+**************************************************************************/
+void ComputePartitionInfo(GraphType *graph, int nparts, idxtype *where)
+{
+  int i, j, k, nvtxs, ncon, mustfree=0;
+  idxtype *xadj, *adjncy, *vwgt, *adjwgt, *kpwgts, *tmpptr;
+  idxtype *padjncy, *padjwgt, *padjcut;
+
+  nvtxs = graph->nvtxs;
+  ncon = graph->ncon;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  vwgt = graph->vwgt;
+  adjwgt = graph->adjwgt;
+
+  if (vwgt == NULL) {
+    vwgt = graph->vwgt = idxsmalloc(nvtxs, 1, "vwgt");
+    mustfree = 1;
+  }
+  if (adjwgt == NULL) {
+    adjwgt = graph->adjwgt = idxsmalloc(xadj[nvtxs], 1, "adjwgt");
+    mustfree += 2;
+  }
+
+  printf("%d-way Cut: %5d, Vol: %5d, ", nparts, ComputeCut(graph, where), ComputeVolume(graph, where));
+
+  /* Compute balance information */
+  kpwgts = idxsmalloc(ncon*nparts, 0, "ComputePartitionInfo: kpwgts");
+
+  for (i=0; i<nvtxs; i++) {
+    for (j=0; j<ncon; j++) 
+      kpwgts[where[i]*ncon+j] += vwgt[i*ncon+j];
+  }
+
+  if (ncon == 1) {
+    printf("\tBalance: %5.3f out of %5.3f\n", 
+            1.0*nparts*kpwgts[idxamax(nparts, kpwgts)]/(1.0*idxsum(nparts, kpwgts)),
+            1.0*nparts*vwgt[idxamax(nvtxs, vwgt)]/(1.0*idxsum(nparts, kpwgts)));
+  }
+  else {
+    printf("\tBalance:");
+    for (j=0; j<ncon; j++) 
+      printf(" (%5.3f out of %5.3f)", 
+            1.0*nparts*kpwgts[ncon*idxamax_strd(nparts, kpwgts+j, ncon)+j]/(1.0*idxsum_strd(nparts, kpwgts+j, ncon)),
+            1.0*nparts*vwgt[ncon*idxamax_strd(nvtxs, vwgt+j, ncon)+j]/(1.0*idxsum_strd(nparts, kpwgts+j, ncon)));
+    printf("\n");
+  }
+
+
+  /* Compute p-adjncy information */
+  padjncy = idxsmalloc(nparts*nparts, 0, "ComputePartitionInfo: padjncy");
+  padjwgt = idxsmalloc(nparts*nparts, 0, "ComputePartitionInfo: padjwgt");
+  padjcut = idxsmalloc(nparts*nparts, 0, "ComputePartitionInfo: padjwgt");
+
+  idxset(nparts, 0, kpwgts);
+  for (i=0; i<nvtxs; i++) {
+    for (j=xadj[i]; j<xadj[i+1]; j++) {
+      if (where[i] != where[adjncy[j]]) {
+        padjncy[where[i]*nparts+where[adjncy[j]]] = 1;
+        padjcut[where[i]*nparts+where[adjncy[j]]] += adjwgt[j];
+        if (kpwgts[where[adjncy[j]]] == 0) {
+          padjwgt[where[i]*nparts+where[adjncy[j]]]++;
+          kpwgts[where[adjncy[j]]] = 1;
+        }
+      }
+    }
+    for (j=xadj[i]; j<xadj[i+1]; j++) 
+      kpwgts[where[adjncy[j]]] = 0;
+  }
+
+  for (i=0; i<nparts; i++)
+    kpwgts[i] = idxsum(nparts, padjncy+i*nparts);
+  printf("Min/Max/Avg/Bal # of adjacent     subdomains: %5d %5d %5.2f %7.3f\n",
+    kpwgts[idxamin(nparts, kpwgts)], kpwgts[idxamax(nparts, kpwgts)], 
+    1.0*idxsum(nparts, kpwgts)/(1.0*nparts), 
+    1.0*nparts*kpwgts[idxamax(nparts, kpwgts)]/(1.0*idxsum(nparts, kpwgts)));
+
+  for (i=0; i<nparts; i++)
+    kpwgts[i] = idxsum(nparts, padjcut+i*nparts);
+  printf("Min/Max/Avg/Bal # of adjacent subdomain cuts: %5d %5d %5d %7.3f\n",
+    kpwgts[idxamin(nparts, kpwgts)], kpwgts[idxamax(nparts, kpwgts)], idxsum(nparts, kpwgts)/nparts, 
+    1.0*nparts*kpwgts[idxamax(nparts, kpwgts)]/(1.0*idxsum(nparts, kpwgts)));
+
+  for (i=0; i<nparts; i++)
+    kpwgts[i] = idxsum(nparts, padjwgt+i*nparts);
+  printf("Min/Max/Avg/Bal/Frac # of interface    nodes: %5d %5d %5d %7.3f %7.3f\n",
+    kpwgts[idxamin(nparts, kpwgts)], kpwgts[idxamax(nparts, kpwgts)], idxsum(nparts, kpwgts)/nparts, 
+    1.0*nparts*kpwgts[idxamax(nparts, kpwgts)]/(1.0*idxsum(nparts, kpwgts)), 1.0*idxsum(nparts, kpwgts)/(1.0*nvtxs));
+
+  tmpptr = graph->where;
+  graph->where = where;
+  for (i=0; i<nparts; i++)
+    IsConnectedSubdomain(NULL, graph, i, 1);
+  graph->where = tmpptr;
+
+  if (mustfree == 1 || mustfree == 3) {
+    free(vwgt);
+    graph->vwgt = NULL;
+  }
+  if (mustfree == 2 || mustfree == 3) {
+    free(adjwgt);
+    graph->adjwgt = NULL;
+  }
+
+  GKfree(&kpwgts, &padjncy, &padjwgt, &padjcut, LTERM);
+}
+
+
+/*************************************************************************
+* This function computes cuts and balance information
+**************************************************************************/
+void ComputePartitionInfoBipartite(GraphType *graph, int nparts, idxtype *where)
+{
+  int i, j, k, nvtxs, ncon, mustfree=0;
+  idxtype *xadj, *adjncy, *vwgt, *vsize, *adjwgt, *kpwgts, *tmpptr;
+  idxtype *padjncy, *padjwgt, *padjcut;
+
+  nvtxs = graph->nvtxs;
+  ncon = graph->ncon;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  vwgt = graph->vwgt;
+  vsize = graph->vsize;
+  adjwgt = graph->adjwgt;
+
+  if (vwgt == NULL) {
+    vwgt = graph->vwgt = idxsmalloc(nvtxs, 1, "vwgt");
+    mustfree = 1;
+  }
+  if (adjwgt == NULL) {
+    adjwgt = graph->adjwgt = idxsmalloc(xadj[nvtxs], 1, "adjwgt");
+    mustfree += 2;
+  }
+
+  printf("%d-way Cut: %5d, Vol: %5d, ", nparts, ComputeCut(graph, where), ComputeVolume(graph, where));
+
+  /* Compute balance information */
+  kpwgts = idxsmalloc(ncon*nparts, 0, "ComputePartitionInfo: kpwgts");
+
+  for (i=0; i<nvtxs; i++) {
+    for (j=0; j<ncon; j++) 
+      kpwgts[where[i]*ncon+j] += vwgt[i*ncon+j];
+  }
+
+  if (ncon == 1) {
+    printf("\tBalance: %5.3f out of %5.3f\n", 
+            1.0*nparts*kpwgts[idxamax(nparts, kpwgts)]/(1.0*idxsum(nparts, kpwgts)),
+            1.0*nparts*vwgt[idxamax(nvtxs, vwgt)]/(1.0*idxsum(nparts, kpwgts)));
+  }
+  else {
+    printf("\tBalance:");
+    for (j=0; j<ncon; j++) 
+      printf(" (%5.3f out of %5.3f)", 
+            1.0*nparts*kpwgts[ncon*idxamax_strd(nparts, kpwgts+j, ncon)+j]/(1.0*idxsum_strd(nparts, kpwgts+j, ncon)),
+            1.0*nparts*vwgt[ncon*idxamax_strd(nvtxs, vwgt+j, ncon)+j]/(1.0*idxsum_strd(nparts, kpwgts+j, ncon)));
+    printf("\n");
+  }
+
+
+  /* Compute p-adjncy information */
+  padjncy = idxsmalloc(nparts*nparts, 0, "ComputePartitionInfo: padjncy");
+  padjwgt = idxsmalloc(nparts*nparts, 0, "ComputePartitionInfo: padjwgt");
+  padjcut = idxsmalloc(nparts*nparts, 0, "ComputePartitionInfo: padjwgt");
+
+  idxset(nparts, 0, kpwgts);
+  for (i=0; i<nvtxs; i++) {
+    for (j=xadj[i]; j<xadj[i+1]; j++) {
+      if (where[i] != where[adjncy[j]]) {
+        padjncy[where[i]*nparts+where[adjncy[j]]] = 1;
+        padjcut[where[i]*nparts+where[adjncy[j]]] += adjwgt[j];
+        if (kpwgts[where[adjncy[j]]] == 0) {
+          padjwgt[where[i]*nparts+where[adjncy[j]]] += vsize[i];
+          kpwgts[where[adjncy[j]]] = 1;
+        }
+      }
+    }
+    for (j=xadj[i]; j<xadj[i+1]; j++) 
+      kpwgts[where[adjncy[j]]] = 0;
+  }
+
+  for (i=0; i<nparts; i++)
+    kpwgts[i] = idxsum(nparts, padjncy+i*nparts);
+  printf("Min/Max/Avg/Bal # of adjacent     subdomains: %5d %5d %5d %7.3f\n",
+    kpwgts[idxamin(nparts, kpwgts)], kpwgts[idxamax(nparts, kpwgts)], idxsum(nparts, kpwgts)/nparts, 
+    1.0*nparts*kpwgts[idxamax(nparts, kpwgts)]/(1.0*idxsum(nparts, kpwgts)));
+
+  for (i=0; i<nparts; i++)
+    kpwgts[i] = idxsum(nparts, padjcut+i*nparts);
+  printf("Min/Max/Avg/Bal # of adjacent subdomain cuts: %5d %5d %5d %7.3f\n",
+    kpwgts[idxamin(nparts, kpwgts)], kpwgts[idxamax(nparts, kpwgts)], idxsum(nparts, kpwgts)/nparts, 
+    1.0*nparts*kpwgts[idxamax(nparts, kpwgts)]/(1.0*idxsum(nparts, kpwgts)));
+
+  for (i=0; i<nparts; i++)
+    kpwgts[i] = idxsum(nparts, padjwgt+i*nparts);
+  printf("Min/Max/Avg/Bal/Frac # of interface    nodes: %5d %5d %5d %7.3f %7.3f\n",
+    kpwgts[idxamin(nparts, kpwgts)], kpwgts[idxamax(nparts, kpwgts)], idxsum(nparts, kpwgts)/nparts, 
+    1.0*nparts*kpwgts[idxamax(nparts, kpwgts)]/(1.0*idxsum(nparts, kpwgts)), 1.0*idxsum(nparts, kpwgts)/(1.0*nvtxs));
+
+
+  if (mustfree == 1 || mustfree == 3) {
+    free(vwgt);
+    graph->vwgt = NULL;
+  }
+  if (mustfree == 2 || mustfree == 3) {
+    free(adjwgt);
+    graph->adjwgt = NULL;
+  }
+
+  GKfree(&kpwgts, &padjncy, &padjwgt, &padjcut, LTERM);
+}
+
+
+
+/*************************************************************************
+* This function computes the balance of the partitioning
+**************************************************************************/
+void ComputePartitionBalance(GraphType *graph, int nparts, idxtype *where, float *ubvec)
+{
+  int i, j, nvtxs, ncon;
+  idxtype *kpwgts, *vwgt;
+  float balance;
+
+  nvtxs = graph->nvtxs;
+  ncon = graph->ncon;
+  vwgt = graph->vwgt;
+
+  kpwgts = idxsmalloc(nparts, 0, "ComputePartitionInfo: kpwgts");
+
+  if (vwgt == NULL) {
+    for (i=0; i<nvtxs; i++)
+      kpwgts[where[i]]++;
+    ubvec[0] = 1.0*nparts*kpwgts[idxamax(nparts, kpwgts)]/(1.0*nvtxs);
+  }
+  else {
+    for (j=0; j<ncon; j++) {
+      idxset(nparts, 0, kpwgts);
+      for (i=0; i<graph->nvtxs; i++)
+        kpwgts[where[i]] += vwgt[i*ncon+j];
+
+      ubvec[j] = 1.0*nparts*kpwgts[idxamax(nparts, kpwgts)]/(1.0*idxsum(nparts, kpwgts));
+    }
+  }
+
+  free(kpwgts);
+
+}
+
+
+/*************************************************************************
+* This function computes the balance of the element partitioning
+**************************************************************************/
+float ComputeElementBalance(int ne, int nparts, idxtype *where)
+{
+  int i;
+  idxtype *kpwgts;
+  float balance;
+
+  kpwgts = idxsmalloc(nparts, 0, "ComputeElementBalance: kpwgts");
+
+  for (i=0; i<ne; i++)
+    kpwgts[where[i]]++;
+
+  balance = 1.0*nparts*kpwgts[idxamax(nparts, kpwgts)]/(1.0*idxsum(nparts, kpwgts));
+
+  free(kpwgts);
+
+  return balance;
+
+}
diff --git a/contrib/Metis/struct.h b/contrib/Metis/struct.h
new file mode 100644
index 0000000000..f565d31c65
--- /dev/null
+++ b/contrib/Metis/struct.h
@@ -0,0 +1,251 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * struct.h
+ *
+ * This file contains data structures for ILU routines.
+ *
+ * Started 9/26/95
+ * George
+ *
+ * $Id: struct.h,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ */
+
+/* Undefine the following #define in order to use short int as the idxtype */
+#define IDXTYPE_INT
+
+/* Indexes are as long as integers for now */
+#ifdef IDXTYPE_INT
+typedef int idxtype;
+#else
+typedef short idxtype;
+#endif
+
+#define MAXIDX	(1<<8*sizeof(idxtype)-2)
+
+
+/*************************************************************************
+* The following data structure stores key-value pair
+**************************************************************************/
+struct KeyValueType {
+  idxtype key;
+  idxtype val;
+};
+
+typedef struct KeyValueType KeyValueType;
+
+
+/*************************************************************************
+* The following data structure will hold a node of a doubly-linked list.
+**************************************************************************/
+struct ListNodeType {
+  int id;                       	/* The id value of the node */
+  struct ListNodeType *prev, *next;     /* It's a doubly-linked list */
+};
+
+typedef struct ListNodeType ListNodeType;
+
+
+
+/*************************************************************************
+* The following data structure is used to store the buckets for the 
+* refinment algorithms
+**************************************************************************/
+struct PQueueType {
+  int type;                     /* The type of the representation used */
+  int nnodes;
+  int maxnodes;
+  int mustfree;
+
+  /* Linear array version of the data structures */
+  int pgainspan, ngainspan;     /* plus and negative gain span */
+  int maxgain;
+  ListNodeType *nodes;
+  ListNodeType **buckets;
+
+  /* Heap version of the data structure */
+  KeyValueType *heap;
+  idxtype *locator;
+};
+
+typedef struct PQueueType PQueueType;
+
+
+/*************************************************************************
+* The following data structure stores an edge
+**************************************************************************/
+struct edegreedef {
+  idxtype pid;
+  idxtype ed;
+};
+typedef struct edegreedef EDegreeType;
+
+
+/*************************************************************************
+* The following data structure stores an edge for vol
+**************************************************************************/
+struct vedegreedef {
+  idxtype pid;
+  idxtype ed, ned;
+  idxtype gv;
+};
+typedef struct vedegreedef VEDegreeType;
+
+
+/*************************************************************************
+* This data structure holds various working space data
+**************************************************************************/
+struct workspacedef {
+  idxtype *core;			/* Where pairs, indices, and degrees are coming from */
+  int maxcore, ccore;
+
+  EDegreeType *edegrees;
+  VEDegreeType *vedegrees;
+  int cdegree;
+
+  idxtype *auxcore;			/* This points to the memory of the edegrees */
+
+  idxtype *pmat;			/* An array of k^2 used for eliminating domain 
+                                           connectivity in k-way refinement */
+};
+
+typedef struct workspacedef WorkSpaceType;
+
+
+/*************************************************************************
+* The following data structure holds information on degrees for k-way
+* partition
+**************************************************************************/
+struct rinfodef {
+ int id, ed;            	/* ID/ED of nodes */
+ int ndegrees;          	/* The number of different ext-degrees */
+ EDegreeType *edegrees;     	/* List of edges */
+};
+
+typedef struct rinfodef RInfoType;
+
+
+/*************************************************************************
+* The following data structure holds information on degrees for k-way
+* vol-based partition
+**************************************************************************/
+struct vrinfodef {
+ int id, ed, nid;            	/* ID/ED of nodes */
+ int gv;            		/* IV/EV of nodes */
+ int ndegrees;          	/* The number of different ext-degrees */
+ VEDegreeType *edegrees;     	/* List of edges */
+};
+
+typedef struct vrinfodef VRInfoType;
+
+
+/*************************************************************************
+* The following data structure holds information on degrees for k-way
+* partition
+**************************************************************************/
+struct nrinfodef {
+ idxtype edegrees[2];  
+};
+
+typedef struct nrinfodef NRInfoType;
+
+
+/*************************************************************************
+* This data structure holds the input graph
+**************************************************************************/
+struct graphdef {
+  idxtype *gdata, *rdata;	/* Memory pools for graph and refinement data.
+                                   This is where memory is allocated and used
+                                   the rest of the fields in this structure */
+
+  int nvtxs, nedges;		/* The # of vertices and edges in the graph */
+  idxtype *xadj;		/* Pointers to the locally stored vertices */
+  idxtype *vwgt;		/* Vertex weights */
+  idxtype *vsize;		/* Vertex sizes for min-volume formulation */
+  idxtype *adjncy;		/* Array that stores the adjacency lists of nvtxs */
+  idxtype *adjwgt;		/* Array that stores the weights of the adjacency lists */
+
+  idxtype *adjwgtsum;		/* The sum of the adjacency weight of each vertex */
+
+  idxtype *label;
+
+  idxtype *cmap;
+
+  /* Partition parameters */
+  int mincut, minvol;
+  idxtype *where, *pwgts;
+  int nbnd;
+  idxtype *bndptr, *bndind;
+
+  /* Bisection refinement parameters */
+  idxtype *id, *ed;
+
+  /* K-way refinement parameters */
+  RInfoType *rinfo;
+
+  /* K-way volume refinement parameters */
+  VRInfoType *vrinfo;
+
+  /* Node refinement information */
+  NRInfoType *nrinfo;
+
+
+  /* Additional info needed by the MOC routines */
+  int ncon;			/* The # of constrains */ 
+  float *nvwgt;			/* Normalized vertex weights */
+  float *npwgts;		/* The normalized partition weights */
+
+  struct graphdef *coarser, *finer;
+};
+
+typedef struct graphdef GraphType;
+
+
+
+/*************************************************************************
+* The following data type implements a timer
+**************************************************************************/
+typedef double timer;
+
+
+/*************************************************************************
+* The following structure stores information used by Metis
+**************************************************************************/
+struct controldef {
+  int CoarsenTo;		/* The # of vertices in the coarsest graph */
+  int dbglvl;			/* Controls the debuging output of the program */
+  int CType;			/* The type of coarsening */
+  int IType;			/* The type of initial partitioning */
+  int RType;			/* The type of refinement */
+  int maxvwgt;			/* The maximum allowed weight for a vertex */
+  float nmaxvwgt;		/* The maximum allowed weight for a vertex for each constrain */
+  int optype;			/* Type of operation */
+  int pfactor;			/* .1*prunning factor */
+  int nseps;			/* The number of separators to be found during multiple bisections */
+  int oflags;
+
+  WorkSpaceType wspace;		/* Work Space Informations */
+
+  /* Various Timers */
+  timer TotalTmr, InitPartTmr, MatchTmr, ContractTmr, CoarsenTmr, UncoarsenTmr, 
+        SepTmr, RefTmr, ProjectTmr, SplitTmr, AuxTmr1, AuxTmr2, AuxTmr3, AuxTmr4, AuxTmr5, AuxTmr6;
+
+};
+
+typedef struct controldef CtrlType;
+
+
+/*************************************************************************
+* The following data structure stores max-partition weight info for 
+* Vertical MOC k-way refinement
+**************************************************************************/
+struct vpwgtdef {
+  float max[2][MAXNCON];
+  int imax[2][MAXNCON];
+};
+
+typedef struct vpwgtdef VPInfoType;
+
+
+
+
diff --git a/contrib/Metis/subdomains.c b/contrib/Metis/subdomains.c
new file mode 100644
index 0000000000..ed3b4db4c3
--- /dev/null
+++ b/contrib/Metis/subdomains.c
@@ -0,0 +1,1295 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * subdomains.c
+ *
+ * This file contains functions that deal with prunning the number of
+ * adjacent subdomains in KMETIS
+ *
+ * Started 7/15/98
+ * George
+ *
+ * $Id: subdomains.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function performs k-way refinement
+**************************************************************************/
+void Random_KWayEdgeRefineMConn(CtrlType *ctrl, GraphType *graph, int nparts, float *tpwgts, float ubfactor, int npasses, int ffactor)
+{
+  int i, ii, iii, j, jj, k, l, pass, nvtxs, nmoves, nbnd, tvwgt, myndegrees; 
+  int from, me, to, oldcut, vwgt, gain;
+  int maxndoms, nadd;
+  idxtype *xadj, *adjncy, *adjwgt;
+  idxtype *where, *pwgts, *perm, *bndptr, *bndind, *minwgt, *maxwgt, *itpwgts;
+  idxtype *phtable, *pmat, *pmatptr, *ndoms;
+  EDegreeType *myedegrees;
+  RInfoType *myrinfo;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  bndptr = graph->bndptr;
+  bndind = graph->bndind;
+
+  where = graph->where;
+  pwgts = graph->pwgts;
+
+  pmat = ctrl->wspace.pmat;
+  phtable = idxwspacemalloc(ctrl, nparts);
+  ndoms = idxwspacemalloc(ctrl, nparts);
+
+  ComputeSubDomainGraph(graph, nparts, pmat, ndoms);
+
+  /* Setup the weight intervals of the various subdomains */
+  minwgt =  idxwspacemalloc(ctrl, nparts);
+  maxwgt = idxwspacemalloc(ctrl, nparts);
+  itpwgts = idxwspacemalloc(ctrl, nparts);
+  tvwgt = idxsum(nparts, pwgts);
+  ASSERT(tvwgt == idxsum(nvtxs, graph->vwgt));
+
+  for (i=0; i<nparts; i++) {
+    itpwgts[i] = tpwgts[i]*tvwgt;
+    maxwgt[i] = tpwgts[i]*tvwgt*ubfactor;
+    minwgt[i] = tpwgts[i]*tvwgt*(1.0/ubfactor);
+  }
+
+  perm = idxwspacemalloc(ctrl, nvtxs);
+
+  IFSET(ctrl->dbglvl, DBG_REFINE,
+     printf("Partitions: [%6d %6d]-[%6d %6d], Balance: %5.3f, Nv-Nb[%6d %6d]. Cut: %6d\n",
+             pwgts[idxamin(nparts, pwgts)], pwgts[idxamax(nparts, pwgts)], minwgt[0], maxwgt[0], 
+             1.0*nparts*pwgts[idxamax(nparts, pwgts)]/tvwgt, graph->nvtxs, graph->nbnd,
+             graph->mincut));
+
+  for (pass=0; pass<npasses; pass++) {
+    ASSERT(ComputeCut(graph, where) == graph->mincut);
+
+    maxndoms = ndoms[idxamax(nparts, ndoms)];
+
+    oldcut = graph->mincut;
+    nbnd = graph->nbnd;
+
+    RandomPermute(nbnd, perm, 1);
+    for (nmoves=iii=0; iii<graph->nbnd; iii++) {
+      ii = perm[iii];
+      if (ii >= nbnd)
+        continue;
+      i = bndind[ii];
+
+      myrinfo = graph->rinfo+i;
+
+      if (myrinfo->ed >= myrinfo->id) { /* Total ED is too high */
+        from = where[i];
+        vwgt = graph->vwgt[i];
+
+        if (myrinfo->id > 0 && pwgts[from]-vwgt < minwgt[from]) 
+          continue;   /* This cannot be moved! */
+
+        myedegrees = myrinfo->edegrees;
+        myndegrees = myrinfo->ndegrees;
+
+        /* Determine the valid domains */
+        for (j=0; j<myndegrees; j++) {
+          to = myedegrees[j].pid;
+          phtable[to] = 1;
+          pmatptr = pmat + to*nparts;
+          for (nadd=0, k=0; k<myndegrees; k++) {
+            if (k == j)
+              continue;
+
+            l = myedegrees[k].pid;
+            if (pmatptr[l] == 0) {
+              if (ndoms[l] > maxndoms-1) {
+                phtable[to] = 0;
+                nadd = maxndoms;
+                break;
+              }
+              nadd++;
+            }
+          }
+          if (ndoms[to]+nadd > maxndoms)
+            phtable[to] = 0;
+          if (nadd == 0)
+            phtable[to] = 2;
+        }
+
+        /* Find the first valid move */
+        j = myrinfo->id;
+        for (k=0; k<myndegrees; k++) {
+          to = myedegrees[k].pid;
+          if (!phtable[to])
+            continue;
+          gain = myedegrees[k].ed-j; /* j = myrinfo->id. Allow good nodes to move */ 
+          if (pwgts[to]+vwgt <= maxwgt[to]+ffactor*gain && gain >= 0)  
+            break;
+        }
+        if (k == myndegrees)
+          continue;  /* break out if you did not find a candidate */
+
+        for (j=k+1; j<myndegrees; j++) {
+          to = myedegrees[j].pid;
+          if (!phtable[to])
+            continue;
+          if ((myedegrees[j].ed > myedegrees[k].ed && pwgts[to]+vwgt <= maxwgt[to]) ||
+              (myedegrees[j].ed == myedegrees[k].ed && 
+               itpwgts[myedegrees[k].pid]*pwgts[to] < itpwgts[to]*pwgts[myedegrees[k].pid]))
+            k = j;
+        }
+
+        to = myedegrees[k].pid;
+
+        j = 0;
+        if (myedegrees[k].ed-myrinfo->id > 0)
+          j = 1;
+        else if (myedegrees[k].ed-myrinfo->id == 0) {
+          if (/*(iii&7) == 0  ||*/ phtable[myedegrees[k].pid] == 2 || pwgts[from] >= maxwgt[from] || itpwgts[from]*(pwgts[to]+vwgt) < itpwgts[to]*pwgts[from])
+            j = 1;
+        }
+        if (j == 0)
+          continue;
+          
+        /*=====================================================================
+        * If we got here, we can now move the vertex from 'from' to 'to' 
+        *======================================================================*/
+        graph->mincut -= myedegrees[k].ed-myrinfo->id;
+
+        IFSET(ctrl->dbglvl, DBG_MOVEINFO, printf("\t\tMoving %6d to %3d. Gain: %4d. Cut: %6d\n", i, to, myedegrees[k].ed-myrinfo->id, graph->mincut));
+
+        /* Update pmat to reflect the move of 'i' */
+        pmat[from*nparts+to] += (myrinfo->id-myedegrees[k].ed);
+        pmat[to*nparts+from] += (myrinfo->id-myedegrees[k].ed);
+        if (pmat[from*nparts+to] == 0) {
+          ndoms[from]--;
+          if (ndoms[from]+1 == maxndoms)
+            maxndoms = ndoms[idxamax(nparts, ndoms)];
+        }
+        if (pmat[to*nparts+from] == 0) {
+          ndoms[to]--;
+          if (ndoms[to]+1 == maxndoms)
+            maxndoms = ndoms[idxamax(nparts, ndoms)];
+        }
+
+        /* Update where, weight, and ID/ED information of the vertex you moved */
+        where[i] = to;
+        INC_DEC(pwgts[to], pwgts[from], vwgt);
+        myrinfo->ed += myrinfo->id-myedegrees[k].ed;
+        SWAP(myrinfo->id, myedegrees[k].ed, j);
+        if (myedegrees[k].ed == 0) 
+          myedegrees[k] = myedegrees[--myrinfo->ndegrees];
+        else
+          myedegrees[k].pid = from;
+
+        if (myrinfo->ed-myrinfo->id < 0)
+          BNDDelete(nbnd, bndind, bndptr, i);
+
+        /* Update the degrees of adjacent vertices */
+        for (j=xadj[i]; j<xadj[i+1]; j++) {
+          ii = adjncy[j];
+          me = where[ii];
+
+          myrinfo = graph->rinfo+ii;
+          if (myrinfo->edegrees == NULL) {
+            myrinfo->edegrees = ctrl->wspace.edegrees+ctrl->wspace.cdegree;
+            ctrl->wspace.cdegree += xadj[ii+1]-xadj[ii];
+          }
+          myedegrees = myrinfo->edegrees;
+
+          ASSERT(CheckRInfo(myrinfo));
+
+          if (me == from) {
+            INC_DEC(myrinfo->ed, myrinfo->id, adjwgt[j]);
+
+            if (myrinfo->ed-myrinfo->id >= 0 && bndptr[ii] == -1)
+              BNDInsert(nbnd, bndind, bndptr, ii);
+          }
+          else if (me == to) {
+            INC_DEC(myrinfo->id, myrinfo->ed, adjwgt[j]);
+
+            if (myrinfo->ed-myrinfo->id < 0 && bndptr[ii] != -1)
+              BNDDelete(nbnd, bndind, bndptr, ii);
+          }
+
+          /* Remove contribution from the .ed of 'from' */
+          if (me != from) {
+            for (k=0; k<myrinfo->ndegrees; k++) {
+              if (myedegrees[k].pid == from) {
+                if (myedegrees[k].ed == adjwgt[j])
+                  myedegrees[k] = myedegrees[--myrinfo->ndegrees];
+                else
+                  myedegrees[k].ed -= adjwgt[j];
+                break;
+              }
+            }
+          }
+
+          /* Add contribution to the .ed of 'to' */
+          if (me != to) {
+            for (k=0; k<myrinfo->ndegrees; k++) {
+              if (myedegrees[k].pid == to) {
+                myedegrees[k].ed += adjwgt[j];
+                break;
+              }
+            }
+            if (k == myrinfo->ndegrees) {
+              myedegrees[myrinfo->ndegrees].pid = to;
+              myedegrees[myrinfo->ndegrees++].ed = adjwgt[j];
+            }
+          }
+
+          /* Update pmat to reflect the move of 'i' for domains other than 'from' and 'to' */
+          if (me != from && me != to) {
+            pmat[me*nparts+from] -= adjwgt[j];
+            pmat[from*nparts+me] -= adjwgt[j];
+            if (pmat[me*nparts+from] == 0) {
+              ndoms[me]--;
+              if (ndoms[me]+1 == maxndoms)
+                maxndoms = ndoms[idxamax(nparts, ndoms)];
+            }
+            if (pmat[from*nparts+me] == 0) {
+              ndoms[from]--;
+              if (ndoms[from]+1 == maxndoms)
+                maxndoms = ndoms[idxamax(nparts, ndoms)];
+            }
+
+            if (pmat[me*nparts+to] == 0) {
+              ndoms[me]++;
+              if (ndoms[me] > maxndoms) {
+                printf("You just increased the maxndoms: %d %d\n", ndoms[me], maxndoms);
+                maxndoms = ndoms[me];
+              }
+            }
+            if (pmat[to*nparts+me] == 0) {
+              ndoms[to]++;
+              if (ndoms[to] > maxndoms) {
+                printf("You just increased the maxndoms: %d %d\n", ndoms[to], maxndoms);
+                maxndoms = ndoms[to];
+              }
+            }
+            pmat[me*nparts+to] += adjwgt[j];
+            pmat[to*nparts+me] += adjwgt[j];
+          }
+
+          ASSERT(myrinfo->ndegrees <= xadj[ii+1]-xadj[ii]);
+          ASSERT(CheckRInfo(myrinfo));
+
+        }
+        nmoves++;
+      }
+    }
+
+    graph->nbnd = nbnd;
+
+    IFSET(ctrl->dbglvl, DBG_REFINE,
+       printf("\t[%6d %6d], Balance: %5.3f, Nb: %6d. Nmoves: %5d, Cut: %5d, Vol: %5d, %d\n",
+               pwgts[idxamin(nparts, pwgts)], pwgts[idxamax(nparts, pwgts)],
+               1.0*nparts*pwgts[idxamax(nparts, pwgts)]/tvwgt, graph->nbnd, nmoves, 
+               graph->mincut, ComputeVolume(graph, where), idxsum(nparts, ndoms)));
+
+    if (graph->mincut == oldcut)
+      break;
+  }
+
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nvtxs);
+}
+
+
+
+/*************************************************************************
+* This function performs k-way refinement
+**************************************************************************/
+void Greedy_KWayEdgeBalanceMConn(CtrlType *ctrl, GraphType *graph, int nparts, float *tpwgts, float ubfactor, int npasses)
+{
+  int i, ii, iii, j, jj, k, l, pass, nvtxs, nbnd, tvwgt, myndegrees, oldgain, gain, nmoves; 
+  int from, me, to, oldcut, vwgt, maxndoms, nadd;
+  idxtype *xadj, *adjncy, *adjwgt;
+  idxtype *where, *pwgts, *perm, *bndptr, *bndind, *minwgt, *maxwgt, *moved, *itpwgts;
+  idxtype *phtable, *pmat, *pmatptr, *ndoms;
+  EDegreeType *myedegrees;
+  RInfoType *myrinfo;
+  PQueueType queue;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  bndind = graph->bndind;
+  bndptr = graph->bndptr;
+
+  where = graph->where;
+  pwgts = graph->pwgts;
+  
+  pmat = ctrl->wspace.pmat;
+  phtable = idxwspacemalloc(ctrl, nparts);
+  ndoms = idxwspacemalloc(ctrl, nparts);
+
+  ComputeSubDomainGraph(graph, nparts, pmat, ndoms);
+
+
+  /* Setup the weight intervals of the various subdomains */
+  minwgt =  idxwspacemalloc(ctrl, nparts);
+  maxwgt = idxwspacemalloc(ctrl, nparts);
+  itpwgts = idxwspacemalloc(ctrl, nparts);
+  tvwgt = idxsum(nparts, pwgts);
+  ASSERT(tvwgt == idxsum(nvtxs, graph->vwgt));
+
+  for (i=0; i<nparts; i++) {
+    itpwgts[i] = tpwgts[i]*tvwgt;
+    maxwgt[i] = tpwgts[i]*tvwgt*ubfactor;
+    minwgt[i] = tpwgts[i]*tvwgt*(1.0/ubfactor);
+  }
+
+  perm = idxwspacemalloc(ctrl, nvtxs);
+  moved = idxwspacemalloc(ctrl, nvtxs);
+
+  PQueueInit(ctrl, &queue, nvtxs, graph->adjwgtsum[idxamax(nvtxs, graph->adjwgtsum)]);
+
+  IFSET(ctrl->dbglvl, DBG_REFINE,
+     printf("Partitions: [%6d %6d]-[%6d %6d], Balance: %5.3f, Nv-Nb[%6d %6d]. Cut: %6d [B]\n",
+             pwgts[idxamin(nparts, pwgts)], pwgts[idxamax(nparts, pwgts)], minwgt[0], maxwgt[0], 
+             1.0*nparts*pwgts[idxamax(nparts, pwgts)]/tvwgt, graph->nvtxs, graph->nbnd,
+             graph->mincut));
+
+  for (pass=0; pass<npasses; pass++) {
+    ASSERT(ComputeCut(graph, where) == graph->mincut);
+
+    /* Check to see if things are out of balance, given the tolerance */
+    for (i=0; i<nparts; i++) {
+      if (pwgts[i] > maxwgt[i])
+        break;
+    }
+    if (i == nparts) /* Things are balanced. Return right away */
+      break;
+
+    PQueueReset(&queue);
+    idxset(nvtxs, -1, moved);
+
+    oldcut = graph->mincut;
+    nbnd = graph->nbnd;
+
+    RandomPermute(nbnd, perm, 1);
+    for (ii=0; ii<nbnd; ii++) {
+      i = bndind[perm[ii]];
+      PQueueInsert(&queue, i, graph->rinfo[i].ed - graph->rinfo[i].id);
+      moved[i] = 2;
+    }
+
+    maxndoms = ndoms[idxamax(nparts, ndoms)];
+
+    for (nmoves=0;;) {
+      if ((i = PQueueGetMax(&queue)) == -1) 
+        break;
+      moved[i] = 1;
+
+      myrinfo = graph->rinfo+i;
+      from = where[i];
+      vwgt = graph->vwgt[i];
+
+      if (pwgts[from]-vwgt < minwgt[from]) 
+        continue;   /* This cannot be moved! */
+
+      myedegrees = myrinfo->edegrees;
+      myndegrees = myrinfo->ndegrees;
+
+      /* Determine the valid domains */
+      for (j=0; j<myndegrees; j++) {
+        to = myedegrees[j].pid;
+        phtable[to] = 1;
+        pmatptr = pmat + to*nparts;
+        for (nadd=0, k=0; k<myndegrees; k++) {
+          if (k == j)
+            continue;
+
+          l = myedegrees[k].pid;
+          if (pmatptr[l] == 0) {
+            if (ndoms[l] > maxndoms-1) {
+              phtable[to] = 0;
+              nadd = maxndoms;
+              break;
+            }
+            nadd++;
+          }
+        }
+        if (ndoms[to]+nadd > maxndoms)
+          phtable[to] = 0;
+      }
+
+      for (k=0; k<myndegrees; k++) {
+        to = myedegrees[k].pid;
+        if (!phtable[to])
+          continue;
+        if (pwgts[to]+vwgt <= maxwgt[to] || itpwgts[from]*(pwgts[to]+vwgt) <= itpwgts[to]*pwgts[from]) 
+          break;
+      }
+      if (k == myndegrees)
+        continue;  /* break out if you did not find a candidate */
+
+      for (j=k+1; j<myndegrees; j++) {
+        to = myedegrees[j].pid;
+        if (!phtable[to])
+          continue;
+        if (itpwgts[myedegrees[k].pid]*pwgts[to] < itpwgts[to]*pwgts[myedegrees[k].pid]) 
+          k = j;
+      }
+
+      to = myedegrees[k].pid;
+
+      if (pwgts[from] < maxwgt[from] && pwgts[to] > minwgt[to] && myedegrees[k].ed-myrinfo->id < 0) 
+        continue;
+
+      /*=====================================================================
+      * If we got here, we can now move the vertex from 'from' to 'to' 
+      *======================================================================*/
+      graph->mincut -= myedegrees[k].ed-myrinfo->id;
+
+      IFSET(ctrl->dbglvl, DBG_MOVEINFO, printf("\t\tMoving %6d to %3d. Gain: %4d. Cut: %6d\n", i, to, myedegrees[k].ed-myrinfo->id, graph->mincut));
+
+      /* Update pmat to reflect the move of 'i' */
+      pmat[from*nparts+to] += (myrinfo->id-myedegrees[k].ed);
+      pmat[to*nparts+from] += (myrinfo->id-myedegrees[k].ed);
+      if (pmat[from*nparts+to] == 0) {
+        ndoms[from]--;
+        if (ndoms[from]+1 == maxndoms)
+          maxndoms = ndoms[idxamax(nparts, ndoms)];
+      }
+      if (pmat[to*nparts+from] == 0) {
+        ndoms[to]--;
+        if (ndoms[to]+1 == maxndoms)
+          maxndoms = ndoms[idxamax(nparts, ndoms)];
+      }
+
+
+      /* Update where, weight, and ID/ED information of the vertex you moved */
+      where[i] = to;
+      INC_DEC(pwgts[to], pwgts[from], vwgt);
+      myrinfo->ed += myrinfo->id-myedegrees[k].ed;
+      SWAP(myrinfo->id, myedegrees[k].ed, j);
+      if (myedegrees[k].ed == 0) 
+        myedegrees[k] = myedegrees[--myrinfo->ndegrees];
+      else
+        myedegrees[k].pid = from;
+
+      if (myrinfo->ed == 0)
+        BNDDelete(nbnd, bndind, bndptr, i);
+
+      /* Update the degrees of adjacent vertices */
+      for (j=xadj[i]; j<xadj[i+1]; j++) {
+        ii = adjncy[j];
+        me = where[ii];
+
+        myrinfo = graph->rinfo+ii;
+        if (myrinfo->edegrees == NULL) {
+          myrinfo->edegrees = ctrl->wspace.edegrees+ctrl->wspace.cdegree;
+          ctrl->wspace.cdegree += xadj[ii+1]-xadj[ii];
+        }
+        myedegrees = myrinfo->edegrees;
+
+        ASSERT(CheckRInfo(myrinfo));
+
+        oldgain = (myrinfo->ed-myrinfo->id);
+
+        if (me == from) {
+          INC_DEC(myrinfo->ed, myrinfo->id, adjwgt[j]);
+
+          if (myrinfo->ed > 0 && bndptr[ii] == -1)
+            BNDInsert(nbnd, bndind, bndptr, ii);
+        }
+        else if (me == to) {
+          INC_DEC(myrinfo->id, myrinfo->ed, adjwgt[j]);
+
+          if (myrinfo->ed == 0 && bndptr[ii] != -1)
+            BNDDelete(nbnd, bndind, bndptr, ii);
+        }
+
+        /* Remove contribution from the .ed of 'from' */
+        if (me != from) {
+          for (k=0; k<myrinfo->ndegrees; k++) {
+            if (myedegrees[k].pid == from) {
+              if (myedegrees[k].ed == adjwgt[j])
+                myedegrees[k] = myedegrees[--myrinfo->ndegrees];
+              else
+                myedegrees[k].ed -= adjwgt[j];
+              break;
+            }
+          }
+        }
+
+        /* Add contribution to the .ed of 'to' */
+        if (me != to) {
+          for (k=0; k<myrinfo->ndegrees; k++) {
+            if (myedegrees[k].pid == to) {
+              myedegrees[k].ed += adjwgt[j];
+              break;
+            }
+          }
+          if (k == myrinfo->ndegrees) {
+            myedegrees[myrinfo->ndegrees].pid = to;
+            myedegrees[myrinfo->ndegrees++].ed = adjwgt[j];
+          }
+        }
+
+        /* Update pmat to reflect the move of 'i' for domains other than 'from' and 'to' */
+        if (me != from && me != to) {
+          pmat[me*nparts+from] -= adjwgt[j];
+          pmat[from*nparts+me] -= adjwgt[j];
+          if (pmat[me*nparts+from] == 0) {
+            ndoms[me]--;
+            if (ndoms[me]+1 == maxndoms)
+              maxndoms = ndoms[idxamax(nparts, ndoms)];
+          }
+          if (pmat[from*nparts+me] == 0) {
+            ndoms[from]--;
+            if (ndoms[from]+1 == maxndoms)
+              maxndoms = ndoms[idxamax(nparts, ndoms)];
+          }
+
+          if (pmat[me*nparts+to] == 0) {
+            ndoms[me]++;
+            if (ndoms[me] > maxndoms) {
+              printf("You just increased the maxndoms: %d %d\n", ndoms[me], maxndoms);
+              maxndoms = ndoms[me];
+            }
+          }
+          if (pmat[to*nparts+me] == 0) {
+            ndoms[to]++;
+            if (ndoms[to] > maxndoms) {
+              printf("You just increased the maxndoms: %d %d\n", ndoms[to], maxndoms);
+              maxndoms = ndoms[to];
+            }
+          }
+          pmat[me*nparts+to] += adjwgt[j];
+          pmat[to*nparts+me] += adjwgt[j];
+        }
+
+        /* Update the queue */
+        if (me == to || me == from) { 
+          gain = myrinfo->ed-myrinfo->id;
+          if (moved[ii] == 2) {
+            if (myrinfo->ed > 0)
+              PQueueUpdate(&queue, ii, oldgain, gain);
+            else {
+              PQueueDelete(&queue, ii, oldgain);
+              moved[ii] = -1;
+            }
+          }
+          else if (moved[ii] == -1 && myrinfo->ed > 0) {
+            PQueueInsert(&queue, ii, gain);
+            moved[ii] = 2;
+          }
+        } 
+
+        ASSERT(myrinfo->ndegrees <= xadj[ii+1]-xadj[ii]);
+        ASSERT(CheckRInfo(myrinfo));
+      }
+      nmoves++;
+    }
+
+    graph->nbnd = nbnd;
+
+    IFSET(ctrl->dbglvl, DBG_REFINE,
+       printf("\t[%6d %6d], Balance: %5.3f, Nb: %6d. Nmoves: %5d, Cut: %6d, %d\n",
+               pwgts[idxamin(nparts, pwgts)], pwgts[idxamax(nparts, pwgts)],
+               1.0*nparts*pwgts[idxamax(nparts, pwgts)]/tvwgt, graph->nbnd, nmoves, graph->mincut,idxsum(nparts, ndoms)));
+  }
+
+  PQueueFree(ctrl, &queue);
+
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+
+}
+
+
+
+
+/*************************************************************************
+* This function computes the subdomain graph
+**************************************************************************/
+void PrintSubDomainGraph(GraphType *graph, int nparts, idxtype *where)
+{
+  int i, j, k, me, nvtxs, total, max;
+  idxtype *xadj, *adjncy, *adjwgt, *pmat;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  pmat = idxsmalloc(nparts*nparts, 0, "ComputeSubDomainGraph: pmat");
+
+  for (i=0; i<nvtxs; i++) {
+    me = where[i];
+    for (j=xadj[i]; j<xadj[i+1]; j++) {
+      k = adjncy[j];
+      if (where[k] != me) 
+        pmat[me*nparts+where[k]] += adjwgt[j];
+    }
+  }
+
+  /* printf("Subdomain Info\n"); */
+  total = max = 0;
+  for (i=0; i<nparts; i++) {
+    for (k=0, j=0; j<nparts; j++) {
+      if (pmat[i*nparts+j] > 0)
+        k++;
+    }
+    total += k;
+
+    if (k > max)
+      max = k;
+/*
+    printf("%2d -> %2d  ", i, k);
+    for (j=0; j<nparts; j++) {
+      if (pmat[i*nparts+j] > 0)
+        printf("[%2d %4d] ", j, pmat[i*nparts+j]);
+    }
+    printf("\n");
+*/
+  }
+  printf("Total adjacent subdomains: %d, Max: %d\n", total, max);
+
+  free(pmat);
+}
+
+
+
+/*************************************************************************
+* This function computes the subdomain graph
+**************************************************************************/
+void ComputeSubDomainGraph(GraphType *graph, int nparts, idxtype *pmat, idxtype *ndoms)
+{
+  int i, j, k, me, nvtxs, ndegrees;
+  idxtype *xadj, *adjncy, *adjwgt, *where;
+  RInfoType *rinfo;
+  EDegreeType *edegrees;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+  where = graph->where;
+  rinfo = graph->rinfo;
+
+  idxset(nparts*nparts, 0, pmat);
+
+  for (i=0; i<nvtxs; i++) {
+    if (rinfo[i].ed > 0) {
+      me = where[i];
+      ndegrees = rinfo[i].ndegrees;
+      edegrees = rinfo[i].edegrees;
+
+      k = me*nparts;
+      for (j=0; j<ndegrees; j++) 
+        pmat[k+edegrees[j].pid] += edegrees[j].ed;
+    }
+  }
+
+  for (i=0; i<nparts; i++) {
+    ndoms[i] = 0;
+    for (j=0; j<nparts; j++) {
+      if (pmat[i*nparts+j] > 0)
+        ndoms[i]++;
+    }
+  }
+
+}
+
+
+
+
+
+/*************************************************************************
+* This function computes the subdomain graph
+**************************************************************************/
+void EliminateSubDomainEdges(CtrlType *ctrl, GraphType *graph, int nparts, float *tpwgts)
+{
+  int i, ii, j, k, me, other, nvtxs, total, max, avg, totalout, nind, ncand, ncand2, target, target2, nadd;
+  int min, move, cpwgt, tvwgt;
+  idxtype *xadj, *adjncy, *vwgt, *adjwgt, *pwgts, *where, *maxpwgt, *pmat, *ndoms, *mypmat, *otherpmat, *ind;
+  KeyValueType *cand, *cand2;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  vwgt = graph->vwgt;
+  adjwgt = graph->adjwgt;
+
+  where = graph->where;
+  pwgts = graph->pwgts;  /* We assume that this is properly initialized */
+
+  maxpwgt = idxwspacemalloc(ctrl, nparts);
+  ndoms = idxwspacemalloc(ctrl, nparts);
+  otherpmat = idxwspacemalloc(ctrl, nparts);
+  ind = idxwspacemalloc(ctrl, nvtxs);
+  pmat = ctrl->wspace.pmat;
+
+  cand = (KeyValueType *)GKmalloc(nparts*sizeof(KeyValueType), "EliminateSubDomainEdges: cand");
+  cand2 = (KeyValueType *)GKmalloc(nparts*sizeof(KeyValueType), "EliminateSubDomainEdges: cand");
+
+  /* Compute the pmat matrix and ndoms */
+  ComputeSubDomainGraph(graph, nparts, pmat, ndoms);
+
+
+  /* Compute the maximum allowed weight for each domain */
+  tvwgt = idxsum(nparts, pwgts);
+  for (i=0; i<nparts; i++)
+    maxpwgt[i] = 1.25*tpwgts[i]*tvwgt;
+
+
+  /* Get into the loop eliminating subdomain connections */
+  for (;;) {
+    total = idxsum(nparts, ndoms);
+    avg = total/nparts;
+    max = ndoms[idxamax(nparts, ndoms)];
+
+    /* printf("Adjacent Subdomain Stats: Total: %3d, Max: %3d, Avg: %3d [%5d]\n", total, max, avg, idxsum(nparts*nparts, pmat)); */
+
+    if (max < 1.4*avg)
+      break;
+
+    me = idxamax(nparts, ndoms);
+    mypmat = pmat + me*nparts;
+    totalout = idxsum(nparts, mypmat);
+
+    /*printf("Me: %d, TotalOut: %d,\n", me, totalout);*/
+
+    /* Sort the connections according to their cut */
+    for (ncand2=0, i=0; i<nparts; i++) {
+      if (mypmat[i] > 0) {
+        cand2[ncand2].key = mypmat[i];
+        cand2[ncand2++].val = i;
+      }
+    }
+    ikeysort(ncand2, cand2);
+
+    move = 0;
+    for (min=0; min<ncand2; min++) {
+      if (cand2[min].key > totalout/(2*ndoms[me])) 
+        break;
+
+      other = cand2[min].val;
+
+      /*printf("\tMinOut: %d to %d\n", mypmat[other], other);*/
+
+      idxset(nparts, 0, otherpmat);
+
+      /* Go and find the vertices in 'other' that are connected in 'me' */
+      for (nind=0, i=0; i<nvtxs; i++) {
+        if (where[i] == other) {
+          for (j=xadj[i]; j<xadj[i+1]; j++) {
+            if (where[adjncy[j]] == me) {
+              ind[nind++] = i;
+              break;
+            }
+          }
+        }
+      }
+
+      /* Go and construct the otherpmat to see where these nind vertices are connected to */
+      for (cpwgt=0, ii=0; ii<nind; ii++) {
+        i = ind[ii];
+        cpwgt += vwgt[i];
+
+        for (j=xadj[i]; j<xadj[i+1]; j++) 
+          otherpmat[where[adjncy[j]]] += adjwgt[j];
+      }
+      otherpmat[other] = 0;
+
+      for (ncand=0, i=0; i<nparts; i++) {
+        if (otherpmat[i] > 0) {
+          cand[ncand].key = -otherpmat[i];
+          cand[ncand++].val = i;
+        }
+      }
+      ikeysort(ncand, cand);
+
+      /* 
+       * Go through and the select the first domain that is common with 'me', and
+       * does not increase the ndoms[target] higher than my ndoms, subject to the
+       * maxpwgt constraint. Traversal is done from the mostly connected to the least.
+       */
+      target = target2 = -1;
+      for (i=0; i<ncand; i++) {
+        k = cand[i].val;
+
+        if (mypmat[k] > 0) {
+          if (pwgts[k] + cpwgt > maxpwgt[k])  /* Check if balance will go off */
+            continue;
+
+          for (j=0; j<nparts; j++) {
+            if (otherpmat[j] > 0 && ndoms[j] >= ndoms[me]-1 && pmat[nparts*j+k] == 0)
+              break;
+          }
+          if (j == nparts) { /* No bad second level effects */
+            for (nadd=0, j=0; j<nparts; j++) {
+              if (otherpmat[j] > 0 && pmat[nparts*k+j] == 0)
+                nadd++;
+            }
+
+            /*printf("\t\tto=%d, nadd=%d, %d\n", k, nadd, ndoms[k]);*/
+            if (target2 == -1 && ndoms[k]+nadd < ndoms[me]) {
+              target2 = k;
+            }
+            if (nadd == 0) {
+              target = k;
+              break;
+            }
+          }
+        }
+      }
+      if (target == -1 && target2 != -1)
+        target = target2;
+
+      if (target == -1) {
+        /* printf("\t\tCould not make the move\n");*/
+        continue;
+      }
+
+      /*printf("\t\tMoving to %d\n", target);*/
+
+      /* Update the partition weights */
+      INC_DEC(pwgts[target], pwgts[other], cpwgt);
+
+      MoveGroupMConn(ctrl, graph, ndoms, pmat, nparts, target, nind, ind);
+
+      move = 1;
+      break;
+    }
+
+    if (move == 0)
+      break;
+  }
+
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nvtxs);
+
+  GKfree(&cand, &cand2, LTERM);
+}
+
+
+/*************************************************************************
+* This function moves a collection of vertices and updates their rinfo
+**************************************************************************/
+void MoveGroupMConn(CtrlType *ctrl, GraphType *graph, idxtype *ndoms, idxtype *pmat,
+                    int nparts, int to, int nind, idxtype *ind)
+{
+  int i, ii, iii, j, jj, k, l, nvtxs, nbnd, myndegrees; 
+  int from, me;
+  idxtype *xadj, *adjncy, *adjwgt;
+  idxtype *where, *bndptr, *bndind;
+  EDegreeType *myedegrees;
+  RInfoType *myrinfo;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  where = graph->where;
+  bndptr = graph->bndptr;
+  bndind = graph->bndind;
+
+  nbnd = graph->nbnd;
+
+  for (iii=0; iii<nind; iii++) {
+    i = ind[iii];
+    from = where[i];
+
+    myrinfo = graph->rinfo+i;
+    if (myrinfo->edegrees == NULL) {
+      myrinfo->edegrees = ctrl->wspace.edegrees+ctrl->wspace.cdegree;
+      ctrl->wspace.cdegree += xadj[i+1]-xadj[i];
+      myrinfo->ndegrees = 0;
+    }
+    myedegrees = myrinfo->edegrees;
+
+    /* find the location of 'to' in myrinfo or create it if it is not there */
+    for (k=0; k<myrinfo->ndegrees; k++) {
+      if (myedegrees[k].pid == to)
+        break;
+    }
+    if (k == myrinfo->ndegrees) {
+      myedegrees[k].pid = to;
+      myedegrees[k].ed = 0;
+      myrinfo->ndegrees++;
+    }
+
+    graph->mincut -= myedegrees[k].ed-myrinfo->id;
+
+    /* Update pmat to reflect the move of 'i' */
+    pmat[from*nparts+to] += (myrinfo->id-myedegrees[k].ed);
+    pmat[to*nparts+from] += (myrinfo->id-myedegrees[k].ed);
+    if (pmat[from*nparts+to] == 0) 
+      ndoms[from]--;
+    if (pmat[to*nparts+from] == 0) 
+      ndoms[to]--;
+
+    /* Update where, weight, and ID/ED information of the vertex you moved */
+    where[i] = to;
+    myrinfo->ed += myrinfo->id-myedegrees[k].ed;
+    SWAP(myrinfo->id, myedegrees[k].ed, j);
+    if (myedegrees[k].ed == 0) 
+      myedegrees[k] = myedegrees[--myrinfo->ndegrees];
+    else
+      myedegrees[k].pid = from;
+
+    if (myrinfo->ed-myrinfo->id < 0 && bndptr[i] != -1)
+      BNDDelete(nbnd, bndind, bndptr, i);
+
+    /* Update the degrees of adjacent vertices */
+    for (j=xadj[i]; j<xadj[i+1]; j++) {
+      ii = adjncy[j];
+      me = where[ii];
+
+      myrinfo = graph->rinfo+ii;
+      if (myrinfo->edegrees == NULL) {
+        myrinfo->edegrees = ctrl->wspace.edegrees+ctrl->wspace.cdegree;
+        ctrl->wspace.cdegree += xadj[ii+1]-xadj[ii];
+      }
+      myedegrees = myrinfo->edegrees;
+
+      ASSERT(CheckRInfo(myrinfo));
+
+      if (me == from) {
+        INC_DEC(myrinfo->ed, myrinfo->id, adjwgt[j]);
+
+        if (myrinfo->ed-myrinfo->id >= 0 && bndptr[ii] == -1)
+          BNDInsert(nbnd, bndind, bndptr, ii);
+      }
+      else if (me == to) {
+        INC_DEC(myrinfo->id, myrinfo->ed, adjwgt[j]);
+
+        if (myrinfo->ed-myrinfo->id < 0 && bndptr[ii] != -1)
+          BNDDelete(nbnd, bndind, bndptr, ii);
+      }
+
+      /* Remove contribution from the .ed of 'from' */
+      if (me != from) {
+        for (k=0; k<myrinfo->ndegrees; k++) {
+          if (myedegrees[k].pid == from) {
+            if (myedegrees[k].ed == adjwgt[j])
+              myedegrees[k] = myedegrees[--myrinfo->ndegrees];
+            else
+              myedegrees[k].ed -= adjwgt[j];
+            break;
+          }
+        }
+      }
+
+      /* Add contribution to the .ed of 'to' */
+      if (me != to) {
+        for (k=0; k<myrinfo->ndegrees; k++) {
+          if (myedegrees[k].pid == to) {
+            myedegrees[k].ed += adjwgt[j];
+            break;
+          }
+        }
+        if (k == myrinfo->ndegrees) {
+          myedegrees[myrinfo->ndegrees].pid = to;
+          myedegrees[myrinfo->ndegrees++].ed = adjwgt[j];
+        }
+      }
+
+      /* Update pmat to reflect the move of 'i' for domains other than 'from' and 'to' */
+      if (me != from && me != to) {
+        pmat[me*nparts+from] -= adjwgt[j];
+        pmat[from*nparts+me] -= adjwgt[j];
+        if (pmat[me*nparts+from] == 0) 
+          ndoms[me]--;
+        if (pmat[from*nparts+me] == 0) 
+          ndoms[from]--;
+
+        if (pmat[me*nparts+to] == 0) 
+          ndoms[me]++;
+        if (pmat[to*nparts+me] == 0) 
+          ndoms[to]++;
+
+        pmat[me*nparts+to] += adjwgt[j];
+        pmat[to*nparts+me] += adjwgt[j];
+      }
+
+      ASSERT(CheckRInfo(myrinfo));
+    }
+
+    ASSERT(CheckRInfo(graph->rinfo+i));
+  }
+
+  graph->nbnd = nbnd;
+
+}
+
+
+
+
+/*************************************************************************
+* This function finds all the connected components induced by the 
+* partitioning vector in wgraph->where and tries to push them around to 
+* remove some of them
+**************************************************************************/
+void EliminateComponents(CtrlType *ctrl, GraphType *graph, int nparts, float *tpwgts, float ubfactor)
+{
+  int i, ii, j, jj, k, me, nvtxs, tvwgt, first, last, nleft, ncmps, cwgt, other, target, deltawgt;
+  idxtype *xadj, *adjncy, *vwgt, *adjwgt, *where, *pwgts, *maxpwgt;
+  idxtype *cpvec, *touched, *perm, *todo, *cind, *cptr, *npcmps;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  vwgt = graph->vwgt;
+  adjwgt = graph->adjwgt;
+
+  where = graph->where;
+  pwgts = graph->pwgts;
+
+  touched = idxset(nvtxs, 0, idxwspacemalloc(ctrl, nvtxs));
+  cptr = idxwspacemalloc(ctrl, nvtxs);
+  cind = idxwspacemalloc(ctrl, nvtxs);
+  perm = idxwspacemalloc(ctrl, nvtxs);
+  todo = idxwspacemalloc(ctrl, nvtxs);
+  maxpwgt = idxwspacemalloc(ctrl, nparts);
+  cpvec = idxwspacemalloc(ctrl, nparts);
+  npcmps = idxset(nparts, 0, idxwspacemalloc(ctrl, nparts));
+
+  for (i=0; i<nvtxs; i++) 
+    perm[i] = todo[i] = i;
+
+  /* Find the connected componends induced by the partition */
+  ncmps = -1;
+  first = last = 0;
+  nleft = nvtxs;
+  while (nleft > 0) {
+    if (first == last) { /* Find another starting vertex */
+      cptr[++ncmps] = first;
+      ASSERT(touched[todo[0]] == 0);
+      i = todo[0];
+      cind[last++] = i;
+      touched[i] = 1;
+      me = where[i];
+      npcmps[me]++;
+    }
+
+    i = cind[first++];
+    k = perm[i];
+    j = todo[k] = todo[--nleft];
+    perm[j] = k;
+
+    for (j=xadj[i]; j<xadj[i+1]; j++) {
+      k = adjncy[j];
+      if (where[k] == me && !touched[k]) {
+        cind[last++] = k;
+        touched[k] = 1;
+      }
+    }
+  }
+  cptr[++ncmps] = first;
+
+  /* printf("I found %d components, for this %d-way partition\n", ncmps, nparts); */
+
+  if (ncmps > nparts) { /* There are more components than processors */
+    /* First determine the max allowed load imbalance */
+    tvwgt = idxsum(nparts, pwgts);
+    for (i=0; i<nparts; i++)
+      maxpwgt[i] = ubfactor*tpwgts[i]*tvwgt;
+
+    deltawgt = 5;
+
+    for (i=0; i<ncmps; i++) {
+      me = where[cind[cptr[i]]];  /* Get the domain of this component */
+      if (npcmps[me] == 1)
+        continue;  /* Skip it because it is contigous */
+
+      /*printf("Trying to move %d from %d\n", i, me); */
+
+      /* Determine the weight of the block to be moved and abort if too high */
+      for (cwgt=0, j=cptr[i]; j<cptr[i+1]; j++) 
+        cwgt += vwgt[cind[j]];
+
+      if (cwgt > .30*pwgts[me])
+        continue;  /* Skip the component if it is over 30% of the weight */
+
+      /* Determine the connectivity */
+      idxset(nparts, 0, cpvec);
+      for (j=cptr[i]; j<cptr[i+1]; j++) {
+        ii = cind[j];
+        for (jj=xadj[ii]; jj<xadj[ii+1]; jj++) 
+          cpvec[where[adjncy[jj]]] += adjwgt[jj];
+      }
+      cpvec[me] = 0;
+
+      target = -1;
+      for (j=0; j<nparts; j++) {
+        if (cpvec[j] > 0 && (cwgt < deltawgt || pwgts[j] + cwgt < maxpwgt[j])) {
+          if (target == -1 || cpvec[target] < cpvec[j])
+            target = j;
+        }
+      }
+
+      /* printf("\tMoving it to %d [%d]\n", target, cpvec[target]);*/
+
+      if (target != -1) {
+        /* Assign all the vertices of 'me' to 'target' and update data structures */
+        INC_DEC(pwgts[target], pwgts[me], cwgt);
+        npcmps[me]--;
+
+        MoveGroup(ctrl, graph, nparts, target, i, cptr, cind);
+      }
+    }
+
+  }
+
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nparts);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+  idxwspacefree(ctrl, nvtxs);
+
+}
+
+
+/*************************************************************************
+* This function moves a collection of vertices and updates their rinfo
+**************************************************************************/
+void MoveGroup(CtrlType *ctrl, GraphType *graph, int nparts, int to, int gid, idxtype *ptr, idxtype *ind)
+{
+  int i, ii, iii, j, jj, k, l, nvtxs, nbnd, myndegrees; 
+  int from, me;
+  idxtype *xadj, *adjncy, *adjwgt;
+  idxtype *where, *bndptr, *bndind;
+  EDegreeType *myedegrees;
+  RInfoType *myrinfo;
+
+  nvtxs = graph->nvtxs;
+  xadj = graph->xadj;
+  adjncy = graph->adjncy;
+  adjwgt = graph->adjwgt;
+
+  where = graph->where;
+  bndptr = graph->bndptr;
+  bndind = graph->bndind;
+
+  nbnd = graph->nbnd;
+
+  for (iii=ptr[gid]; iii<ptr[gid+1]; iii++) {
+    i = ind[iii];
+    from = where[i];
+
+    myrinfo = graph->rinfo+i;
+    if (myrinfo->edegrees == NULL) {
+      myrinfo->edegrees = ctrl->wspace.edegrees+ctrl->wspace.cdegree;
+      ctrl->wspace.cdegree += xadj[i+1]-xadj[i];
+      myrinfo->ndegrees = 0;
+    }
+    myedegrees = myrinfo->edegrees;
+
+    /* find the location of 'to' in myrinfo or create it if it is not there */
+    for (k=0; k<myrinfo->ndegrees; k++) {
+      if (myedegrees[k].pid == to)
+        break;
+    }
+    if (k == myrinfo->ndegrees) {
+      myedegrees[k].pid = to;
+      myedegrees[k].ed = 0;
+      myrinfo->ndegrees++;
+    }
+
+    graph->mincut -= myedegrees[k].ed-myrinfo->id;
+
+
+    /* Update where, weight, and ID/ED information of the vertex you moved */
+    where[i] = to;
+    myrinfo->ed += myrinfo->id-myedegrees[k].ed;
+    SWAP(myrinfo->id, myedegrees[k].ed, j);
+    if (myedegrees[k].ed == 0) 
+      myedegrees[k] = myedegrees[--myrinfo->ndegrees];
+    else
+      myedegrees[k].pid = from;
+
+    if (myrinfo->ed-myrinfo->id < 0 && bndptr[i] != -1)
+      BNDDelete(nbnd, bndind, bndptr, i);
+
+    /* Update the degrees of adjacent vertices */
+    for (j=xadj[i]; j<xadj[i+1]; j++) {
+      ii = adjncy[j];
+      me = where[ii];
+
+      myrinfo = graph->rinfo+ii;
+      if (myrinfo->edegrees == NULL) {
+        myrinfo->edegrees = ctrl->wspace.edegrees+ctrl->wspace.cdegree;
+        ctrl->wspace.cdegree += xadj[ii+1]-xadj[ii];
+      }
+      myedegrees = myrinfo->edegrees;
+
+      ASSERT(CheckRInfo(myrinfo));
+
+      if (me == from) {
+        INC_DEC(myrinfo->ed, myrinfo->id, adjwgt[j]);
+
+        if (myrinfo->ed-myrinfo->id >= 0 && bndptr[ii] == -1)
+          BNDInsert(nbnd, bndind, bndptr, ii);
+      }
+      else if (me == to) {
+        INC_DEC(myrinfo->id, myrinfo->ed, adjwgt[j]);
+
+        if (myrinfo->ed-myrinfo->id < 0 && bndptr[ii] != -1)
+          BNDDelete(nbnd, bndind, bndptr, ii);
+      }
+
+      /* Remove contribution from the .ed of 'from' */
+      if (me != from) {
+        for (k=0; k<myrinfo->ndegrees; k++) {
+          if (myedegrees[k].pid == from) {
+            if (myedegrees[k].ed == adjwgt[j])
+              myedegrees[k] = myedegrees[--myrinfo->ndegrees];
+            else
+              myedegrees[k].ed -= adjwgt[j];
+            break;
+          }
+        }
+      }
+
+      /* Add contribution to the .ed of 'to' */
+      if (me != to) {
+        for (k=0; k<myrinfo->ndegrees; k++) {
+          if (myedegrees[k].pid == to) {
+            myedegrees[k].ed += adjwgt[j];
+            break;
+          }
+        }
+        if (k == myrinfo->ndegrees) {
+          myedegrees[myrinfo->ndegrees].pid = to;
+          myedegrees[myrinfo->ndegrees++].ed = adjwgt[j];
+        }
+      }
+
+      ASSERT(CheckRInfo(myrinfo));
+    }
+
+    ASSERT(CheckRInfo(graph->rinfo+i));
+  }
+
+  graph->nbnd = nbnd;
+
+}
+
diff --git a/contrib/Metis/timing.c b/contrib/Metis/timing.c
new file mode 100644
index 0000000000..c8ded4a171
--- /dev/null
+++ b/contrib/Metis/timing.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * timing.c
+ *
+ * This file contains routines that deal with timing Metis
+ *
+ * Started 7/24/97
+ * George
+ *
+ * $Id: timing.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ *
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function clears the timers
+**************************************************************************/
+void InitTimers(CtrlType *ctrl)
+{
+  cleartimer(ctrl->TotalTmr);
+  cleartimer(ctrl->InitPartTmr);
+  cleartimer(ctrl->MatchTmr);
+  cleartimer(ctrl->ContractTmr);
+  cleartimer(ctrl->CoarsenTmr);
+  cleartimer(ctrl->UncoarsenTmr);
+  cleartimer(ctrl->RefTmr);
+  cleartimer(ctrl->ProjectTmr);
+  cleartimer(ctrl->SplitTmr);
+  cleartimer(ctrl->SepTmr);
+  cleartimer(ctrl->AuxTmr1);
+  cleartimer(ctrl->AuxTmr2);
+  cleartimer(ctrl->AuxTmr3);
+  cleartimer(ctrl->AuxTmr4);
+  cleartimer(ctrl->AuxTmr5);
+  cleartimer(ctrl->AuxTmr6);
+}
+
+
+
+/*************************************************************************
+* This function prints the various timers
+**************************************************************************/
+void PrintTimers(CtrlType *ctrl)
+{
+  printf("\nTiming Information -------------------------------------------------");
+  printf("\n Multilevel: \t\t %7.3f", gettimer(ctrl->TotalTmr));
+  printf("\n     Coarsening: \t\t %7.3f", gettimer(ctrl->CoarsenTmr));
+  printf("\n            Matching: \t\t\t %7.3f", gettimer(ctrl->MatchTmr));
+  printf("\n            Contract: \t\t\t %7.3f", gettimer(ctrl->ContractTmr));
+  printf("\n     Initial Partition: \t %7.3f", gettimer(ctrl->InitPartTmr));
+  printf("\n   Construct Separator: \t %7.3f", gettimer(ctrl->SepTmr));
+  printf("\n     Uncoarsening: \t\t %7.3f", gettimer(ctrl->UncoarsenTmr));
+  printf("\n          Refinement: \t\t\t %7.3f", gettimer(ctrl->RefTmr));
+  printf("\n          Projection: \t\t\t %7.3f", gettimer(ctrl->ProjectTmr));
+  printf("\n     Splitting: \t\t %7.3f", gettimer(ctrl->SplitTmr));
+  printf("\n          AUX1: \t\t %7.3f", gettimer(ctrl->AuxTmr1));
+  printf("\n          AUX2: \t\t %7.3f", gettimer(ctrl->AuxTmr2));
+  printf("\n          AUX3: \t\t %7.3f", gettimer(ctrl->AuxTmr3));
+  printf("\n********************************************************************\n");
+}
+
+
+/*************************************************************************
+* This function returns the seconds
+**************************************************************************/
+double seconds(void)
+{
+  return((double) clock()/CLOCKS_PER_SEC);
+}
+
+
diff --git a/contrib/Metis/util.c b/contrib/Metis/util.c
new file mode 100644
index 0000000000..3eb87deb1e
--- /dev/null
+++ b/contrib/Metis/util.c
@@ -0,0 +1,519 @@
+/*
+ * Copyright 1997, Regents of the University of Minnesota
+ *
+ * util.c
+ *
+ * This function contains various utility routines
+ *
+ * Started 9/28/95
+ * George
+ *
+ * $Id: util.c,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ */
+
+#include <metis.h>
+
+
+/*************************************************************************
+* This function prints an error message and exits
+**************************************************************************/
+void errexit(char *f_str,...)
+{
+  va_list argp;
+  char out1[256], out2[256];
+
+  va_start(argp, f_str);
+  vsprintf(out1, f_str, argp);
+  va_end(argp);
+
+  sprintf(out2, "Error! %s", out1);
+
+  fprintf(stdout, out2);
+  fflush(stdout);
+
+  abort();
+}
+
+
+
+#ifndef DMALLOC
+/*************************************************************************
+* The following function allocates an array of integers
+**************************************************************************/
+int *imalloc(int n, char *msg)
+{
+  if (n == 0)
+    return NULL;
+
+  return (int *)GKmalloc(sizeof(int)*n, msg);
+}
+
+
+/*************************************************************************
+* The following function allocates an array of integers
+**************************************************************************/
+idxtype *idxmalloc(int n, char *msg)
+{
+  if (n == 0)
+    return NULL;
+
+  return (idxtype *)GKmalloc(sizeof(idxtype)*n, msg);
+}
+
+
+/*************************************************************************
+* The following function allocates an array of float 
+**************************************************************************/
+float *fmalloc(int n, char *msg)
+{
+  if (n == 0)
+    return NULL;
+
+  return (float *)GKmalloc(sizeof(float)*n, msg);
+}
+
+
+/*************************************************************************
+* The follwoing function allocates an array of integers
+**************************************************************************/
+int *ismalloc(int n, int ival, char *msg)
+{
+  if (n == 0)
+    return NULL;
+
+  return iset(n, ival, (int *)GKmalloc(sizeof(int)*n, msg));
+}
+
+
+
+/*************************************************************************
+* The follwoing function allocates an array of integers
+**************************************************************************/
+idxtype *idxsmalloc(int n, idxtype ival, char *msg)
+{
+  if (n == 0)
+    return NULL;
+
+  return idxset(n, ival, (idxtype *)GKmalloc(sizeof(idxtype)*n, msg));
+}
+
+
+/*************************************************************************
+* This function is my wrapper around malloc
+**************************************************************************/
+void *GKmalloc(int nbytes, char *msg)
+{
+  void *ptr;
+
+  if (nbytes == 0)
+    return NULL;
+
+  ptr = (void *)malloc(nbytes);
+  if (ptr == NULL) 
+    errexit("***Memory allocation failed for %s. Requested size: %d bytes", msg, nbytes);
+
+  return ptr;
+}
+#endif
+
+/*************************************************************************
+* This function is my wrapper around free, allows multiple pointers    
+**************************************************************************/
+void GKfree(void **ptr1,...)
+{
+  va_list plist;
+  void **ptr;
+
+  if (*ptr1 != NULL)
+    free(*ptr1);
+  *ptr1 = NULL;
+
+  va_start(plist, ptr1);
+
+  /* while ((int)(ptr = va_arg(plist, void **)) != -1) { */
+  while ((ptr = va_arg(plist, void **)) != LTERM) {
+    if (*ptr != NULL)
+      free(*ptr);
+    *ptr = NULL;
+  }
+
+  va_end(plist);
+}            
+
+
+/*************************************************************************
+* These functions set the values of a vector
+**************************************************************************/
+int *iset(int n, int val, int *x)
+{
+  int i;
+
+  for (i=0; i<n; i++)
+    x[i] = val;
+
+  return x;
+}
+
+
+/*************************************************************************
+* These functions set the values of a vector
+**************************************************************************/
+idxtype *idxset(int n, idxtype val, idxtype *x)
+{
+  int i;
+
+  for (i=0; i<n; i++)
+    x[i] = val;
+
+  return x;
+}
+
+
+/*************************************************************************
+* These functions set the values of a vector
+**************************************************************************/
+float *sset(int n, float val, float *x)
+{
+  int i;
+
+  for (i=0; i<n; i++)
+    x[i] = val;
+
+  return x;
+}
+
+
+
+/*************************************************************************
+* These functions return the index of the maximum element in a vector
+**************************************************************************/
+int iamax(int n, int *x)
+{
+  int i, max=0;
+
+  for (i=1; i<n; i++)
+    max = (x[i] > x[max] ? i : max);
+
+  return max;
+}
+
+
+/*************************************************************************
+* These functions return the index of the maximum element in a vector
+**************************************************************************/
+int idxamax(int n, idxtype *x)
+{
+  int i, max=0;
+
+  for (i=1; i<n; i++)
+    max = (x[i] > x[max] ? i : max);
+
+  return max;
+}
+
+/*************************************************************************
+* These functions return the index of the maximum element in a vector
+**************************************************************************/
+int idxamax_strd(int n, idxtype *x, int incx)
+{
+  int i, max=0;
+
+  n *= incx;
+  for (i=incx; i<n; i+=incx)
+    max = (x[i] > x[max] ? i : max);
+
+  return max/incx;
+}
+
+
+
+/*************************************************************************
+* These functions return the index of the maximum element in a vector
+**************************************************************************/
+int samax(int n, float *x)
+{
+  int i, max=0;
+
+  for (i=1; i<n; i++)
+    max = (x[i] > x[max] ? i : max);
+
+  return max;
+}
+
+/*************************************************************************
+* These functions return the index of the almost maximum element in a vector
+**************************************************************************/
+int samax2(int n, float *x)
+{
+  int i, max1, max2;
+
+  if (x[0] > x[1]) {
+    max1 = 0;
+    max2 = 1;
+  }
+  else {
+    max1 = 1;
+    max2 = 0;
+  }
+
+  for (i=2; i<n; i++) {
+    if (x[i] > x[max1]) {
+      max2 = max1;
+      max1 = i;
+    }
+    else if (x[i] > x[max2])
+      max2 = i;
+  }
+
+  return max2;
+}
+
+
+/*************************************************************************
+* These functions return the index of the minimum element in a vector
+**************************************************************************/
+int idxamin(int n, idxtype *x)
+{
+  int i, min=0;
+
+  for (i=1; i<n; i++)
+    min = (x[i] < x[min] ? i : min);
+
+  return min;
+}
+
+
+/*************************************************************************
+* These functions return the index of the minimum element in a vector
+**************************************************************************/
+int samin(int n, float *x)
+{
+  int i, min=0;
+
+  for (i=1; i<n; i++)
+    min = (x[i] < x[min] ? i : min);
+
+  return min;
+}
+
+
+/*************************************************************************
+* This function sums the entries in an array
+**************************************************************************/
+int idxsum(int n, idxtype *x)
+{
+  int i, sum = 0;
+
+  for (i=0; i<n; i++)
+    sum += x[i];
+
+  return sum;
+}
+
+
+/*************************************************************************
+* This function sums the entries in an array
+**************************************************************************/
+int idxsum_strd(int n, idxtype *x, int incx)
+{
+  int i, sum = 0;
+
+  for (i=0; i<n; i++, x+=incx) {
+    sum += *x;
+  }
+
+  return sum;
+}
+
+
+/*************************************************************************
+* This function sums the entries in an array
+**************************************************************************/
+void idxadd(int n, idxtype *x, idxtype *y)
+{
+  for (n--; n>=0; n--)
+    y[n] += x[n];
+}
+
+
+/*************************************************************************
+* This function sums the entries in an array
+**************************************************************************/
+int charsum(int n, char *x)
+{
+  int i, sum = 0;
+
+  for (i=0; i<n; i++)
+    sum += x[i];
+
+  return sum;
+}
+
+/*************************************************************************
+* This function sums the entries in an array
+**************************************************************************/
+int isum(int n, int *x)
+{
+  int i, sum = 0;
+
+  for (i=0; i<n; i++)
+    sum += x[i];
+
+  return sum;
+}
+
+/*************************************************************************
+* This function sums the entries in an array
+**************************************************************************/
+float ssum(int n, float *x)
+{
+  int i;
+  float sum = 0.0;
+
+  for (i=0; i<n; i++)
+    sum += x[i];
+
+  return sum;
+}
+
+/*************************************************************************
+* This function sums the entries in an array
+**************************************************************************/
+float ssum_strd(int n, float *x, int incx)
+{
+  int i;
+  float sum = 0.0;
+
+  for (i=0; i<n; i++, x+=incx)
+    sum += *x;
+
+  return sum;
+}
+
+/*************************************************************************
+* This function sums the entries in an array
+**************************************************************************/
+void sscale(int n, float alpha, float *x)
+{
+  int i;
+
+  for (i=0; i<n; i++)
+    x[i] *= alpha;
+}
+
+
+/*************************************************************************
+* This function computes a 2-norm
+**************************************************************************/
+float snorm2(int n, float *v)
+{
+  int i;
+  float partial = 0;
+ 
+  for (i = 0; i<n; i++)
+    partial += v[i] * v[i];
+
+  return sqrt(partial);
+}
+
+
+
+/*************************************************************************
+* This function computes a 2-norm
+**************************************************************************/
+float sdot(int n, float *x, float *y)
+{
+  int i;
+  float partial = 0;
+ 
+  for (i = 0; i<n; i++)
+    partial += x[i] * y[i];
+
+  return partial;
+}
+
+
+/*************************************************************************
+* This function computes a 2-norm
+**************************************************************************/
+void saxpy(int n, float alpha, float *x, int incx, float *y, int incy)
+{
+  int i;
+ 
+  for (i=0; i<n; i++, x+=incx, y+=incy) 
+    *y += alpha*(*x);
+}
+
+
+
+
+/*************************************************************************
+* This file randomly permutes the contents of an array.
+* flag == 0, don't initialize perm
+* flag == 1, set p[i] = i 
+**************************************************************************/
+void RandomPermute(int n, idxtype *p, int flag)
+{
+  int i, u, v;
+  idxtype tmp;
+
+  if (flag == 1) {
+    for (i=0; i<n; i++)
+      p[i] = i;
+  }
+
+  if (n <= 4)
+    return;
+
+  for (i=0; i<n; i+=16) {
+    u = RandomInRangeFast(n-4);
+    v = RandomInRangeFast(n-4);
+    SWAP(p[v], p[u], tmp);
+    SWAP(p[v+1], p[u+1], tmp);
+    SWAP(p[v+2], p[u+2], tmp);
+    SWAP(p[v+3], p[u+3], tmp);
+  }
+}
+
+
+
+/*************************************************************************
+* This function returns true if the a is a power of 2
+**************************************************************************/
+int ispow2(int a)
+{
+  for (; a%2 != 1; a = a>>1);
+  return (a > 1 ? 0 : 1);
+}
+
+
+/*************************************************************************
+* This function initializes the random number generator
+**************************************************************************/
+void InitRandom(int seed)
+{
+  if (seed == -1) {
+#ifndef __VC__
+    srand48(7654321L);  
+#endif
+    srand(4321);  
+  }
+  else {
+#ifndef __VC__
+    srand48(seed);  
+#endif
+    srand(seed);  
+  }
+}
+
+/*************************************************************************
+* This function returns the log2(x)
+**************************************************************************/
+int log2(int a)
+{
+  int i;
+
+  for (i=1; a > 1; i++, a = a>>1);
+  return i-1;
+}
+
diff --git a/contrib/NR/Makefile b/contrib/NR/Makefile
new file mode 100644
index 0000000000..4944e04f46
--- /dev/null
+++ b/contrib/NR/Makefile
@@ -0,0 +1,77 @@
+# $Id: Makefile,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+#
+# Copyright (C) 1997-2005 C. Geuzaine, J.-F. Remacle
+#
+# 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.
+# 
+# Please report all bugs and problems to <gmsh@geuz.org>.
+
+include ../../variables
+
+LIB     = ../../lib/libGmshNR.a
+INCLUDE = -I../../Common -I../../DataStr -I../../Numeric
+# don't optimize this library: there are some problems with gcc...
+OPTIM = -O0
+CFLAGS  = ${OPTIM} ${FLAGS} ${INCLUDE} 
+
+SRC = brent.cpp\
+      dpythag.cpp\
+      dsvdcmp.cpp\
+      fdjac.cpp\
+      fmin.cpp\
+      lnsrch.cpp\
+      lubksb.cpp\
+      ludcmp.cpp\
+      mnbrak.cpp\
+      newt.cpp\
+      nrutil.cpp
+
+OBJ = ${SRC:.cpp=.o}
+
+.SUFFIXES: .o .cpp
+
+${LIB}: ${OBJ}
+	${AR} ${LIB} ${OBJ}
+	${RANLIB} ${LIB}
+
+.cpp.o:
+	${CXX} ${CFLAGS} -c $<
+
+clean:
+	rm -f *.o 
+
+depend:
+	(sed '/^# DO NOT DELETE THIS LINE/q' Makefile && \
+	${CXX} -MM ${CFLAGS} ${SRC} \
+	) >Makefile.new
+	cp Makefile Makefile.bak
+	cp Makefile.new Makefile
+	rm -f Makefile.new
+
+# DO NOT DELETE THIS LINE
+brent.o: brent.cpp nrutil.h ../Numeric/Numeric.h
+dpythag.o: dpythag.cpp nrutil.h ../Numeric/Numeric.h
+dsvdcmp.o: dsvdcmp.cpp nrutil.h ../Numeric/Numeric.h
+fdjac.o: fdjac.cpp nrutil.h ../Numeric/Numeric.h
+fmin.o: fmin.cpp nrutil.h ../Numeric/Numeric.h
+lnsrch.o: lnsrch.cpp nrutil.h ../Numeric/Numeric.h
+lubksb.o: lubksb.cpp
+ludcmp.o: ludcmp.cpp nrutil.h ../Numeric/Numeric.h
+mnbrak.o: mnbrak.cpp nrutil.h ../Numeric/Numeric.h
+newt.o: newt.cpp nrutil.h ../Numeric/Numeric.h
+nrutil.o: nrutil.cpp ../Common/Gmsh.h ../Common/Message.h \
+  ../DataStr/Malloc.h ../DataStr/List.h ../DataStr/Tree.h \
+  ../DataStr/avl.h ../DataStr/Tools.h
diff --git a/contrib/NR/brent.cpp b/contrib/NR/brent.cpp
new file mode 100644
index 0000000000..8dc82e8813
--- /dev/null
+++ b/contrib/NR/brent.cpp
@@ -0,0 +1,77 @@
+#include <math.h>
+#define NRANSI
+#include "nrutil.h"
+#define ITMAX 100
+#define CGOLD 0.3819660
+#define ZEPS 1.0e-10
+#define SHFT(a,b,c,d) (a)=(b);(b)=(c);(c)=(d);
+
+/* This file has been modified for inclusion in Gmsh (float->double) */
+
+double brent(double ax, double bx, double cx, double (*f)(double), double tol,
+	double *xmin)
+{
+	int iter;
+	double a,b,d,etemp,fu,fv,fw,fx,p,q,r,tol1,tol2,u,v,w,x,xm;
+	double e=0.0;
+
+	a=(ax < cx ? ax : cx);
+	b=(ax > cx ? ax : cx);
+	x=w=v=bx;
+	fw=fv=fx=(*f)(x);
+	for (iter=1;iter<=ITMAX;iter++) {
+		xm=0.5*(a+b);
+		tol2=2.0*(tol1=tol*fabs(x)+ZEPS);
+		if (fabs(x-xm) <= (tol2-0.5*(b-a))) {
+			*xmin=x;
+			return fx;
+		}
+		if (fabs(e) > tol1) {
+			r=(x-w)*(fx-fv);
+			q=(x-v)*(fx-fw);
+			p=(x-v)*q-(x-w)*r;
+			q=2.0*(q-r);
+			if (q > 0.0) p = -p;
+			q=fabs(q);
+			etemp=e;
+			e=d;
+			if (fabs(p) >= fabs(0.5*q*etemp) || p <= q*(a-x) || p >= q*(b-x))
+				d=CGOLD*(e=(x >= xm ? a-x : b-x));
+			else {
+				d=p/q;
+				u=x+d;
+				if (u-a < tol2 || b-u < tol2)
+					d=SIGN(tol1,xm-x);
+			}
+		} else {
+			d=CGOLD*(e=(x >= xm ? a-x : b-x));
+		}
+		u=(fabs(d) >= tol1 ? x+d : x+SIGN(tol1,d));
+		fu=(*f)(u);
+		if (fu <= fx) {
+			if (u >= x) a=x; else b=x;
+			SHFT(v,w,x,u)
+			SHFT(fv,fw,fx,fu)
+		} else {
+			if (u < x) a=u; else b=u;
+			if (fu <= fw || w == x) {
+				v=w;
+				w=u;
+				fv=fw;
+				fw=fu;
+			} else if (fu <= fv || v == x || v == w) {
+				v=u;
+				fv=fu;
+			}
+		}
+	}
+	nrerror("Too many iterations in brent");
+	*xmin=x;
+	return fx;
+}
+#undef ITMAX
+#undef CGOLD
+#undef ZEPS
+#undef SHFT
+#undef NRANSI
+/* (C) Copr. 1986-92 Numerical Recipes Software J!0. */
diff --git a/contrib/NR/dpythag.cpp b/contrib/NR/dpythag.cpp
new file mode 100644
index 0000000000..4585295c43
--- /dev/null
+++ b/contrib/NR/dpythag.cpp
@@ -0,0 +1,14 @@
+#include <math.h>
+#define NRANSI
+#include "nrutil.h"
+
+double dpythag(double a, double b)
+{
+	double absa,absb;
+	absa=fabs(a);
+	absb=fabs(b);
+	if (absa > absb) return absa*sqrt(1.0+DSQR(absb/absa));
+	else return (absb == 0.0 ? 0.0 : absb*sqrt(1.0+DSQR(absa/absb)));
+}
+#undef NRANSI
+/* (C) Copr. 1986-92 Numerical Recipes Software J!0. */
diff --git a/contrib/NR/dsvdcmp.cpp b/contrib/NR/dsvdcmp.cpp
new file mode 100644
index 0000000000..81317ec7db
--- /dev/null
+++ b/contrib/NR/dsvdcmp.cpp
@@ -0,0 +1,183 @@
+#include <math.h>
+#define NRANSI
+#include "nrutil.h"
+
+void dsvdcmp(double **a, int m, int n, double w[], double **v)
+{
+	double dpythag(double a, double b);
+	int flag,i,its,j,jj,k,l,nm;
+	double anorm,c,f,g,h,s,scale,x,y,z,*rv1;
+
+	rv1=dvector(1,n);
+	g=scale=anorm=0.0;
+	for (i=1;i<=n;i++) {
+		l=i+1;
+		rv1[i]=scale*g;
+		g=s=scale=0.0;
+		if (i <= m) {
+			for (k=i;k<=m;k++) scale += fabs(a[k][i]);
+			if (scale) {
+				for (k=i;k<=m;k++) {
+					a[k][i] /= scale;
+					s += a[k][i]*a[k][i];
+				}
+				f=a[i][i];
+				g = -SIGN(sqrt(s),f);
+				h=f*g-s;
+				a[i][i]=f-g;
+				for (j=l;j<=n;j++) {
+					for (s=0.0,k=i;k<=m;k++) s += a[k][i]*a[k][j];
+					f=s/h;
+					for (k=i;k<=m;k++) a[k][j] += f*a[k][i];
+				}
+				for (k=i;k<=m;k++) a[k][i] *= scale;
+			}
+		}
+		w[i]=scale *g;
+		g=s=scale=0.0;
+		if (i <= m && i != n) {
+			for (k=l;k<=n;k++) scale += fabs(a[i][k]);
+			if (scale) {
+				for (k=l;k<=n;k++) {
+					a[i][k] /= scale;
+					s += a[i][k]*a[i][k];
+				}
+				f=a[i][l];
+				g = -SIGN(sqrt(s),f);
+				h=f*g-s;
+				a[i][l]=f-g;
+				for (k=l;k<=n;k++) rv1[k]=a[i][k]/h;
+				for (j=l;j<=m;j++) {
+					for (s=0.0,k=l;k<=n;k++) s += a[j][k]*a[i][k];
+					for (k=l;k<=n;k++) a[j][k] += s*rv1[k];
+				}
+				for (k=l;k<=n;k++) a[i][k] *= scale;
+			}
+		}
+		anorm=DMAX(anorm,(fabs(w[i])+fabs(rv1[i])));
+	}
+	for (i=n;i>=1;i--) {
+		if (i < n) {
+			if (g) {
+				for (j=l;j<=n;j++) v[j][i]=(a[i][j]/a[i][l])/g;
+				for (j=l;j<=n;j++) {
+					for (s=0.0,k=l;k<=n;k++) s += a[i][k]*v[k][j];
+					for (k=l;k<=n;k++) v[k][j] += s*v[k][i];
+				}
+			}
+			for (j=l;j<=n;j++) v[i][j]=v[j][i]=0.0;
+		}
+		v[i][i]=1.0;
+		g=rv1[i];
+		l=i;
+	}
+	for (i=IMIN(m,n);i>=1;i--) {
+		l=i+1;
+		g=w[i];
+		for (j=l;j<=n;j++) a[i][j]=0.0;
+		if (g) {
+			g=1.0/g;
+			for (j=l;j<=n;j++) {
+				for (s=0.0,k=l;k<=m;k++) s += a[k][i]*a[k][j];
+				f=(s/a[i][i])*g;
+				for (k=i;k<=m;k++) a[k][j] += f*a[k][i];
+			}
+			for (j=i;j<=m;j++) a[j][i] *= g;
+		} else for (j=i;j<=m;j++) a[j][i]=0.0;
+		++a[i][i];
+	}
+	for (k=n;k>=1;k--) {
+		for (its=1;its<=30;its++) {
+			flag=1;
+			for (l=k;l>=1;l--) {
+				nm=l-1;
+				if ((double)(fabs(rv1[l])+anorm) == anorm) {
+					flag=0;
+					break;
+				}
+				if ((double)(fabs(w[nm])+anorm) == anorm) break;
+			}
+			if (flag) {
+				c=0.0;
+				s=1.0;
+				for (i=l;i<=k;i++) {
+					f=s*rv1[i];
+					rv1[i]=c*rv1[i];
+					if ((double)(fabs(f)+anorm) == anorm) break;
+					g=w[i];
+					h=dpythag(f,g);
+					w[i]=h;
+					h=1.0/h;
+					c=g*h;
+					s = -f*h;
+					for (j=1;j<=m;j++) {
+						y=a[j][nm];
+						z=a[j][i];
+						a[j][nm]=y*c+z*s;
+						a[j][i]=z*c-y*s;
+					}
+				}
+			}
+			z=w[k];
+			if (l == k) {
+				if (z < 0.0) {
+					w[k] = -z;
+					for (j=1;j<=n;j++) v[j][k] = -v[j][k];
+				}
+				break;
+			}
+			if (its == 30) nrerror("no convergence in 30 dsvdcmp iterations");
+			x=w[l];
+			nm=k-1;
+			y=w[nm];
+			g=rv1[nm];
+			h=rv1[k];
+			f=((y-z)*(y+z)+(g-h)*(g+h))/(2.0*h*y);
+			g=dpythag(f,1.0);
+			f=((x-z)*(x+z)+h*((y/(f+SIGN(g,f)))-h))/x;
+			c=s=1.0;
+			for (j=l;j<=nm;j++) {
+				i=j+1;
+				g=rv1[i];
+				y=w[i];
+				h=s*g;
+				g=c*g;
+				z=dpythag(f,h);
+				rv1[j]=z;
+				c=f/z;
+				s=h/z;
+				f=x*c+g*s;
+				g = g*c-x*s;
+				h=y*s;
+				y *= c;
+				for (jj=1;jj<=n;jj++) {
+					x=v[jj][j];
+					z=v[jj][i];
+					v[jj][j]=x*c+z*s;
+					v[jj][i]=z*c-x*s;
+				}
+				z=dpythag(f,h);
+				w[j]=z;
+				if (z) {
+					z=1.0/z;
+					c=f*z;
+					s=h*z;
+				}
+				f=c*g+s*y;
+				x=c*y-s*g;
+				for (jj=1;jj<=m;jj++) {
+					y=a[jj][j];
+					z=a[jj][i];
+					a[jj][j]=y*c+z*s;
+					a[jj][i]=z*c-y*s;
+				}
+			}
+			rv1[l]=0.0;
+			rv1[k]=f;
+			w[k]=x;
+		}
+	}
+	free_dvector(rv1,1,n);
+}
+#undef NRANSI
+/* (C) Copr. 1986-92 Numerical Recipes Software J!0. */
diff --git a/contrib/NR/fdjac.cpp b/contrib/NR/fdjac.cpp
new file mode 100644
index 0000000000..7767ff1bc4
--- /dev/null
+++ b/contrib/NR/fdjac.cpp
@@ -0,0 +1,29 @@
+#include <math.h>
+#define NRANSI
+#include "nrutil.h"
+#define EPS 1.0e-4
+
+/* This file has been modified for inclusion in Gmsh (float->double) */
+
+void fdjac(int n, double x[], double fvec[], double **df,
+	void (*vecfunc)(int, double [], double []))
+{
+	int i,j;
+	double h,temp,*f;
+
+	f=dvector(1,n);
+	for (j=1;j<=n;j++) {
+		temp=x[j];
+		h=EPS*fabs(temp);
+		if (h == 0.0) h=EPS;
+		x[j]=temp+h;
+		h=x[j]-temp;
+		(*vecfunc)(n,x,f);
+		x[j]=temp;
+		for (i=1;i<=n;i++) df[i][j]=(f[i]-fvec[i])/h;
+	}
+	free_dvector(f,1,n);
+}
+#undef EPS
+#undef NRANSI
+/* (C) Copr. 1986-92 Numerical Recipes Software J!0. */
diff --git a/contrib/NR/fmin.cpp b/contrib/NR/fmin.cpp
new file mode 100644
index 0000000000..f461aa7a05
--- /dev/null
+++ b/contrib/NR/fmin.cpp
@@ -0,0 +1,20 @@
+#define NRANSI
+#include "nrutil.h"
+
+/* This file has been modified for inclusion in Gmsh (float->double) */
+
+extern int nn;
+extern double *fvec;
+extern void (*nrfuncv)(int n, double v[], double f[]);
+
+double fmin(double x[])
+{
+	int i;
+	double sum;
+
+	(*nrfuncv)(nn,x,fvec);
+	for (sum=0.0,i=1;i<=nn;i++) sum += SQR(fvec[i]);
+	return 0.5*sum;
+}
+#undef NRANSI
+/* (C) Copr. 1986-92 Numerical Recipes Software J!0. */
diff --git a/contrib/NR/lnsrch.cpp b/contrib/NR/lnsrch.cpp
new file mode 100644
index 0000000000..e4a5346a5f
--- /dev/null
+++ b/contrib/NR/lnsrch.cpp
@@ -0,0 +1,65 @@
+#include <math.h>
+#define NRANSI
+#include "nrutil.h"
+#define ALF 1.0e-4
+#define TOLX 1.0e-7
+
+/* This file has been modified for inclusion in Gmsh (float->double) */
+
+void lnsrch(int n, double xold[], double fold, double g[], double p[], double x[],
+	double *f, double stpmax, int *check, double (*func)(double []))
+{
+	int i;
+	double a,alam,alam2,alamin,b,disc,f2,fold2,rhs1,rhs2,slope,sum,temp,
+		test,tmplam;
+
+	*check=0;
+	for (sum=0.0,i=1;i<=n;i++) sum += p[i]*p[i];
+	sum=sqrt(sum);
+	if (sum > stpmax)
+		for (i=1;i<=n;i++) p[i] *= stpmax/sum;
+	for (slope=0.0,i=1;i<=n;i++)
+		slope += g[i]*p[i];
+	test=0.0;
+	for (i=1;i<=n;i++) {
+		temp=fabs(p[i])/FMAX(fabs(xold[i]),1.0);
+		if (temp > test) test=temp;
+	}
+	alamin=TOLX/test;
+	alam=1.0;
+	for (;;) {
+		for (i=1;i<=n;i++) x[i]=xold[i]+alam*p[i];
+		*f=(*func)(x);
+		if (alam < alamin) {
+			for (i=1;i<=n;i++) x[i]=xold[i];
+			*check=1;
+			return;
+		} else if (*f <= fold+ALF*alam*slope) return;
+		else {
+			if (alam == 1.0)
+				tmplam = -slope/(2.0*(*f-fold-slope));
+			else {
+				rhs1 = *f-fold-alam*slope;
+				rhs2=f2-fold2-alam2*slope;
+				a=(rhs1/(alam*alam)-rhs2/(alam2*alam2))/(alam-alam2);
+				b=(-alam2*rhs1/(alam*alam)+alam*rhs2/(alam2*alam2))/(alam-alam2);
+				if (a == 0.0) tmplam = -slope/(2.0*b);
+				else {
+					disc=b*b-3.0*a*slope;
+					if (disc<0.0) nrerror("Roundoff problem in lnsrch.");
+					else tmplam=(-b+sqrt(disc))/(3.0*a);
+				}
+				if (tmplam>0.5*alam)
+					tmplam=0.5*alam;
+			}
+		}
+		alam2=alam;
+		f2 = *f;
+		fold2=fold;
+		alam=FMAX(tmplam,0.1*alam);
+	}
+}
+#undef ALF
+#undef TOLX
+#undef NRANSI
+/* (C) Copr. 1986-92 Numerical Recipes Software J!0. */
diff --git a/contrib/NR/lubksb.cpp b/contrib/NR/lubksb.cpp
new file mode 100644
index 0000000000..202c826e2f
--- /dev/null
+++ b/contrib/NR/lubksb.cpp
@@ -0,0 +1,23 @@
+/* This file has been modified for inclusion in Gmsh (float->double) */
+
+void lubksb(double **a, int n, int *indx, double b[])
+{
+	int i,ii=0,ip,j;
+	double sum;
+
+	for (i=1;i<=n;i++) {
+		ip=indx[i];
+		sum=b[ip];
+		b[ip]=b[i];
+		if (ii)
+			for (j=ii;j<=i-1;j++) sum -= a[i][j]*b[j];
+		else if (sum) ii=i;
+		b[i]=sum;
+	}
+	for (i=n;i>=1;i--) {
+		sum=b[i];
+		for (j=i+1;j<=n;j++) sum -= a[i][j]*b[j];
+		b[i]=sum/a[i][i];
+	}
+}
+/* (C) Copr. 1986-92 Numerical Recipes Software J!0. */
diff --git a/contrib/NR/ludcmp.cpp b/contrib/NR/ludcmp.cpp
new file mode 100644
index 0000000000..8f4e0c17fc
--- /dev/null
+++ b/contrib/NR/ludcmp.cpp
@@ -0,0 +1,60 @@
+#include <math.h>
+#define NRANSI
+#include "nrutil.h"
+#define TINY 1.0e-20;
+
+/* This file has been modified for inclusion in Gmsh (float->double) */
+
+void ludcmp(double **a, int n, int *indx, double *d)
+{
+	int i,imax,j,k;
+	double big,dum,sum,temp;
+	double *vv;
+
+	vv=dvector(1,n);
+	*d=1.0;
+	for (i=1;i<=n;i++) {
+		big=0.0;
+		for (j=1;j<=n;j++)
+			if ((temp=fabs(a[i][j])) > big) big=temp;
+		if (big == 0.0) nrerror("Singular matrix in routine ludcmp");
+		vv[i]=1.0/big;
+	}
+	for (j=1;j<=n;j++) {
+		for (i=1;i<j;i++) {
+			sum=a[i][j];
+			for (k=1;k<i;k++) sum -= a[i][k]*a[k][j];
+			a[i][j]=sum;
+		}
+		big=0.0;
+		for (i=j;i<=n;i++) {
+			sum=a[i][j];
+			for (k=1;k<j;k++)
+				sum -= a[i][k]*a[k][j];
+			a[i][j]=sum;
+			if ( (dum=vv[i]*fabs(sum)) >= big) {
+				big=dum;
+				imax=i;
+			}
+		}
+		if (j != imax) {
+			for (k=1;k<=n;k++) {
+				dum=a[imax][k];
+				a[imax][k]=a[j][k];
+				a[j][k]=dum;
+			}
+			*d = -(*d);
+			vv[imax]=vv[j];
+		}
+		indx[j]=imax;
+		if (a[j][j] == 0.0) a[j][j]=TINY;
+		if (j != n) {
+			dum=1.0/(a[j][j]);
+			for (i=j+1;i<=n;i++) a[i][j] *= dum;
+		}
+	}
+	free_dvector(vv,1,n);
+}
+#undef TINY
+#undef NRANSI
+/* (C) Copr. 1986-92 Numerical Recipes Software J!0. */
diff --git a/contrib/NR/mnbrak.cpp b/contrib/NR/mnbrak.cpp
new file mode 100644
index 0000000000..9aedccc886
--- /dev/null
+++ b/contrib/NR/mnbrak.cpp
@@ -0,0 +1,67 @@
+#include <math.h>
+#define NRANSI
+#include "nrutil.h"
+#define GOLD 1.618034
+#define GLIMIT 100.0
+#define TINY 1.0e-20
+#define SHFT(a,b,c,d) (a)=(b);(b)=(c);(c)=(d);
+
+/* This file has been modified for inclusion in Gmsh (float->double) */
+
+void mnbrak(double *ax, double *bx, double *cx, double *fa, double *fb, double *fc,
+	double (*func)(double))
+{
+	double ulim,u,r,q,fu,dum;
+
+	*fa=(*func)(*ax);
+	*fb=(*func)(*bx);
+	if (*fb > *fa) {
+		SHFT(dum,*ax,*bx,dum)
+		SHFT(dum,*fb,*fa,dum)
+	}
+	*cx=(*bx)+GOLD*(*bx-*ax);
+	*fc=(*func)(*cx);
+	while (*fb > *fc) {
+		r=(*bx-*ax)*(*fb-*fc);
+		q=(*bx-*cx)*(*fb-*fa);
+		u=(*bx)-((*bx-*cx)*q-(*bx-*ax)*r)/
+			(2.0*SIGN(FMAX(fabs(q-r),TINY),q-r));
+		ulim=(*bx)+GLIMIT*(*cx-*bx);
+		if ((*bx-u)*(u-*cx) > 0.0) {
+			fu=(*func)(u);
+			if (fu < *fc) {
+				*ax=(*bx);
+				*bx=u;
+				*fa=(*fb);
+				*fb=fu;
+				return;
+			} else if (fu > *fb) {
+				*cx=u;
+				*fc=fu;
+				return;
+			}
+			u=(*cx)+GOLD*(*cx-*bx);
+			fu=(*func)(u);
+		} else if ((*cx-u)*(u-ulim) > 0.0) {
+			fu=(*func)(u);
+			if (fu < *fc) {
+				SHFT(*bx,*cx,u,*cx+GOLD*(*cx-*bx))
+				SHFT(*fb,*fc,fu,(*func)(u))
+			}
+		} else if ((u-ulim)*(ulim-*cx) >= 0.0) {
+			u=ulim;
+			fu=(*func)(u);
+		} else {
+			u=(*cx)+GOLD*(*cx-*bx);
+			fu=(*func)(u);
+		}
+		SHFT(*ax,*bx,*cx,u)
+		SHFT(*fa,*fb,*fc,fu)
+	}
+}
+#undef GOLD
+#undef GLIMIT
+#undef TINY
+#undef SHFT
+#undef NRANSI
+/* (C) Copr. 1986-92 Numerical Recipes Software J!0. */
diff --git a/contrib/NR/newt.cpp b/contrib/NR/newt.cpp
new file mode 100644
index 0000000000..b09482177e
--- /dev/null
+++ b/contrib/NR/newt.cpp
@@ -0,0 +1,92 @@
+#include <math.h>
+#define NRANSI
+#include "nrutil.h"
+#define MAXITS 200
+#define TOLF 1.0e-4
+#define TOLMIN 1.0e-6
+#define TOLX 1.0e-7
+#define STPMX 100.0
+
+/* This file has been modified for inclusion in Gmsh (float->double) */
+
+int nn;
+double *fvec;
+void (*nrfuncv)(int n, double v[], double f[]);
+#define FREERETURN {free_dvector(fvec,1,n);free_dvector(xold,1,n);\
+	free_dvector(p,1,n);free_dvector(g,1,n);free_dmatrix(fjac,1,n,1,n);\
+	free_ivector(indx,1,n);return;}
+
+void newt(double x[], int n, int *check,
+	void (*vecfunc)(int, double [], double []))
+{
+	void fdjac(int n, double x[], double fvec[], double **df,
+		void (*vecfunc)(int, double [], double []));
+	double fmin(double x[]);
+	void lnsrch(int n, double xold[], double fold, double g[], double p[], double x[],
+		 double *f, double stpmax, int *check, double (*func)(double []));
+	void lubksb(double **a, int n, int *indx, double b[]);
+	void ludcmp(double **a, int n, int *indx, double *d);
+	int i,its,j,*indx;
+	double d,den,f,fold,stpmax,sum,temp,test,**fjac,*g,*p,*xold;
+
+	indx=ivector(1,n);
+	fjac=dmatrix(1,n,1,n);
+	g=dvector(1,n);
+	p=dvector(1,n);
+	xold=dvector(1,n);
+	fvec=dvector(1,n);
+	nn=n;
+	nrfuncv=vecfunc;
+	f=fmin(x);
+	test=0.0;
+	for (i=1;i<=n;i++)
+		if (fabs(fvec[i]) > test) test=fabs(fvec[i]);
+	if (test<0.01*TOLF) FREERETURN
+	for (sum=0.0,i=1;i<=n;i++) sum += SQR(x[i]);
+	stpmax=STPMX*FMAX(sqrt(sum),(double)n);
+	for (its=1;its<=MAXITS;its++) {
+		fdjac(n,x,fvec,fjac,vecfunc);
+		for (i=1;i<=n;i++) {
+			for (sum=0.0,j=1;j<=n;j++) sum += fjac[j][i]*fvec[j];
+			g[i]=sum;
+		}
+		for (i=1;i<=n;i++) xold[i]=x[i];
+		fold=f;
+		for (i=1;i<=n;i++) p[i] = -fvec[i];
+		ludcmp(fjac,n,indx,&d);
+		lubksb(fjac,n,indx,p);
+		lnsrch(n,xold,fold,g,p,x,&f,stpmax,check,fmin);
+		test=0.0;
+		for (i=1;i<=n;i++)
+			if (fabs(fvec[i]) > test) test=fabs(fvec[i]);
+		if (test < TOLF) {
+			*check=0;
+			FREERETURN
+		}
+		if (*check) {
+			test=0.0;
+			den=FMAX(f,0.5*n);
+			for (i=1;i<=n;i++) {
+				temp=fabs(g[i])*FMAX(fabs(x[i]),1.0)/den;
+				if (temp > test) test=temp;
+			}
+			*check=(test < TOLMIN ? 1 : 0);
+			FREERETURN
+		}
+		test=0.0;
+		for (i=1;i<=n;i++) {
+			temp=(fabs(x[i]-xold[i]))/FMAX(fabs(x[i]),1.0);
+			if (temp > test) test=temp;
+		}
+		if (test < TOLX) FREERETURN
+	}
+	nrerror("MAXITS exceeded in newt");
+}
+#undef MAXITS
+#undef TOLF
+#undef TOLMIN
+#undef TOLX
+#undef STPMX
+#undef FREERETURN
+#undef NRANSI
+/* (C) Copr. 1986-92 Numerical Recipes Software J!0. */
diff --git a/contrib/NR/nrutil.cpp b/contrib/NR/nrutil.cpp
new file mode 100644
index 0000000000..8aeaf3a79e
--- /dev/null
+++ b/contrib/NR/nrutil.cpp
@@ -0,0 +1,620 @@
+#define NRANSI
+#if defined(__STDC__) || defined(ANSI) || defined(NRANSI) /* ANSI */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#define NR_END 1
+#define FREE_ARG char*
+
+#include "Gmsh.h"
+
+void nrerror(char error_text[])
+/* Numerical Recipes standard error handler */
+{
+  Msg(GERROR, "%s", error_text);
+  /*
+	fprintf(stderr,"Numerical Recipes run-time error...\n");
+	fprintf(stderr,"%s\n",error_text);
+	fprintf(stderr,"...now exiting to system...\n");
+	exit(1);
+  */
+}
+
+float *vector(long nl, long nh)
+/* allocate a float vector with subscript range v[nl..nh] */
+{
+	float *v;
+
+	v=(float *)malloc((size_t) ((nh-nl+1+NR_END)*sizeof(float)));
+	if (!v) nrerror("allocation failure in vector()");
+	return v-nl+NR_END;
+}
+
+int *ivector(long nl, long nh)
+/* allocate an int vector with subscript range v[nl..nh] */
+{
+	int *v;
+
+	v=(int *)malloc((size_t) ((nh-nl+1+NR_END)*sizeof(int)));
+	if (!v) nrerror("allocation failure in ivector()");
+	return v-nl+NR_END;
+}
+
+unsigned char *cvector(long nl, long nh)
+/* allocate an unsigned char vector with subscript range v[nl..nh] */
+{
+	unsigned char *v;
+
+	v=(unsigned char *)malloc((size_t) ((nh-nl+1+NR_END)*sizeof(unsigned char)));
+	if (!v) nrerror("allocation failure in cvector()");
+	return v-nl+NR_END;
+}
+
+unsigned long *lvector(long nl, long nh)
+/* allocate an unsigned long vector with subscript range v[nl..nh] */
+{
+	unsigned long *v;
+
+	v=(unsigned long *)malloc((size_t) ((nh-nl+1+NR_END)*sizeof(long)));
+	if (!v) nrerror("allocation failure in lvector()");
+	return v-nl+NR_END;
+}
+
+double *dvector(long nl, long nh)
+/* allocate a double vector with subscript range v[nl..nh] */
+{
+	double *v;
+
+	v=(double *)malloc((size_t) ((nh-nl+1+NR_END)*sizeof(double)));
+	if (!v) nrerror("allocation failure in dvector()");
+	return v-nl+NR_END;
+}
+
+float **matrix(long nrl, long nrh, long ncl, long nch)
+/* allocate a float matrix with subscript range m[nrl..nrh][ncl..nch] */
+{
+	long i, nrow=nrh-nrl+1,ncol=nch-ncl+1;
+	float **m;
+
+	/* allocate pointers to rows */
+	m=(float **) malloc((size_t)((nrow+NR_END)*sizeof(float*)));
+	if (!m) nrerror("allocation failure 1 in matrix()");
+	m += NR_END;
+	m -= nrl;
+
+	/* allocate rows and set pointers to them */
+	m[nrl]=(float *) malloc((size_t)((nrow*ncol+NR_END)*sizeof(float)));
+	if (!m[nrl]) nrerror("allocation failure 2 in matrix()");
+	m[nrl] += NR_END;
+	m[nrl] -= ncl;
+
+	for(i=nrl+1;i<=nrh;i++) m[i]=m[i-1]+ncol;
+
+	/* return pointer to array of pointers to rows */
+	return m;
+}
+
+double **dmatrix(long nrl, long nrh, long ncl, long nch)
+/* allocate a double matrix with subscript range m[nrl..nrh][ncl..nch] */
+{
+	long i, nrow=nrh-nrl+1,ncol=nch-ncl+1;
+	double **m;
+
+	/* allocate pointers to rows */
+	m=(double **) malloc((size_t)((nrow+NR_END)*sizeof(double*)));
+	if (!m) nrerror("allocation failure 1 in matrix()");
+	m += NR_END;
+	m -= nrl;
+
+	/* allocate rows and set pointers to them */
+	m[nrl]=(double *) malloc((size_t)((nrow*ncol+NR_END)*sizeof(double)));
+	if (!m[nrl]) nrerror("allocation failure 2 in matrix()");
+	m[nrl] += NR_END;
+	m[nrl] -= ncl;
+
+	for(i=nrl+1;i<=nrh;i++) m[i]=m[i-1]+ncol;
+
+	/* return pointer to array of pointers to rows */
+	return m;
+}
+
+int **imatrix(long nrl, long nrh, long ncl, long nch)
+/* allocate a int matrix with subscript range m[nrl..nrh][ncl..nch] */
+{
+	long i, nrow=nrh-nrl+1,ncol=nch-ncl+1;
+	int **m;
+
+	/* allocate pointers to rows */
+	m=(int **) malloc((size_t)((nrow+NR_END)*sizeof(int*)));
+	if (!m) nrerror("allocation failure 1 in matrix()");
+	m += NR_END;
+	m -= nrl;
+
+
+	/* allocate rows and set pointers to them */
+	m[nrl]=(int *) malloc((size_t)((nrow*ncol+NR_END)*sizeof(int)));
+	if (!m[nrl]) nrerror("allocation failure 2 in matrix()");
+	m[nrl] += NR_END;
+	m[nrl] -= ncl;
+
+	for(i=nrl+1;i<=nrh;i++) m[i]=m[i-1]+ncol;
+
+	/* return pointer to array of pointers to rows */
+	return m;
+}
+
+float **submatrix(float **a, long oldrl, long oldrh, long oldcl, long oldch,
+	long newrl, long newcl)
+/* point a submatrix [newrl..][newcl..] to a[oldrl..oldrh][oldcl..oldch] */
+{
+	long i,j,nrow=oldrh-oldrl+1,ncol=oldcl-newcl;
+	float **m;
+
+	/* allocate array of pointers to rows */
+	m=(float **) malloc((size_t) ((nrow+NR_END)*sizeof(float*)));
+	if (!m) nrerror("allocation failure in submatrix()");
+	m += NR_END;
+	m -= newrl;
+
+	/* set pointers to rows */
+	for(i=oldrl,j=newrl;i<=oldrh;i++,j++) m[j]=a[i]+ncol;
+
+	/* return pointer to array of pointers to rows */
+	return m;
+}
+
+float **convert_matrix(float *a, long nrl, long nrh, long ncl, long nch)
+/* allocate a float matrix m[nrl..nrh][ncl..nch] that points to the matrix
+declared in the standard C manner as a[nrow][ncol], where nrow=nrh-nrl+1
+and ncol=nch-ncl+1. The routine should be called with the address
+&a[0][0] as the first argument. */
+{
+	long i,j,nrow=nrh-nrl+1,ncol=nch-ncl+1;
+	float **m;
+
+	/* allocate pointers to rows */
+	m=(float **) malloc((size_t) ((nrow+NR_END)*sizeof(float*)));
+	if (!m) nrerror("allocation failure in convert_matrix()");
+	m += NR_END;
+	m -= nrl;
+
+	/* set pointers to rows */
+	m[nrl]=a-ncl;
+	for(i=1,j=nrl+1;i<nrow;i++,j++) m[j]=m[j-1]+ncol;
+	/* return pointer to array of pointers to rows */
+	return m;
+}
+
+float ***f3tensor(long nrl, long nrh, long ncl, long nch, long ndl, long ndh)
+/* allocate a float 3tensor with range t[nrl..nrh][ncl..nch][ndl..ndh] */
+{
+	long i,j,nrow=nrh-nrl+1,ncol=nch-ncl+1,ndep=ndh-ndl+1;
+	float ***t;
+
+	/* allocate pointers to pointers to rows */
+	t=(float ***) malloc((size_t)((nrow+NR_END)*sizeof(float**)));
+	if (!t) nrerror("allocation failure 1 in f3tensor()");
+	t += NR_END;
+	t -= nrl;
+
+	/* allocate pointers to rows and set pointers to them */
+	t[nrl]=(float **) malloc((size_t)((nrow*ncol+NR_END)*sizeof(float*)));
+	if (!t[nrl]) nrerror("allocation failure 2 in f3tensor()");
+	t[nrl] += NR_END;
+	t[nrl] -= ncl;
+
+	/* allocate rows and set pointers to them */
+	t[nrl][ncl]=(float *) malloc((size_t)((nrow*ncol*ndep+NR_END)*sizeof(float)));
+	if (!t[nrl][ncl]) nrerror("allocation failure 3 in f3tensor()");
+	t[nrl][ncl] += NR_END;
+	t[nrl][ncl] -= ndl;
+
+	for(j=ncl+1;j<=nch;j++) t[nrl][j]=t[nrl][j-1]+ndep;
+	for(i=nrl+1;i<=nrh;i++) {
+		t[i]=t[i-1]+ncol;
+		t[i][ncl]=t[i-1][ncl]+ncol*ndep;
+		for(j=ncl+1;j<=nch;j++) t[i][j]=t[i][j-1]+ndep;
+	}
+
+	/* return pointer to array of pointers to rows */
+	return t;
+}
+
+void free_vector(float *v, long nl, long nh)
+/* free a float vector allocated with vector() */
+{
+	free((FREE_ARG) (v+nl-NR_END));
+}
+
+void free_ivector(int *v, long nl, long nh)
+/* free an int vector allocated with ivector() */
+{
+	free((FREE_ARG) (v+nl-NR_END));
+}
+
+void free_cvector(unsigned char *v, long nl, long nh)
+/* free an unsigned char vector allocated with cvector() */
+{
+	free((FREE_ARG) (v+nl-NR_END));
+}
+
+void free_lvector(unsigned long *v, long nl, long nh)
+/* free an unsigned long vector allocated with lvector() */
+{
+	free((FREE_ARG) (v+nl-NR_END));
+}
+
+void free_dvector(double *v, long nl, long nh)
+/* free a double vector allocated with dvector() */
+{
+	free((FREE_ARG) (v+nl-NR_END));
+}
+
+void free_matrix(float **m, long nrl, long nrh, long ncl, long nch)
+/* free a float matrix allocated by matrix() */
+{
+	free((FREE_ARG) (m[nrl]+ncl-NR_END));
+	free((FREE_ARG) (m+nrl-NR_END));
+}
+
+void free_dmatrix(double **m, long nrl, long nrh, long ncl, long nch)
+/* free a double matrix allocated by dmatrix() */
+{
+	free((FREE_ARG) (m[nrl]+ncl-NR_END));
+	free((FREE_ARG) (m+nrl-NR_END));
+}
+
+void free_imatrix(int **m, long nrl, long nrh, long ncl, long nch)
+/* free an int matrix allocated by imatrix() */
+{
+	free((FREE_ARG) (m[nrl]+ncl-NR_END));
+	free((FREE_ARG) (m+nrl-NR_END));
+}
+
+void free_submatrix(float **b, long nrl, long nrh, long ncl, long nch)
+/* free a submatrix allocated by submatrix() */
+{
+	free((FREE_ARG) (b+nrl-NR_END));
+}
+
+void free_convert_matrix(float **b, long nrl, long nrh, long ncl, long nch)
+/* free a matrix allocated by convert_matrix() */
+{
+	free((FREE_ARG) (b+nrl-NR_END));
+}
+
+void free_f3tensor(float ***t, long nrl, long nrh, long ncl, long nch,
+	long ndl, long ndh)
+/* free a float f3tensor allocated by f3tensor() */
+{
+	free((FREE_ARG) (t[nrl][ncl]+ndl-NR_END));
+	free((FREE_ARG) (t[nrl]+ncl-NR_END));
+	free((FREE_ARG) (t+nrl-NR_END));
+}
+
+#else /* ANSI */
+/* traditional - K&R */
+
+#include <stdio.h>
+#define NR_END 1
+#define FREE_ARG char*
+
+void nrerror(error_text)
+char error_text[];
+/* Numerical Recipes standard error handler */
+{
+	void exit();
+
+	fprintf(stderr,"Numerical Recipes run-time error...\n");
+	fprintf(stderr,"%s\n",error_text);
+	fprintf(stderr,"...now exiting to system...\n");
+	exit(1);
+}
+
+float *vector(nl,nh)
+long nh,nl;
+/* allocate a float vector with subscript range v[nl..nh] */
+{
+	float *v;
+
+	v=(float *)malloc((unsigned int) ((nh-nl+1+NR_END)*sizeof(float)));
+	if (!v) nrerror("allocation failure in vector()");
+	return v-nl+NR_END;
+}
+
+int *ivector(nl,nh)
+long nh,nl;
+/* allocate an int vector with subscript range v[nl..nh] */
+{
+	int *v;
+
+	v=(int *)malloc((unsigned int) ((nh-nl+1+NR_END)*sizeof(int)));
+	if (!v) nrerror("allocation failure in ivector()");
+	return v-nl+NR_END;
+}
+
+unsigned char *cvector(nl,nh)
+long nh,nl;
+/* allocate an unsigned char vector with subscript range v[nl..nh] */
+{
+	unsigned char *v;
+
+	v=(unsigned char *)malloc((unsigned int) ((nh-nl+1+NR_END)*sizeof(unsigned char)));
+	if (!v) nrerror("allocation failure in cvector()");
+	return v-nl+NR_END;
+}
+
+unsigned long *lvector(nl,nh)
+long nh,nl;
+/* allocate an unsigned long vector with subscript range v[nl..nh] */
+{
+	unsigned long *v;
+
+	v=(unsigned long *)malloc((unsigned int) ((nh-nl+1+NR_END)*sizeof(long)));
+	if (!v) nrerror("allocation failure in lvector()");
+	return v-nl+NR_END;
+}
+
+double *dvector(nl,nh)
+long nh,nl;
+/* allocate a double vector with subscript range v[nl..nh] */
+{
+	double *v;
+
+	v=(double *)malloc((unsigned int) ((nh-nl+1+NR_END)*sizeof(double)));
+	if (!v) nrerror("allocation failure in dvector()");
+	return v-nl+NR_END;
+}
+
+float **matrix(nrl,nrh,ncl,nch)
+long nch,ncl,nrh,nrl;
+/* allocate a float matrix with subscript range m[nrl..nrh][ncl..nch] */
+{
+	long i, nrow=nrh-nrl+1,ncol=nch-ncl+1;
+	float **m;
+
+	/* allocate pointers to rows */
+	m=(float **) malloc((unsigned int)((nrow+NR_END)*sizeof(float*)));
+	if (!m) nrerror("allocation failure 1 in matrix()");
+	m += NR_END;
+	m -= nrl;
+
+	/* allocate rows and set pointers to them */
+	m[nrl]=(float *) malloc((unsigned int)((nrow*ncol+NR_END)*sizeof(float)));
+	if (!m[nrl]) nrerror("allocation failure 2 in matrix()");
+	m[nrl] += NR_END;
+	m[nrl] -= ncl;
+
+	for(i=nrl+1;i<=nrh;i++) m[i]=m[i-1]+ncol;
+
+	/* return pointer to array of pointers to rows */
+	return m;
+}
+
+double **dmatrix(nrl,nrh,ncl,nch)
+long nch,ncl,nrh,nrl;
+/* allocate a double matrix with subscript range m[nrl..nrh][ncl..nch] */
+{
+	long i, nrow=nrh-nrl+1,ncol=nch-ncl+1;
+	double **m;
+
+	/* allocate pointers to rows */
+	m=(double **) malloc((unsigned int)((nrow+NR_END)*sizeof(double*)));
+	if (!m) nrerror("allocation failure 1 in matrix()");
+	m += NR_END;
+	m -= nrl;
+
+	/* allocate rows and set pointers to them */
+	m[nrl]=(double *) malloc((unsigned int)((nrow*ncol+NR_END)*sizeof(double)));
+	if (!m[nrl]) nrerror("allocation failure 2 in matrix()");
+	m[nrl] += NR_END;
+	m[nrl] -= ncl;
+
+	for(i=nrl+1;i<=nrh;i++) m[i]=m[i-1]+ncol;
+
+	/* return pointer to array of pointers to rows */
+	return m;
+}
+
+int **imatrix(nrl,nrh,ncl,nch)
+long nch,ncl,nrh,nrl;
+/* allocate a int matrix with subscript range m[nrl..nrh][ncl..nch] */
+{
+	long i, nrow=nrh-nrl+1,ncol=nch-ncl+1;
+	int **m;
+
+	/* allocate pointers to rows */
+	m=(int **) malloc((unsigned int)((nrow+NR_END)*sizeof(int*)));
+	if (!m) nrerror("allocation failure 1 in matrix()");
+	m += NR_END;
+	m -= nrl;
+
+
+	/* allocate rows and set pointers to them */
+	m[nrl]=(int *) malloc((unsigned int)((nrow*ncol+NR_END)*sizeof(int)));
+	if (!m[nrl]) nrerror("allocation failure 2 in matrix()");
+	m[nrl] += NR_END;
+	m[nrl] -= ncl;
+
+	for(i=nrl+1;i<=nrh;i++) m[i]=m[i-1]+ncol;
+
+	/* return pointer to array of pointers to rows */
+	return m;
+}
+
+float **submatrix(a,oldrl,oldrh,oldcl,oldch,newrl,newcl)
+float **a;
+long newcl,newrl,oldch,oldcl,oldrh,oldrl;
+/* point a submatrix [newrl..][newcl..] to a[oldrl..oldrh][oldcl..oldch] */
+{
+	long i,j,nrow=oldrh-oldrl+1,ncol=oldcl-newcl;
+	float **m;
+
+	/* allocate array of pointers to rows */
+	m=(float **) malloc((unsigned int) ((nrow+NR_END)*sizeof(float*)));
+	if (!m) nrerror("allocation failure in submatrix()");
+	m += NR_END;
+	m -= newrl;
+
+	/* set pointers to rows */
+	for(i=oldrl,j=newrl;i<=oldrh;i++,j++) m[j]=a[i]+ncol;
+
+	/* return pointer to array of pointers to rows */
+	return m;
+}
+
+float **convert_matrix(a,nrl,nrh,ncl,nch)
+float *a;
+long nch,ncl,nrh,nrl;
+/* allocate a float matrix m[nrl..nrh][ncl..nch] that points to the matrix
+declared in the standard C manner as a[nrow][ncol], where nrow=nrh-nrl+1
+and ncol=nch-ncl+1. The routine should be called with the address
+&a[0][0] as the first argument. */
+{
+	long i,j,nrow=nrh-nrl+1,ncol=nch-ncl+1;
+	float **m;
+
+	/* allocate pointers to rows */
+	m=(float **) malloc((unsigned int) ((nrow+NR_END)*sizeof(float*)));
+	if (!m)	nrerror("allocation failure in convert_matrix()");
+	m += NR_END;
+	m -= nrl;
+
+	/* set pointers to rows */
+	m[nrl]=a-ncl;
+	for(i=1,j=nrl+1;i<nrow;i++,j++) m[j]=m[j-1]+ncol;
+	/* return pointer to array of pointers to rows */
+	return m;
+}
+
+float ***f3tensor(nrl,nrh,ncl,nch,ndl,ndh)
+long nch,ncl,ndh,ndl,nrh,nrl;
+/* allocate a float 3tensor with range t[nrl..nrh][ncl..nch][ndl..ndh] */
+{
+	long i,j,nrow=nrh-nrl+1,ncol=nch-ncl+1,ndep=ndh-ndl+1;
+	float ***t;
+
+	/* allocate pointers to pointers to rows */
+	t=(float ***) malloc((unsigned int)((nrow+NR_END)*sizeof(float**)));
+	if (!t) nrerror("allocation failure 1 in f3tensor()");
+	t += NR_END;
+	t -= nrl;
+
+	/* allocate pointers to rows and set pointers to them */
+	t[nrl]=(float **) malloc((unsigned int)((nrow*ncol+NR_END)*sizeof(float*)));
+	if (!t[nrl]) nrerror("allocation failure 2 in f3tensor()");
+	t[nrl] += NR_END;
+	t[nrl] -= ncl;
+
+	/* allocate rows and set pointers to them */
+	t[nrl][ncl]=(float *) malloc((unsigned int)((nrow*ncol*ndep+NR_END)*sizeof(float)));
+	if (!t[nrl][ncl]) nrerror("allocation failure 3 in f3tensor()");
+	t[nrl][ncl] += NR_END;
+	t[nrl][ncl] -= ndl;
+
+	for(j=ncl+1;j<=nch;j++) t[nrl][j]=t[nrl][j-1]+ndep;
+	for(i=nrl+1;i<=nrh;i++) {
+		t[i]=t[i-1]+ncol;
+		t[i][ncl]=t[i-1][ncl]+ncol*ndep;
+		for(j=ncl+1;j<=nch;j++) t[i][j]=t[i][j-1]+ndep;
+	}
+
+	/* return pointer to array of pointers to rows */
+	return t;
+}
+
+void free_vector(v,nl,nh)
+float *v;
+long nh,nl;
+/* free a float vector allocated with vector() */
+{
+	free((FREE_ARG) (v+nl-NR_END));
+}
+
+void free_ivector(v,nl,nh)
+int *v;
+long nh,nl;
+/* free an int vector allocated with ivector() */
+{
+	free((FREE_ARG) (v+nl-NR_END));
+}
+
+void free_cvector(v,nl,nh)
+long nh,nl;
+unsigned char *v;
+/* free an unsigned char vector allocated with cvector() */
+{
+	free((FREE_ARG) (v+nl-NR_END));
+}
+
+void free_lvector(v,nl,nh)
+long nh,nl;
+unsigned long *v;
+/* free an unsigned long vector allocated with lvector() */
+{
+	free((FREE_ARG) (v+nl-NR_END));
+}
+
+void free_dvector(v,nl,nh)
+double *v;
+long nh,nl;
+/* free a double vector allocated with dvector() */
+{
+	free((FREE_ARG) (v+nl-NR_END));
+}
+
+void free_matrix(m,nrl,nrh,ncl,nch)
+float **m;
+long nch,ncl,nrh,nrl;
+/* free a float matrix allocated by matrix() */
+{
+	free((FREE_ARG) (m[nrl]+ncl-NR_END));
+	free((FREE_ARG) (m+nrl-NR_END));
+}
+
+void free_dmatrix(m,nrl,nrh,ncl,nch)
+double **m;
+long nch,ncl,nrh,nrl;
+/* free a double matrix allocated by dmatrix() */
+{
+	free((FREE_ARG) (m[nrl]+ncl-NR_END));
+	free((FREE_ARG) (m+nrl-NR_END));
+}
+
+void free_imatrix(m,nrl,nrh,ncl,nch)
+int **m;
+long nch,ncl,nrh,nrl;
+/* free an int matrix allocated by imatrix() */
+{
+	free((FREE_ARG) (m[nrl]+ncl-NR_END));
+	free((FREE_ARG) (m+nrl-NR_END));
+}
+
+void free_submatrix(b,nrl,nrh,ncl,nch)
+float **b;
+long nch,ncl,nrh,nrl;
+/* free a submatrix allocated by submatrix() */
+{
+	free((FREE_ARG) (b+nrl-NR_END));
+}
+
+void free_convert_matrix(b,nrl,nrh,ncl,nch)
+float **b;
+long nch,ncl,nrh,nrl;
+/* free a matrix allocated by convert_matrix() */
+{
+	free((FREE_ARG) (b+nrl-NR_END));
+}
+
+void free_f3tensor(t,nrl,nrh,ncl,nch,ndl,ndh)
+float ***t;
+long nch,ncl,ndh,ndl,nrh,nrl;
+/* free a float f3tensor allocated by f3tensor() */
+{
+	free((FREE_ARG) (t[nrl][ncl]+ndl-NR_END));
+	free((FREE_ARG) (t[nrl]+ncl-NR_END));
+	free((FREE_ARG) (t+nrl-NR_END));
+}
+
+#endif /* ANSI */
diff --git a/contrib/NR/nrutil.h b/contrib/NR/nrutil.h
new file mode 100644
index 0000000000..436193de5e
--- /dev/null
+++ b/contrib/NR/nrutil.h
@@ -0,0 +1,109 @@
+#ifndef _NR_UTILS_H_
+#define _NR_UTILS_H_
+
+/* This file has been modified for inclusion in Gmsh */
+
+/* Gmsh: */
+#include "Gmsh.h"
+#include "Numeric.h"
+
+/* Gmsh:
+static float sqrarg;
+#define SQR(a) ((sqrarg=(a)) == 0.0 ? 0.0 : sqrarg*sqrarg)
+
+static double dsqrarg;
+#define DSQR(a) ((dsqrarg=(a)) == 0.0 ? 0.0 : dsqrarg*dsqrarg)
+
+static double dmaxarg1,dmaxarg2;
+#define DMAX(a,b) (dmaxarg1=(a),dmaxarg2=(b),(dmaxarg1) > (dmaxarg2) ?\
+        (dmaxarg1) : (dmaxarg2))
+
+static double dminarg1,dminarg2;
+#define DMIN(a,b) (dminarg1=(a),dminarg2=(b),(dminarg1) < (dminarg2) ?\
+        (dminarg1) : (dminarg2))
+
+static float maxarg1,maxarg2;
+#define FMAX(a,b) (maxarg1=(a),maxarg2=(b),(maxarg1) > (maxarg2) ?\
+        (maxarg1) : (maxarg2))
+
+static float minarg1,minarg2;
+#define FMIN(a,b) (minarg1=(a),minarg2=(b),(minarg1) < (minarg2) ?\
+        (minarg1) : (minarg2))
+
+static long lmaxarg1,lmaxarg2;
+#define LMAX(a,b) (lmaxarg1=(a),lmaxarg2=(b),(lmaxarg1) > (lmaxarg2) ?\
+        (lmaxarg1) : (lmaxarg2))
+
+static long lminarg1,lminarg2;
+#define LMIN(a,b) (lminarg1=(a),lminarg2=(b),(lminarg1) < (lminarg2) ?\
+        (lminarg1) : (lminarg2))
+
+static int imaxarg1,imaxarg2;
+#define IMAX(a,b) (imaxarg1=(a),imaxarg2=(b),(imaxarg1) > (imaxarg2) ?\
+        (imaxarg1) : (imaxarg2))
+
+static int iminarg1,iminarg2;
+#define IMIN(a,b) (iminarg1=(a),iminarg2=(b),(iminarg1) < (iminarg2) ?\
+        (iminarg1) : (iminarg2))
+
+#define SIGN(a,b) ((b) >= 0.0 ? fabs(a) : -fabs(a))
+*/
+
+#if defined(__STDC__) || defined(ANSI) || defined(NRANSI) /* ANSI */
+
+void nrerror(char error_text[]);
+float *vector(long nl, long nh);
+int *ivector(long nl, long nh);
+unsigned char *cvector(long nl, long nh);
+unsigned long *lvector(long nl, long nh);
+double *dvector(long nl, long nh);
+float **matrix(long nrl, long nrh, long ncl, long nch);
+double **dmatrix(long nrl, long nrh, long ncl, long nch);
+int **imatrix(long nrl, long nrh, long ncl, long nch);
+float **submatrix(float **a, long oldrl, long oldrh, long oldcl, long oldch,
+	long newrl, long newcl);
+float **convert_matrix(float *a, long nrl, long nrh, long ncl, long nch);
+float ***f3tensor(long nrl, long nrh, long ncl, long nch, long ndl, long ndh);
+void free_vector(float *v, long nl, long nh);
+void free_ivector(int *v, long nl, long nh);
+void free_cvector(unsigned char *v, long nl, long nh);
+void free_lvector(unsigned long *v, long nl, long nh);
+void free_dvector(double *v, long nl, long nh);
+void free_matrix(float **m, long nrl, long nrh, long ncl, long nch);
+void free_dmatrix(double **m, long nrl, long nrh, long ncl, long nch);
+void free_imatrix(int **m, long nrl, long nrh, long ncl, long nch);
+void free_submatrix(float **b, long nrl, long nrh, long ncl, long nch);
+void free_convert_matrix(float **b, long nrl, long nrh, long ncl, long nch);
+void free_f3tensor(float ***t, long nrl, long nrh, long ncl, long nch,
+	long ndl, long ndh);
+
+#else /* ANSI */
+/* traditional - K&R */
+
+void nrerror();
+float *vector();
+float **matrix();
+float **submatrix();
+float **convert_matrix();
+float ***f3tensor();
+double *dvector();
+double **dmatrix();
+int *ivector();
+int **imatrix();
+unsigned char *cvector();
+unsigned long *lvector();
+void free_vector();
+void free_dvector();
+void free_ivector();
+void free_cvector();
+void free_lvector();
+void free_matrix();
+void free_submatrix();
+void free_convert_matrix();
+void free_dmatrix();
+void free_imatrix();
+void free_f3tensor();
+
+#endif /* ANSI */
+
+#endif /* _NR_UTILS_H_ */
diff --git a/contrib/Netgen/COPYING.LIB b/contrib/Netgen/COPYING.LIB
new file mode 100644
index 0000000000..b1e3f5a263
--- /dev/null
+++ b/contrib/Netgen/COPYING.LIB
@@ -0,0 +1,504 @@
+		  GNU LESSER GENERAL PUBLIC LICENSE
+		       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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/Makefile b/contrib/Netgen/Makefile
new file mode 100644
index 0000000000..c339277943
--- /dev/null
+++ b/contrib/Netgen/Makefile
@@ -0,0 +1,4412 @@
+# $Id: Makefile,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+#
+# Copyright (C) 1997-2005 C. Geuzaine, J.-F. Remacle
+#
+# 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.
+# 
+# Please report all bugs and problems to <gmsh@geuz.org>.
+
+include ../../variables
+
+LIB     = ../../lib/libGmshNetgen.a
+INCLUDE = -I../../Common -Ilibsrc/include -Ilibsrc/interface
+CFLAGS  = ${OPTIM} ${FLAGS} ${INCLUDE} -DNO_PARALLEL_THREADS -UWIN32
+
+SRC = libsrc/opti/linopt.cpp \
+	libsrc/opti/bfgs.cpp \
+	libsrc/opti/linsearch.cpp \
+	libsrc/meshing/global.cpp \
+	libsrc/meshing/bisect.cpp \
+	libsrc/meshing/meshtool.cpp \
+	libsrc/meshing/refine.cpp \
+	libsrc/meshing/ruler3.cpp \
+	libsrc/meshing/improve3.cpp \
+	libsrc/meshing/smoothing3.cpp \
+	libsrc/meshing/adfront3.cpp \
+	libsrc/meshing/tetrarls.cpp \
+	libsrc/meshing/prism2rls.cpp \
+	libsrc/meshing/pyramidrls.cpp \
+	libsrc/meshing/pyramid2rls.cpp \
+	libsrc/meshing/netrule3.cpp \
+	libsrc/meshing/ruler2.cpp \
+	libsrc/meshing/meshclass.cpp \
+	libsrc/meshing/improve2.cpp \
+	libsrc/meshing/smoothing2.cpp \
+	libsrc/meshing/adfront2.cpp \
+	libsrc/meshing/netrule2.cpp \
+	libsrc/meshing/triarls.cpp \
+	libsrc/meshing/geomsearch.cpp \
+	libsrc/meshing/secondorder.cpp \
+	libsrc/meshing/meshtype.cpp \
+	libsrc/meshing/parser3.cpp \
+	libsrc/meshing/meshing2.cpp \
+	libsrc/meshing/quadrls.cpp \
+	libsrc/meshing/specials.cpp \
+	libsrc/meshing/parser2.cpp \
+	libsrc/meshing/meshing3.cpp \
+	libsrc/meshing/meshfunc.cpp \
+	libsrc/meshing/localh.cpp \
+	libsrc/meshing/improve2gen.cpp \
+	libsrc/meshing/delaunay.cpp \
+	libsrc/meshing/boundarylayer.cpp \
+	libsrc/meshing/msghandler.cpp \
+	libsrc/meshing/meshfunc2d.cpp \
+	libsrc/meshing/topology.cpp \
+	libsrc/meshing/clusters.cpp \
+	libsrc/meshing/curvedelems.cpp \
+	libsrc/meshing/curvedelems2.cpp \
+	libsrc/meshing/hprefinement.cpp \
+	libsrc/interface/nglib.cpp \
+	libsrc/gprim/geomtest3d.cpp \
+	libsrc/gprim/geom2d.cpp \
+	libsrc/gprim/geom3d.cpp \
+	libsrc/gprim/adtree.cpp \
+	libsrc/gprim/transform3d.cpp \
+	libsrc/gprim/geomfuncs.cpp \
+	libsrc/linalg/polynomial.cpp \
+	libsrc/linalg/densemat.cpp \
+	libsrc/linalg/vector.cpp \
+	libsrc/csg/algprim.cpp \
+	libsrc/csg/brick.cpp \
+	libsrc/csg/manifold.cpp \
+	libsrc/csg/bspline2d.cpp \
+	libsrc/csg/meshsurf.cpp \
+	libsrc/csg/csgeom.cpp \
+	libsrc/csg/polyhedra.cpp \
+	libsrc/csg/curve2d.cpp \
+	libsrc/csg/singularref.cpp \
+	libsrc/csg/edgeflw.cpp \
+	libsrc/csg/solid.cpp \
+	libsrc/csg/explicitcurve2d.cpp \
+	libsrc/csg/specpoin.cpp \
+	libsrc/csg/gencyl.cpp \
+	libsrc/csg/revolution.cpp \
+	libsrc/csg/genmesh.cpp \
+	libsrc/csg/spline3d.cpp \
+	libsrc/csg/surface.cpp \
+	libsrc/csg/identify.cpp \
+	libsrc/csg/triapprox.cpp \
+	libsrc/geom2d/geom2dmesh.cpp \
+	libsrc/geom2d/spline2d.cpp \
+	libsrc/geom2d/splinegeometry2.cpp \
+	libsrc/geom2d/genmesh2d.cpp \
+	libsrc/stlgeom/meshstlsurface.cpp \
+	libsrc/stlgeom/stlline.cpp \
+	libsrc/stlgeom/stltopology.cpp \
+	libsrc/stlgeom/stltool.cpp \
+	libsrc/stlgeom/stlgeom.cpp \
+	libsrc/stlgeom/stlgeomchart.cpp \
+	libsrc/stlgeom/stlgeommesh.cpp \
+	libsrc/general/moveablemem.cpp \
+	libsrc/general/ngexception.cpp \
+	libsrc/general/table.cpp \
+	libsrc/general/optmem.cpp \
+	libsrc/general/spbita2d.cpp \
+	libsrc/general/hashtabl.cpp \
+	libsrc/general/sort.cpp \
+	libsrc/general/flags.cpp \
+	libsrc/general/seti.cpp \
+	libsrc/general/bitarray.cpp \
+	libsrc/general/array.cpp \
+	libsrc/general/symbolta.cpp \
+	libsrc/general/mystring.cpp \
+	nglib_addon.cpp
+
+OBJ = ${SRC:.cpp=.o}
+
+.SUFFIXES: .o .cpp
+
+${LIB}: ${OBJ} 
+	${AR} ${LIB} ${OBJ} 
+	${RANLIB} ${LIB}
+
+.cpp.o:
+	${CXX} ${CFLAGS} -c $< -o ${<:.cpp=.o}
+
+clean:
+	rm -f libsrc/*/*.o
+
+depend:
+	(sed '/^# DO NOT DELETE THIS LINE/q' Makefile && \
+	${CXX} -MM ${CFLAGS} ${SRC} \
+	) >Makefile.new
+	cp Makefile Makefile.bak
+	cp Makefile.new Makefile
+	rm -f Makefile.new
+
+# DO NOT DELETE THIS LINE
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+linopt.o: libsrc/opti/linopt.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/opti/opti.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+bfgs.o: libsrc/opti/bfgs.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/opti/opti.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+linsearch.o: libsrc/opti/linsearch.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/opti/opti.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+global.o: libsrc/meshing/global.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+bisect.o: libsrc/meshing/bisect.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+meshtool.o: libsrc/meshing/meshtool.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp libsrc/include/csg.hpp \
+  libsrc/include/../csg/csg.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/meshing.hpp libsrc/include/../meshing/meshing.hpp \
+  libsrc/include/../csg/surface.hpp libsrc/include/../csg/solid.hpp \
+  libsrc/include/../csg/identify.hpp \
+  libsrc/include/../csg/singularref.hpp libsrc/include/../csg/csgeom.hpp \
+  libsrc/include/../csg/triapprox.hpp libsrc/include/../csg/algprim.hpp \
+  libsrc/include/../csg/brick.hpp libsrc/include/../csg/spline3d.hpp \
+  libsrc/include/../csg/manifold.hpp libsrc/include/../csg/curve2d.hpp \
+  libsrc/include/../csg/explicitcurve2d.hpp \
+  libsrc/include/../csg/gencyl.hpp libsrc/include/../csg/polyhedra.hpp \
+  libsrc/include/../csg/extrusion.hpp \
+  libsrc/include/../csg/revolution.hpp libsrc/include/../csg/specpoin.hpp \
+  libsrc/include/../csg/edgeflw.hpp libsrc/include/../csg/meshsurf.hpp \
+  libsrc/include/geometry2d.hpp libsrc/include/../geom2d/geometry2d.hpp \
+  libsrc/include/../gprim/gprim.hpp libsrc/include/../geom2d/spline2d.hpp \
+  libsrc/include/../geom2d/splinegeometry2.hpp \
+  libsrc/include/../geom2d/geom2dmesh.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+refine.o: libsrc/meshing/refine.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+ruler3.o: libsrc/meshing/ruler3.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+improve3.o: libsrc/meshing/improve3.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp libsrc/include/../opti/opti.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+smoothing3.o: libsrc/meshing/smoothing3.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp libsrc/include/../opti/opti.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+adfront3.o: libsrc/meshing/adfront3.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+tetrarls.o: libsrc/meshing/tetrarls.cpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+prism2rls.o: libsrc/meshing/prism2rls.cpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+pyramidrls.o: libsrc/meshing/pyramidrls.cpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+pyramid2rls.o: libsrc/meshing/pyramid2rls.cpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+netrule3.o: libsrc/meshing/netrule3.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+ruler2.o: libsrc/meshing/ruler2.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+meshclass.o: libsrc/meshing/meshclass.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+improve2.o: libsrc/meshing/improve2.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp libsrc/include/../opti/opti.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+smoothing2.o: libsrc/meshing/smoothing2.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp libsrc/include/../opti/opti.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+adfront2.o: libsrc/meshing/adfront2.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+netrule2.o: libsrc/meshing/netrule2.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+triarls.o: libsrc/meshing/triarls.cpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+geomsearch.o: libsrc/meshing/geomsearch.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+secondorder.o: libsrc/meshing/secondorder.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+meshtype.o: libsrc/meshing/meshtype.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+parser3.o: libsrc/meshing/parser3.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+meshing2.o: libsrc/meshing/meshing2.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+quadrls.o: libsrc/meshing/quadrls.cpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+specials.o: libsrc/meshing/specials.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+parser2.o: libsrc/meshing/parser2.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+meshing3.o: libsrc/meshing/meshing3.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+meshfunc.o: libsrc/meshing/meshfunc.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+localh.o: libsrc/meshing/localh.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+improve2gen.o: libsrc/meshing/improve2gen.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp libsrc/include/../opti/opti.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+delaunay.o: libsrc/meshing/delaunay.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+boundarylayer.o: libsrc/meshing/boundarylayer.cpp \
+  libsrc/include/mystdlib.h libsrc/meshing/meshing.hpp \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+msghandler.o: libsrc/meshing/msghandler.cpp libsrc/include/meshing.hpp \
+  libsrc/include/../meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mystdlib.h \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+meshfunc2d.o: libsrc/meshing/meshfunc2d.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+topology.o: libsrc/meshing/topology.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+clusters.o: libsrc/meshing/clusters.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+curvedelems.o: libsrc/meshing/curvedelems.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+curvedelems2.o: libsrc/meshing/curvedelems2.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+hprefinement.o: libsrc/meshing/hprefinement.cpp libsrc/include/mystdlib.h \
+  libsrc/meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp libsrc/meshing/msghandler.hpp \
+  libsrc/meshing/meshtype.hpp libsrc/meshing/localh.hpp \
+  libsrc/meshing/meshclass.hpp libsrc/meshing/global.hpp \
+  libsrc/meshing/meshtool.hpp libsrc/meshing/ruler2.hpp \
+  libsrc/meshing/adfront2.hpp libsrc/meshing/meshing2.hpp \
+  libsrc/meshing/improve2.hpp libsrc/meshing/geomsearch.hpp \
+  libsrc/meshing/adfront3.hpp libsrc/meshing/ruler3.hpp \
+  libsrc/meshing/meshing3.hpp libsrc/meshing/improve3.hpp \
+  libsrc/meshing/findip.hpp libsrc/meshing/topology.hpp \
+  libsrc/meshing/curvedelems.hpp libsrc/meshing/bisect.hpp \
+  libsrc/meshing/clusters.hpp libsrc/meshing/meshfunc.hpp \
+  libsrc/meshing/hprefinement.hpp libsrc/meshing/boundarylayer.hpp \
+  libsrc/meshing/specials.hpp libsrc/meshing/hpref_trig.hpp \
+  libsrc/meshing/hpref_quad.hpp libsrc/meshing/hpref_tet.hpp \
+  libsrc/meshing/hpref_prism.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+nglib.o: libsrc/interface/nglib.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/csg.hpp \
+  libsrc/include/../csg/csg.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/meshing.hpp \
+  libsrc/include/../meshing/meshing.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp \
+  libsrc/include/../csg/surface.hpp libsrc/include/../csg/solid.hpp \
+  libsrc/include/../csg/identify.hpp \
+  libsrc/include/../csg/singularref.hpp libsrc/include/../csg/csgeom.hpp \
+  libsrc/include/../csg/triapprox.hpp libsrc/include/../csg/algprim.hpp \
+  libsrc/include/../csg/brick.hpp libsrc/include/../csg/spline3d.hpp \
+  libsrc/include/../csg/manifold.hpp libsrc/include/../csg/curve2d.hpp \
+  libsrc/include/../csg/explicitcurve2d.hpp \
+  libsrc/include/../csg/gencyl.hpp libsrc/include/../csg/polyhedra.hpp \
+  libsrc/include/../csg/extrusion.hpp \
+  libsrc/include/../csg/revolution.hpp libsrc/include/../csg/specpoin.hpp \
+  libsrc/include/../csg/edgeflw.hpp libsrc/include/../csg/meshsurf.hpp \
+  libsrc/include/stlgeom.hpp libsrc/include/../stlgeom/stlgeom.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../stlgeom/stltopology.hpp \
+  libsrc/include/../stlgeom/stltool.hpp \
+  libsrc/include/../stlgeom/stlline.hpp \
+  libsrc/include/../stlgeom/meshstlsurface.hpp \
+  libsrc/include/geometry2d.hpp libsrc/include/../geom2d/geometry2d.hpp \
+  libsrc/include/../gprim/gprim.hpp libsrc/include/../geom2d/spline2d.hpp \
+  libsrc/include/../geom2d/splinegeometry2.hpp \
+  libsrc/include/../geom2d/geom2dmesh.hpp libsrc/interface/nglib.h
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+geomtest3d.o: libsrc/gprim/geomtest3d.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+geom2d.o: libsrc/gprim/geom2d.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+geom3d.o: libsrc/gprim/geom3d.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+adtree.o: libsrc/gprim/adtree.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+transform3d.o: libsrc/gprim/transform3d.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+geomfuncs.o: libsrc/gprim/geomfuncs.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+polynomial.o: libsrc/linalg/polynomial.cpp libsrc/include/mystdlib.h \
+  libsrc/include/linalg.hpp libsrc/include/../linalg/linalg.hpp \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp \
+  libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+densemat.o: libsrc/linalg/densemat.cpp libsrc/include/mystdlib.h \
+  libsrc/include/linalg.hpp libsrc/include/../linalg/linalg.hpp \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp \
+  libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+vector.o: libsrc/linalg/vector.cpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+algprim.o: libsrc/csg/algprim.cpp libsrc/include/mystdlib.h \
+  libsrc/include/linalg.hpp libsrc/include/../linalg/linalg.hpp \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp \
+  libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/csg.hpp \
+  libsrc/include/../csg/csg.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/meshing.hpp \
+  libsrc/include/../meshing/meshing.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp \
+  libsrc/include/../csg/surface.hpp libsrc/include/../csg/solid.hpp \
+  libsrc/include/../csg/identify.hpp \
+  libsrc/include/../csg/singularref.hpp libsrc/include/../csg/csgeom.hpp \
+  libsrc/include/../csg/triapprox.hpp libsrc/include/../csg/algprim.hpp \
+  libsrc/include/../csg/brick.hpp libsrc/include/../csg/spline3d.hpp \
+  libsrc/include/../csg/manifold.hpp libsrc/include/../csg/curve2d.hpp \
+  libsrc/include/../csg/explicitcurve2d.hpp \
+  libsrc/include/../csg/gencyl.hpp libsrc/include/../csg/polyhedra.hpp \
+  libsrc/include/../csg/extrusion.hpp \
+  libsrc/include/../csg/revolution.hpp libsrc/include/../csg/specpoin.hpp \
+  libsrc/include/../csg/edgeflw.hpp libsrc/include/../csg/meshsurf.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+brick.o: libsrc/csg/brick.cpp libsrc/include/mystdlib.h \
+  libsrc/include/linalg.hpp libsrc/include/../linalg/linalg.hpp \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp \
+  libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/csg.hpp \
+  libsrc/include/../csg/csg.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/meshing.hpp \
+  libsrc/include/../meshing/meshing.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp \
+  libsrc/include/../csg/surface.hpp libsrc/include/../csg/solid.hpp \
+  libsrc/include/../csg/identify.hpp \
+  libsrc/include/../csg/singularref.hpp libsrc/include/../csg/csgeom.hpp \
+  libsrc/include/../csg/triapprox.hpp libsrc/include/../csg/algprim.hpp \
+  libsrc/include/../csg/brick.hpp libsrc/include/../csg/spline3d.hpp \
+  libsrc/include/../csg/manifold.hpp libsrc/include/../csg/curve2d.hpp \
+  libsrc/include/../csg/explicitcurve2d.hpp \
+  libsrc/include/../csg/gencyl.hpp libsrc/include/../csg/polyhedra.hpp \
+  libsrc/include/../csg/extrusion.hpp \
+  libsrc/include/../csg/revolution.hpp libsrc/include/../csg/specpoin.hpp \
+  libsrc/include/../csg/edgeflw.hpp libsrc/include/../csg/meshsurf.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+manifold.o: libsrc/csg/manifold.cpp libsrc/include/csg.hpp \
+  libsrc/include/../csg/csg.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mystdlib.h \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/meshing.hpp \
+  libsrc/include/../meshing/meshing.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/linalg.hpp libsrc/include/../linalg/linalg.hpp \
+  libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp \
+  libsrc/include/../csg/surface.hpp libsrc/include/../csg/solid.hpp \
+  libsrc/include/../csg/identify.hpp \
+  libsrc/include/../csg/singularref.hpp libsrc/include/../csg/csgeom.hpp \
+  libsrc/include/../csg/triapprox.hpp libsrc/include/../csg/algprim.hpp \
+  libsrc/include/../csg/brick.hpp libsrc/include/../csg/spline3d.hpp \
+  libsrc/include/../csg/manifold.hpp libsrc/include/../csg/curve2d.hpp \
+  libsrc/include/../csg/explicitcurve2d.hpp \
+  libsrc/include/../csg/gencyl.hpp libsrc/include/../csg/polyhedra.hpp \
+  libsrc/include/../csg/extrusion.hpp \
+  libsrc/include/../csg/revolution.hpp libsrc/include/../csg/specpoin.hpp \
+  libsrc/include/../csg/edgeflw.hpp libsrc/include/../csg/meshsurf.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+bspline2d.o: libsrc/csg/bspline2d.cpp libsrc/include/mystdlib.h \
+  libsrc/include/csg.hpp libsrc/include/../csg/csg.hpp \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/meshing.hpp \
+  libsrc/include/../meshing/meshing.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/linalg.hpp libsrc/include/../linalg/linalg.hpp \
+  libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp \
+  libsrc/include/../csg/surface.hpp libsrc/include/../csg/solid.hpp \
+  libsrc/include/../csg/identify.hpp \
+  libsrc/include/../csg/singularref.hpp libsrc/include/../csg/csgeom.hpp \
+  libsrc/include/../csg/triapprox.hpp libsrc/include/../csg/algprim.hpp \
+  libsrc/include/../csg/brick.hpp libsrc/include/../csg/spline3d.hpp \
+  libsrc/include/../csg/manifold.hpp libsrc/include/../csg/curve2d.hpp \
+  libsrc/include/../csg/explicitcurve2d.hpp \
+  libsrc/include/../csg/gencyl.hpp libsrc/include/../csg/polyhedra.hpp \
+  libsrc/include/../csg/extrusion.hpp \
+  libsrc/include/../csg/revolution.hpp libsrc/include/../csg/specpoin.hpp \
+  libsrc/include/../csg/edgeflw.hpp libsrc/include/../csg/meshsurf.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+meshsurf.o: libsrc/csg/meshsurf.cpp libsrc/include/mystdlib.h \
+  libsrc/include/csg.hpp libsrc/include/../csg/csg.hpp \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/meshing.hpp \
+  libsrc/include/../meshing/meshing.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/linalg.hpp libsrc/include/../linalg/linalg.hpp \
+  libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp \
+  libsrc/include/../csg/surface.hpp libsrc/include/../csg/solid.hpp \
+  libsrc/include/../csg/identify.hpp \
+  libsrc/include/../csg/singularref.hpp libsrc/include/../csg/csgeom.hpp \
+  libsrc/include/../csg/triapprox.hpp libsrc/include/../csg/algprim.hpp \
+  libsrc/include/../csg/brick.hpp libsrc/include/../csg/spline3d.hpp \
+  libsrc/include/../csg/manifold.hpp libsrc/include/../csg/curve2d.hpp \
+  libsrc/include/../csg/explicitcurve2d.hpp \
+  libsrc/include/../csg/gencyl.hpp libsrc/include/../csg/polyhedra.hpp \
+  libsrc/include/../csg/extrusion.hpp \
+  libsrc/include/../csg/revolution.hpp libsrc/include/../csg/specpoin.hpp \
+  libsrc/include/../csg/edgeflw.hpp libsrc/include/../csg/meshsurf.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+csgeom.o: libsrc/csg/csgeom.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/csg.hpp \
+  libsrc/include/../csg/csg.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/meshing.hpp \
+  libsrc/include/../meshing/meshing.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp \
+  libsrc/include/../csg/surface.hpp libsrc/include/../csg/solid.hpp \
+  libsrc/include/../csg/identify.hpp \
+  libsrc/include/../csg/singularref.hpp libsrc/include/../csg/csgeom.hpp \
+  libsrc/include/../csg/triapprox.hpp libsrc/include/../csg/algprim.hpp \
+  libsrc/include/../csg/brick.hpp libsrc/include/../csg/spline3d.hpp \
+  libsrc/include/../csg/manifold.hpp libsrc/include/../csg/curve2d.hpp \
+  libsrc/include/../csg/explicitcurve2d.hpp \
+  libsrc/include/../csg/gencyl.hpp libsrc/include/../csg/polyhedra.hpp \
+  libsrc/include/../csg/extrusion.hpp \
+  libsrc/include/../csg/revolution.hpp libsrc/include/../csg/specpoin.hpp \
+  libsrc/include/../csg/edgeflw.hpp libsrc/include/../csg/meshsurf.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+polyhedra.o: libsrc/csg/polyhedra.cpp libsrc/include/mystdlib.h \
+  libsrc/include/linalg.hpp libsrc/include/../linalg/linalg.hpp \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp \
+  libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/csg.hpp \
+  libsrc/include/../csg/csg.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/meshing.hpp \
+  libsrc/include/../meshing/meshing.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp \
+  libsrc/include/../csg/surface.hpp libsrc/include/../csg/solid.hpp \
+  libsrc/include/../csg/identify.hpp \
+  libsrc/include/../csg/singularref.hpp libsrc/include/../csg/csgeom.hpp \
+  libsrc/include/../csg/triapprox.hpp libsrc/include/../csg/algprim.hpp \
+  libsrc/include/../csg/brick.hpp libsrc/include/../csg/spline3d.hpp \
+  libsrc/include/../csg/manifold.hpp libsrc/include/../csg/curve2d.hpp \
+  libsrc/include/../csg/explicitcurve2d.hpp \
+  libsrc/include/../csg/gencyl.hpp libsrc/include/../csg/polyhedra.hpp \
+  libsrc/include/../csg/extrusion.hpp \
+  libsrc/include/../csg/revolution.hpp libsrc/include/../csg/specpoin.hpp \
+  libsrc/include/../csg/edgeflw.hpp libsrc/include/../csg/meshsurf.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+curve2d.o: libsrc/csg/curve2d.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/csg.hpp \
+  libsrc/include/../csg/csg.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/meshing.hpp \
+  libsrc/include/../meshing/meshing.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/linalg.hpp libsrc/include/../linalg/linalg.hpp \
+  libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp \
+  libsrc/include/../csg/surface.hpp libsrc/include/../csg/solid.hpp \
+  libsrc/include/../csg/identify.hpp \
+  libsrc/include/../csg/singularref.hpp libsrc/include/../csg/csgeom.hpp \
+  libsrc/include/../csg/triapprox.hpp libsrc/include/../csg/algprim.hpp \
+  libsrc/include/../csg/brick.hpp libsrc/include/../csg/spline3d.hpp \
+  libsrc/include/../csg/manifold.hpp libsrc/include/../csg/curve2d.hpp \
+  libsrc/include/../csg/explicitcurve2d.hpp \
+  libsrc/include/../csg/gencyl.hpp libsrc/include/../csg/polyhedra.hpp \
+  libsrc/include/../csg/extrusion.hpp \
+  libsrc/include/../csg/revolution.hpp libsrc/include/../csg/specpoin.hpp \
+  libsrc/include/../csg/edgeflw.hpp libsrc/include/../csg/meshsurf.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+singularref.o: libsrc/csg/singularref.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/csg.hpp \
+  libsrc/include/../csg/csg.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/meshing.hpp \
+  libsrc/include/../meshing/meshing.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp \
+  libsrc/include/../csg/surface.hpp libsrc/include/../csg/solid.hpp \
+  libsrc/include/../csg/identify.hpp \
+  libsrc/include/../csg/singularref.hpp libsrc/include/../csg/csgeom.hpp \
+  libsrc/include/../csg/triapprox.hpp libsrc/include/../csg/algprim.hpp \
+  libsrc/include/../csg/brick.hpp libsrc/include/../csg/spline3d.hpp \
+  libsrc/include/../csg/manifold.hpp libsrc/include/../csg/curve2d.hpp \
+  libsrc/include/../csg/explicitcurve2d.hpp \
+  libsrc/include/../csg/gencyl.hpp libsrc/include/../csg/polyhedra.hpp \
+  libsrc/include/../csg/extrusion.hpp \
+  libsrc/include/../csg/revolution.hpp libsrc/include/../csg/specpoin.hpp \
+  libsrc/include/../csg/edgeflw.hpp libsrc/include/../csg/meshsurf.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+edgeflw.o: libsrc/csg/edgeflw.cpp libsrc/include/mystdlib.h \
+  libsrc/include/meshing.hpp libsrc/include/../meshing/meshing.hpp \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp libsrc/include/csg.hpp \
+  libsrc/include/../csg/csg.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../csg/surface.hpp libsrc/include/../csg/solid.hpp \
+  libsrc/include/../csg/identify.hpp \
+  libsrc/include/../csg/singularref.hpp libsrc/include/../csg/csgeom.hpp \
+  libsrc/include/../csg/triapprox.hpp libsrc/include/../csg/algprim.hpp \
+  libsrc/include/../csg/brick.hpp libsrc/include/../csg/spline3d.hpp \
+  libsrc/include/../csg/manifold.hpp libsrc/include/../csg/curve2d.hpp \
+  libsrc/include/../csg/explicitcurve2d.hpp \
+  libsrc/include/../csg/gencyl.hpp libsrc/include/../csg/polyhedra.hpp \
+  libsrc/include/../csg/extrusion.hpp \
+  libsrc/include/../csg/revolution.hpp libsrc/include/../csg/specpoin.hpp \
+  libsrc/include/../csg/edgeflw.hpp libsrc/include/../csg/meshsurf.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+solid.o: libsrc/csg/solid.cpp libsrc/include/mystdlib.h \
+  libsrc/include/linalg.hpp libsrc/include/../linalg/linalg.hpp \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp \
+  libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/csg.hpp \
+  libsrc/include/../csg/csg.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/meshing.hpp \
+  libsrc/include/../meshing/meshing.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp \
+  libsrc/include/../csg/surface.hpp libsrc/include/../csg/solid.hpp \
+  libsrc/include/../csg/identify.hpp \
+  libsrc/include/../csg/singularref.hpp libsrc/include/../csg/csgeom.hpp \
+  libsrc/include/../csg/triapprox.hpp libsrc/include/../csg/algprim.hpp \
+  libsrc/include/../csg/brick.hpp libsrc/include/../csg/spline3d.hpp \
+  libsrc/include/../csg/manifold.hpp libsrc/include/../csg/curve2d.hpp \
+  libsrc/include/../csg/explicitcurve2d.hpp \
+  libsrc/include/../csg/gencyl.hpp libsrc/include/../csg/polyhedra.hpp \
+  libsrc/include/../csg/extrusion.hpp \
+  libsrc/include/../csg/revolution.hpp libsrc/include/../csg/specpoin.hpp \
+  libsrc/include/../csg/edgeflw.hpp libsrc/include/../csg/meshsurf.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+explicitcurve2d.o: libsrc/csg/explicitcurve2d.cpp \
+  libsrc/include/mystdlib.h libsrc/include/csg.hpp \
+  libsrc/include/../csg/csg.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/meshing.hpp \
+  libsrc/include/../meshing/meshing.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/linalg.hpp libsrc/include/../linalg/linalg.hpp \
+  libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp \
+  libsrc/include/../csg/surface.hpp libsrc/include/../csg/solid.hpp \
+  libsrc/include/../csg/identify.hpp \
+  libsrc/include/../csg/singularref.hpp libsrc/include/../csg/csgeom.hpp \
+  libsrc/include/../csg/triapprox.hpp libsrc/include/../csg/algprim.hpp \
+  libsrc/include/../csg/brick.hpp libsrc/include/../csg/spline3d.hpp \
+  libsrc/include/../csg/manifold.hpp libsrc/include/../csg/curve2d.hpp \
+  libsrc/include/../csg/explicitcurve2d.hpp \
+  libsrc/include/../csg/gencyl.hpp libsrc/include/../csg/polyhedra.hpp \
+  libsrc/include/../csg/extrusion.hpp \
+  libsrc/include/../csg/revolution.hpp libsrc/include/../csg/specpoin.hpp \
+  libsrc/include/../csg/edgeflw.hpp libsrc/include/../csg/meshsurf.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+specpoin.o: libsrc/csg/specpoin.cpp libsrc/include/mystdlib.h \
+  libsrc/include/meshing.hpp libsrc/include/../meshing/meshing.hpp \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp libsrc/include/csg.hpp \
+  libsrc/include/../csg/csg.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../csg/surface.hpp libsrc/include/../csg/solid.hpp \
+  libsrc/include/../csg/identify.hpp \
+  libsrc/include/../csg/singularref.hpp libsrc/include/../csg/csgeom.hpp \
+  libsrc/include/../csg/triapprox.hpp libsrc/include/../csg/algprim.hpp \
+  libsrc/include/../csg/brick.hpp libsrc/include/../csg/spline3d.hpp \
+  libsrc/include/../csg/manifold.hpp libsrc/include/../csg/curve2d.hpp \
+  libsrc/include/../csg/explicitcurve2d.hpp \
+  libsrc/include/../csg/gencyl.hpp libsrc/include/../csg/polyhedra.hpp \
+  libsrc/include/../csg/extrusion.hpp \
+  libsrc/include/../csg/revolution.hpp libsrc/include/../csg/specpoin.hpp \
+  libsrc/include/../csg/edgeflw.hpp libsrc/include/../csg/meshsurf.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+gencyl.o: libsrc/csg/gencyl.cpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mystdlib.h \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp \
+  libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/csg.hpp \
+  libsrc/include/../csg/csg.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/meshing.hpp \
+  libsrc/include/../meshing/meshing.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp \
+  libsrc/include/../csg/surface.hpp libsrc/include/../csg/solid.hpp \
+  libsrc/include/../csg/identify.hpp \
+  libsrc/include/../csg/singularref.hpp libsrc/include/../csg/csgeom.hpp \
+  libsrc/include/../csg/triapprox.hpp libsrc/include/../csg/algprim.hpp \
+  libsrc/include/../csg/brick.hpp libsrc/include/../csg/spline3d.hpp \
+  libsrc/include/../csg/manifold.hpp libsrc/include/../csg/curve2d.hpp \
+  libsrc/include/../csg/explicitcurve2d.hpp \
+  libsrc/include/../csg/gencyl.hpp libsrc/include/../csg/polyhedra.hpp \
+  libsrc/include/../csg/extrusion.hpp \
+  libsrc/include/../csg/revolution.hpp libsrc/include/../csg/specpoin.hpp \
+  libsrc/include/../csg/edgeflw.hpp libsrc/include/../csg/meshsurf.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+revolution.o: libsrc/csg/revolution.cpp libsrc/include/mystdlib.h \
+  libsrc/include/linalg.hpp libsrc/include/../linalg/linalg.hpp \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp \
+  libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/csg.hpp \
+  libsrc/include/../csg/csg.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/meshing.hpp \
+  libsrc/include/../meshing/meshing.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp \
+  libsrc/include/../csg/surface.hpp libsrc/include/../csg/solid.hpp \
+  libsrc/include/../csg/identify.hpp \
+  libsrc/include/../csg/singularref.hpp libsrc/include/../csg/csgeom.hpp \
+  libsrc/include/../csg/triapprox.hpp libsrc/include/../csg/algprim.hpp \
+  libsrc/include/../csg/brick.hpp libsrc/include/../csg/spline3d.hpp \
+  libsrc/include/../csg/manifold.hpp libsrc/include/../csg/curve2d.hpp \
+  libsrc/include/../csg/explicitcurve2d.hpp \
+  libsrc/include/../csg/gencyl.hpp libsrc/include/../csg/polyhedra.hpp \
+  libsrc/include/../csg/extrusion.hpp \
+  libsrc/include/../csg/revolution.hpp libsrc/include/../csg/specpoin.hpp \
+  libsrc/include/../csg/edgeflw.hpp libsrc/include/../csg/meshsurf.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+genmesh.o: libsrc/csg/genmesh.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/csg.hpp \
+  libsrc/include/../csg/csg.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/meshing.hpp \
+  libsrc/include/../meshing/meshing.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp \
+  libsrc/include/../csg/surface.hpp libsrc/include/../csg/solid.hpp \
+  libsrc/include/../csg/identify.hpp \
+  libsrc/include/../csg/singularref.hpp libsrc/include/../csg/csgeom.hpp \
+  libsrc/include/../csg/triapprox.hpp libsrc/include/../csg/algprim.hpp \
+  libsrc/include/../csg/brick.hpp libsrc/include/../csg/spline3d.hpp \
+  libsrc/include/../csg/manifold.hpp libsrc/include/../csg/curve2d.hpp \
+  libsrc/include/../csg/explicitcurve2d.hpp \
+  libsrc/include/../csg/gencyl.hpp libsrc/include/../csg/polyhedra.hpp \
+  libsrc/include/../csg/extrusion.hpp \
+  libsrc/include/../csg/revolution.hpp libsrc/include/../csg/specpoin.hpp \
+  libsrc/include/../csg/edgeflw.hpp libsrc/include/../csg/meshsurf.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+spline3d.o: libsrc/csg/spline3d.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/csg.hpp \
+  libsrc/include/../csg/csg.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/meshing.hpp \
+  libsrc/include/../meshing/meshing.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp \
+  libsrc/include/../csg/surface.hpp libsrc/include/../csg/solid.hpp \
+  libsrc/include/../csg/identify.hpp \
+  libsrc/include/../csg/singularref.hpp libsrc/include/../csg/csgeom.hpp \
+  libsrc/include/../csg/triapprox.hpp libsrc/include/../csg/algprim.hpp \
+  libsrc/include/../csg/brick.hpp libsrc/include/../csg/spline3d.hpp \
+  libsrc/include/../csg/manifold.hpp libsrc/include/../csg/curve2d.hpp \
+  libsrc/include/../csg/explicitcurve2d.hpp \
+  libsrc/include/../csg/gencyl.hpp libsrc/include/../csg/polyhedra.hpp \
+  libsrc/include/../csg/extrusion.hpp \
+  libsrc/include/../csg/revolution.hpp libsrc/include/../csg/specpoin.hpp \
+  libsrc/include/../csg/edgeflw.hpp libsrc/include/../csg/meshsurf.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+surface.o: libsrc/csg/surface.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/csg.hpp \
+  libsrc/include/../csg/csg.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/meshing.hpp \
+  libsrc/include/../meshing/meshing.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/linalg.hpp libsrc/include/../linalg/linalg.hpp \
+  libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp \
+  libsrc/include/../csg/surface.hpp libsrc/include/../csg/solid.hpp \
+  libsrc/include/../csg/identify.hpp \
+  libsrc/include/../csg/singularref.hpp libsrc/include/../csg/csgeom.hpp \
+  libsrc/include/../csg/triapprox.hpp libsrc/include/../csg/algprim.hpp \
+  libsrc/include/../csg/brick.hpp libsrc/include/../csg/spline3d.hpp \
+  libsrc/include/../csg/manifold.hpp libsrc/include/../csg/curve2d.hpp \
+  libsrc/include/../csg/explicitcurve2d.hpp \
+  libsrc/include/../csg/gencyl.hpp libsrc/include/../csg/polyhedra.hpp \
+  libsrc/include/../csg/extrusion.hpp \
+  libsrc/include/../csg/revolution.hpp libsrc/include/../csg/specpoin.hpp \
+  libsrc/include/../csg/edgeflw.hpp libsrc/include/../csg/meshsurf.hpp \
+  libsrc/include/../linalg/linalg.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+identify.o: libsrc/csg/identify.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/csg.hpp \
+  libsrc/include/../csg/csg.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/meshing.hpp \
+  libsrc/include/../meshing/meshing.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp \
+  libsrc/include/../csg/surface.hpp libsrc/include/../csg/solid.hpp \
+  libsrc/include/../csg/identify.hpp \
+  libsrc/include/../csg/singularref.hpp libsrc/include/../csg/csgeom.hpp \
+  libsrc/include/../csg/triapprox.hpp libsrc/include/../csg/algprim.hpp \
+  libsrc/include/../csg/brick.hpp libsrc/include/../csg/spline3d.hpp \
+  libsrc/include/../csg/manifold.hpp libsrc/include/../csg/curve2d.hpp \
+  libsrc/include/../csg/explicitcurve2d.hpp \
+  libsrc/include/../csg/gencyl.hpp libsrc/include/../csg/polyhedra.hpp \
+  libsrc/include/../csg/extrusion.hpp \
+  libsrc/include/../csg/revolution.hpp libsrc/include/../csg/specpoin.hpp \
+  libsrc/include/../csg/edgeflw.hpp libsrc/include/../csg/meshsurf.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+triapprox.o: libsrc/csg/triapprox.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/csg.hpp \
+  libsrc/include/../csg/csg.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/meshing.hpp \
+  libsrc/include/../meshing/meshing.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp \
+  libsrc/include/../csg/surface.hpp libsrc/include/../csg/solid.hpp \
+  libsrc/include/../csg/identify.hpp \
+  libsrc/include/../csg/singularref.hpp libsrc/include/../csg/csgeom.hpp \
+  libsrc/include/../csg/triapprox.hpp libsrc/include/../csg/algprim.hpp \
+  libsrc/include/../csg/brick.hpp libsrc/include/../csg/spline3d.hpp \
+  libsrc/include/../csg/manifold.hpp libsrc/include/../csg/curve2d.hpp \
+  libsrc/include/../csg/explicitcurve2d.hpp \
+  libsrc/include/../csg/gencyl.hpp libsrc/include/../csg/polyhedra.hpp \
+  libsrc/include/../csg/extrusion.hpp \
+  libsrc/include/../csg/revolution.hpp libsrc/include/../csg/specpoin.hpp \
+  libsrc/include/../csg/edgeflw.hpp libsrc/include/../csg/meshsurf.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+geom2dmesh.o: libsrc/geom2d/geom2dmesh.cpp libsrc/include/mystdlib.h \
+  libsrc/include/csg.hpp libsrc/include/../csg/csg.hpp \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/meshing.hpp \
+  libsrc/include/../meshing/meshing.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/linalg.hpp libsrc/include/../linalg/linalg.hpp \
+  libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp \
+  libsrc/include/../csg/surface.hpp libsrc/include/../csg/solid.hpp \
+  libsrc/include/../csg/identify.hpp \
+  libsrc/include/../csg/singularref.hpp libsrc/include/../csg/csgeom.hpp \
+  libsrc/include/../csg/triapprox.hpp libsrc/include/../csg/algprim.hpp \
+  libsrc/include/../csg/brick.hpp libsrc/include/../csg/spline3d.hpp \
+  libsrc/include/../csg/manifold.hpp libsrc/include/../csg/curve2d.hpp \
+  libsrc/include/../csg/explicitcurve2d.hpp \
+  libsrc/include/../csg/gencyl.hpp libsrc/include/../csg/polyhedra.hpp \
+  libsrc/include/../csg/extrusion.hpp \
+  libsrc/include/../csg/revolution.hpp libsrc/include/../csg/specpoin.hpp \
+  libsrc/include/../csg/edgeflw.hpp libsrc/include/../csg/meshsurf.hpp \
+  libsrc/include/geometry2d.hpp libsrc/include/../geom2d/geometry2d.hpp \
+  libsrc/include/../gprim/gprim.hpp libsrc/include/../geom2d/spline2d.hpp \
+  libsrc/include/../geom2d/splinegeometry2.hpp \
+  libsrc/include/../geom2d/geom2dmesh.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+spline2d.o: libsrc/geom2d/spline2d.cpp libsrc/include/mystdlib.h \
+  libsrc/include/csg.hpp libsrc/include/../csg/csg.hpp \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/meshing.hpp \
+  libsrc/include/../meshing/meshing.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/linalg.hpp libsrc/include/../linalg/linalg.hpp \
+  libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp \
+  libsrc/include/../csg/surface.hpp libsrc/include/../csg/solid.hpp \
+  libsrc/include/../csg/identify.hpp \
+  libsrc/include/../csg/singularref.hpp libsrc/include/../csg/csgeom.hpp \
+  libsrc/include/../csg/triapprox.hpp libsrc/include/../csg/algprim.hpp \
+  libsrc/include/../csg/brick.hpp libsrc/include/../csg/spline3d.hpp \
+  libsrc/include/../csg/manifold.hpp libsrc/include/../csg/curve2d.hpp \
+  libsrc/include/../csg/explicitcurve2d.hpp \
+  libsrc/include/../csg/gencyl.hpp libsrc/include/../csg/polyhedra.hpp \
+  libsrc/include/../csg/extrusion.hpp \
+  libsrc/include/../csg/revolution.hpp libsrc/include/../csg/specpoin.hpp \
+  libsrc/include/../csg/edgeflw.hpp libsrc/include/../csg/meshsurf.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/geom2d/spline2d.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+splinegeometry2.o: libsrc/geom2d/splinegeometry2.cpp \
+  libsrc/include/mystdlib.h libsrc/include/csg.hpp \
+  libsrc/include/../csg/csg.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/meshing.hpp \
+  libsrc/include/../meshing/meshing.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/linalg.hpp libsrc/include/../linalg/linalg.hpp \
+  libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp \
+  libsrc/include/../csg/surface.hpp libsrc/include/../csg/solid.hpp \
+  libsrc/include/../csg/identify.hpp \
+  libsrc/include/../csg/singularref.hpp libsrc/include/../csg/csgeom.hpp \
+  libsrc/include/../csg/triapprox.hpp libsrc/include/../csg/algprim.hpp \
+  libsrc/include/../csg/brick.hpp libsrc/include/../csg/spline3d.hpp \
+  libsrc/include/../csg/manifold.hpp libsrc/include/../csg/curve2d.hpp \
+  libsrc/include/../csg/explicitcurve2d.hpp \
+  libsrc/include/../csg/gencyl.hpp libsrc/include/../csg/polyhedra.hpp \
+  libsrc/include/../csg/extrusion.hpp \
+  libsrc/include/../csg/revolution.hpp libsrc/include/../csg/specpoin.hpp \
+  libsrc/include/../csg/edgeflw.hpp libsrc/include/../csg/meshsurf.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/geom2d/spline2d.hpp \
+  libsrc/geom2d/splinegeometry2.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+genmesh2d.o: libsrc/geom2d/genmesh2d.cpp libsrc/include/mystdlib.h \
+  libsrc/include/csg.hpp libsrc/include/../csg/csg.hpp \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/meshing.hpp \
+  libsrc/include/../meshing/meshing.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/linalg.hpp libsrc/include/../linalg/linalg.hpp \
+  libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp \
+  libsrc/include/../csg/surface.hpp libsrc/include/../csg/solid.hpp \
+  libsrc/include/../csg/identify.hpp \
+  libsrc/include/../csg/singularref.hpp libsrc/include/../csg/csgeom.hpp \
+  libsrc/include/../csg/triapprox.hpp libsrc/include/../csg/algprim.hpp \
+  libsrc/include/../csg/brick.hpp libsrc/include/../csg/spline3d.hpp \
+  libsrc/include/../csg/manifold.hpp libsrc/include/../csg/curve2d.hpp \
+  libsrc/include/../csg/explicitcurve2d.hpp \
+  libsrc/include/../csg/gencyl.hpp libsrc/include/../csg/polyhedra.hpp \
+  libsrc/include/../csg/extrusion.hpp \
+  libsrc/include/../csg/revolution.hpp libsrc/include/../csg/specpoin.hpp \
+  libsrc/include/../csg/edgeflw.hpp libsrc/include/../csg/meshsurf.hpp \
+  libsrc/include/geometry2d.hpp libsrc/include/../geom2d/geometry2d.hpp \
+  libsrc/include/../gprim/gprim.hpp libsrc/include/../geom2d/spline2d.hpp \
+  libsrc/include/../geom2d/splinegeometry2.hpp \
+  libsrc/include/../geom2d/geom2dmesh.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+meshstlsurface.o: libsrc/stlgeom/meshstlsurface.cpp \
+  libsrc/include/mystdlib.h libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mydefs.hpp \
+  libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/meshing.hpp \
+  libsrc/include/../meshing/meshing.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp libsrc/stlgeom/stlgeom.hpp \
+  libsrc/include/../gprim/gprim.hpp libsrc/stlgeom/stltopology.hpp \
+  libsrc/stlgeom/stltool.hpp libsrc/stlgeom/stlline.hpp \
+  libsrc/stlgeom/meshstlsurface.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+stlline.o: libsrc/stlgeom/stlline.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/meshing.hpp \
+  libsrc/include/../meshing/meshing.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp libsrc/stlgeom/stlgeom.hpp \
+  libsrc/include/../gprim/gprim.hpp libsrc/stlgeom/stltopology.hpp \
+  libsrc/stlgeom/stltool.hpp libsrc/stlgeom/stlline.hpp \
+  libsrc/stlgeom/meshstlsurface.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+stltopology.o: libsrc/stlgeom/stltopology.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/meshing.hpp \
+  libsrc/include/../meshing/meshing.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp libsrc/stlgeom/stlgeom.hpp \
+  libsrc/include/../gprim/gprim.hpp libsrc/stlgeom/stltopology.hpp \
+  libsrc/stlgeom/stltool.hpp libsrc/stlgeom/stlline.hpp \
+  libsrc/stlgeom/meshstlsurface.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+stltool.o: libsrc/stlgeom/stltool.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/meshing.hpp \
+  libsrc/include/../meshing/meshing.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp libsrc/stlgeom/stlgeom.hpp \
+  libsrc/include/../gprim/gprim.hpp libsrc/stlgeom/stltopology.hpp \
+  libsrc/stlgeom/stltool.hpp libsrc/stlgeom/stlline.hpp \
+  libsrc/stlgeom/meshstlsurface.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+stlgeom.o: libsrc/stlgeom/stlgeom.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/meshing.hpp \
+  libsrc/include/../meshing/meshing.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp libsrc/stlgeom/stlgeom.hpp \
+  libsrc/include/../gprim/gprim.hpp libsrc/stlgeom/stltopology.hpp \
+  libsrc/stlgeom/stltool.hpp libsrc/stlgeom/stlline.hpp \
+  libsrc/stlgeom/meshstlsurface.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+stlgeomchart.o: libsrc/stlgeom/stlgeomchart.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/meshing.hpp \
+  libsrc/include/../meshing/meshing.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp libsrc/stlgeom/stlgeom.hpp \
+  libsrc/include/../gprim/gprim.hpp libsrc/stlgeom/stltopology.hpp \
+  libsrc/stlgeom/stltool.hpp libsrc/stlgeom/stlline.hpp \
+  libsrc/stlgeom/meshstlsurface.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+stlgeommesh.o: libsrc/stlgeom/stlgeommesh.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/meshing.hpp \
+  libsrc/include/../meshing/meshing.hpp libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp libsrc/stlgeom/stlgeom.hpp \
+  libsrc/include/../gprim/gprim.hpp libsrc/stlgeom/stltopology.hpp \
+  libsrc/stlgeom/stltool.hpp libsrc/stlgeom/stlline.hpp \
+  libsrc/stlgeom/meshstlsurface.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+moveablemem.o: libsrc/general/moveablemem.cpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mystdlib.h \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+ngexception.o: libsrc/general/ngexception.cpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mystdlib.h \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+table.o: libsrc/general/table.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+optmem.o: libsrc/general/optmem.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+spbita2d.o: libsrc/general/spbita2d.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+hashtabl.o: libsrc/general/hashtabl.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+sort.o: libsrc/general/sort.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+flags.o: libsrc/general/flags.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+seti.o: libsrc/general/seti.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+bitarray.o: libsrc/general/bitarray.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+array.o: libsrc/general/array.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+symbolta.o: libsrc/general/symbolta.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+mystring.o: libsrc/general/mystring.cpp libsrc/include/mystdlib.h \
+  libsrc/include/myadt.hpp libsrc/include/../general/myadt.hpp \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp
+# 1 "/Users/geuzaine/.gmsh/Netgen//"
+nglib_addon.o: nglib_addon.cpp libsrc/include/meshing.hpp \
+  libsrc/include/../meshing/meshing.hpp libsrc/include/myadt.hpp \
+  libsrc/include/../general/myadt.hpp libsrc/include/mystdlib.h \
+  libsrc/include/mydefs.hpp libsrc/include/../general/ngexception.hpp \
+  libsrc/include/../general/parthreads.hpp \
+  libsrc/include/../general/moveablemem.hpp \
+  libsrc/include/../general/dynamicmem.hpp \
+  libsrc/include/../general/template.hpp \
+  libsrc/include/../general/array.hpp libsrc/include/../general/table.hpp \
+  libsrc/include/../general/hashtabl.hpp \
+  libsrc/include/../general/symbolta.hpp \
+  libsrc/include/../general/bitarray.hpp \
+  libsrc/include/../general/flags.hpp \
+  libsrc/include/../general/spbita2d.hpp \
+  libsrc/include/../general/seti.hpp libsrc/include/../general/optmem.hpp \
+  libsrc/include/../general/autoptr.hpp \
+  libsrc/include/../general/sort.hpp libsrc/include/../general/stack.hpp \
+  libsrc/include/../general/mystring.hpp libsrc/include/gprim.hpp \
+  libsrc/include/../gprim/gprim.hpp \
+  libsrc/include/../gprim/geomobjects.hpp \
+  libsrc/include/../gprim/geomops.hpp \
+  libsrc/include/../gprim/geomfuncs.hpp \
+  libsrc/include/../gprim/geom2d.hpp libsrc/include/../gprim/geom3d.hpp \
+  libsrc/include/../gprim/geomtest3d.hpp \
+  libsrc/include/../gprim/transform3d.hpp \
+  libsrc/include/../gprim/adtree.hpp libsrc/include/linalg.hpp \
+  libsrc/include/../linalg/linalg.hpp libsrc/include/../linalg/vector.hpp \
+  libsrc/include/../linalg/densemat.hpp \
+  libsrc/include/../linalg/polynomial.hpp libsrc/include/opti.hpp \
+  libsrc/include/../opti/opti.hpp \
+  libsrc/include/../meshing/msghandler.hpp \
+  libsrc/include/../meshing/meshtype.hpp \
+  libsrc/include/../meshing/localh.hpp \
+  libsrc/include/../meshing/meshclass.hpp \
+  libsrc/include/../meshing/global.hpp \
+  libsrc/include/../meshing/meshtool.hpp \
+  libsrc/include/../meshing/ruler2.hpp \
+  libsrc/include/../meshing/adfront2.hpp \
+  libsrc/include/../meshing/meshing2.hpp \
+  libsrc/include/../meshing/improve2.hpp \
+  libsrc/include/../meshing/geomsearch.hpp \
+  libsrc/include/../meshing/adfront3.hpp \
+  libsrc/include/../meshing/ruler3.hpp \
+  libsrc/include/../meshing/meshing3.hpp \
+  libsrc/include/../meshing/improve3.hpp \
+  libsrc/include/../meshing/findip.hpp \
+  libsrc/include/../meshing/topology.hpp \
+  libsrc/include/../meshing/curvedelems.hpp \
+  libsrc/include/../meshing/bisect.hpp \
+  libsrc/include/../meshing/clusters.hpp \
+  libsrc/include/../meshing/meshfunc.hpp \
+  libsrc/include/../meshing/hprefinement.hpp \
+  libsrc/include/../meshing/boundarylayer.hpp \
+  libsrc/include/../meshing/specials.hpp libsrc/interface/nglib.h \
+  ../Common/Message.h
diff --git a/contrib/Netgen/README b/contrib/Netgen/README
new file mode 100644
index 0000000000..08e5573ac2
--- /dev/null
+++ b/contrib/Netgen/README
@@ -0,0 +1,72 @@
+
+This directory may contain a slightly modified version of Joachim
+Sch\"oberl's NETGEN mesh generator:
+
+- only the libsrc directory was kept from the original distribution
+
+- the file meshing/improve2.cpp was slightly modified to fix build
+  problems on machines without TCL/TK
+
+- the file meshing/meshtype.hpp was slightly modified so that it
+  compiles with ISO-C++ complient compilers
+
+**IMPORTANT NOTICE** 
+
+NETGEN requires the boundary mesh to be oriented with exterior
+pointing normals. You HAVE TO define your geometry so that this
+criterion is fulfilled (i.e., correctly orient the geometry surfaces
+and the surface loops).
+
+**************************************************************
+
+From NETGEN's documentation:
+
+What is NETGEN
+==============
+
+NETGEN is an automatic mesh generation tool for two and three
+dimensions. Netgen is open source under the conditions of the LGPL.
+It comes as stand alone programme with graphical user interface, or as
+C++ library to be linked into an other application.  Netgen is
+available for Unix/Linux and Windows 98/NT.  Netgen generates
+triangular or quadrilateral meshes in 2D, and tetrahedral meshes in
+3D. The input for 2D is described by spline curves, and the input for
+3D problems is either defined by constructive solid geometries (CSG)
+or by the standard STL file format.  NETGEN contains modules for mesh
+optimization and hierarchical mesh refinement. Curved elements are
+supported of arbitrary order.
+
+The history of NETGEN
+=====================
+
+The NETGEN project was started 1994 in the master's programme of
+Joachim Sch\"oberl, under supervision of Prof. Ulrich Langer, at the
+Department of Computational Mathematics and Optimization, University
+Linz, Austria.  Its further development was supported by the Austrian
+science Fund ``Fonds zur F\"orderung der wissenschaftlichen
+Forschung'' (http://www.fwf.ac.at) under projects P 10643-TEC and SFB
+1306.  The current home of Netgen is the Start project ``hp-FEM''
+(http://www.hpfem.jku.at) granted by the FWF.
+
+Special thanks go to
+- Robert Gaisbauer: High order curved elements
+- Hannes Gerstmayr: Meshing of STL geometry
+
+How to receive NETGEN
+=====================
+
+NETGEN is available from the WEB at http://www.hpfem.jku.at/netgen
+You find there source code releases for Linux/Unix/Windows, as well
+as compiled versions for Windows. You can use CVS access to receive
+the most up to date version.
+
+**************************************************************
+
+From NETGEN's README.install file:
+
+Latest information is available from:
+http://www.sfb013.uni-linz.ac.at/~joachim/netgen
+
+People might have asked similar questions on
+https://www.sfb013.uni-linz.ac.at/mailman/listinfo/netgen
+(please note the s in https)
diff --git a/contrib/Netgen/VERSION b/contrib/Netgen/VERSION
new file mode 100644
index 0000000000..f56504b6db
--- /dev/null
+++ b/contrib/Netgen/VERSION
@@ -0,0 +1 @@
+NG Version 4.4
\ No newline at end of file
diff --git a/contrib/Netgen/libsrc/Makefile b/contrib/Netgen/libsrc/Makefile
new file mode 100644
index 0000000000..4bf820bc2a
--- /dev/null
+++ b/contrib/Netgen/libsrc/Makefile
@@ -0,0 +1,38 @@
+#
+#
+appl = NETGEN
+.default all:
+#
+#	
+all:
+	@ (cd linalg; $(MAKE) -f Makefile) 
+	@ (cd general; $(MAKE) -f Makefile)  
+	@ (cd gprim; $(MAKE) -f Makefile)
+	@ (cd csg; $(MAKE) -f Makefile)  
+	@ (cd geom2d; $(MAKE) -f Makefile)
+	@ (cd stlgeom; $(MAKE) -f Makefile)
+	@ (cd occ; $(MAKE) -f Makefile)
+	@ (cd meshing; $(MAKE) -f Makefile)
+	@ (cd opti; $(MAKE) -f Makefile)
+	@ (cd visualization; $(MAKE) -f Makefile)
+	@ (cd interface; $(MAKE) -f Makefile)
+#
+#	@ (cd step; $(MAKE) -f Makefile)
+#	@ (cd stepgeom; $(MAKE) -f Makefile)
+#	@ (cd graphics; $(MAKE) -f Makefile)
+
+tar:
+	tar cvf ../../libsrc.tar Makefile
+	tar rf ../../libsrc.tar linalg/Makefile linalg/*.hh linalg/*.cc
+	tar rf ../../libsrc.tar general/Makefile general/*.hh general/*.cc
+	tar rf ../../libsrc.tar gprim/Makefile gprim/*.hh gprim/*.cc
+	tar rf ../../libsrc.tar csg/Makefile csg/*.hh csg/*.cc
+	tar rf ../../libsrc.tar stlgeom/Makefile stlgeom/*.hh stlgeom/*.cc
+	tar rf ../../libsrc.tar occ/Makefile occ/*.h* occ/*.c*
+	tar rf ../../libsrc.tar meshing/Makefile meshing/*.hh meshing/*.cc meshing/*.h
+	tar rf ../../libsrc.tar opti/Makefile opti/*.hh opti/*.cc
+	tar rf ../../libsrc.tar step/Makefile step/*.h step/*.cc
+	tar rf ../../libsrc.tar stepgeom/Makefile stepgeom/*.hh stepgeom/*.cc
+	tar tf ../../libsrc.tar include/*.h include/*.hh
+	gzip -9 ../../libsrc.tar
+
diff --git a/contrib/Netgen/libsrc/csg/Makefile b/contrib/Netgen/libsrc/csg/Makefile
new file mode 100644
index 0000000000..e8c51dc218
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/Makefile
@@ -0,0 +1,26 @@
+#
+# Makefile for geometric library
+#
+src =  csgparser.cpp algprim.cpp curve2d.cpp brick.cpp  \
+	solid.cpp spline3d.cpp surface.cpp bspline2d.cpp \
+	explicitcurve2d.cpp gencyl.cpp csgeom.cpp polyhedra.cpp extrusion.cpp revolution.cpp  \
+	manifold.cpp curve2d.cpp triapprox.cpp identify.cpp \
+	singularref.cpp  \
+	edgeflw.cpp specpoin.cpp meshsurf.cpp genmesh.cpp 
+#
+#  lex.yy.cpp geometry.cpp
+# 
+lib = csg
+libpath = libsrc/csg
+#
+#
+include ../makefile.inc
+#
+# geometry.cpp : geometry.yy 
+#	bison -d -o geometry.c geometry.yy
+#	mv -f geometry.c geometry.cpp
+#
+# lex.yy.cpp : geometry.yy geometry.ll
+#	flex  -+ -d -I geometry.ll
+#	mv lex.yy.cc lex.yy.cpp
+
diff --git a/contrib/Netgen/libsrc/csg/algprim.cpp b/contrib/Netgen/libsrc/csg/algprim.cpp
new file mode 100644
index 0000000000..1123706b1e
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/algprim.cpp
@@ -0,0 +1,1389 @@
+#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 << endl;
+}
+
+
+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)
+{
+  p = ap;
+  n = an;
+  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;
+
+  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));
+}
+
+
+
+void Plane :: GetPrimitiveData (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);
+
+  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 :: 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
+{
+  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
+{
+  /*
+  Vec<3> p1p;
+  Point<2> pplane2 = pplane;
+  
+  pplane2 *= h;
+  p1p = pplane2(0) * ex + pplane2(1) * ey;
+  p3d = p1 + p1p;
+  */
+  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> p;
+
+  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++)
+	{
+	  p = box.GetPointNr (i);
+	  val = Plane::CalcFunctionValue (p);
+	  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++)
+	{
+	  p = box.GetPointNr (i);
+	  val = Plane::CalcFunctionValue (p);
+	  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;
+  
+  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 (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);
+
+  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;
+}
+
+
+
+
+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 i, j;
+  double lg, bg;
+  int n = int(facets) + 1;  
+
+  for (j = 0; j <= n; j++)
+    for (i = 0; i <= n; i++)
+      {
+	lg = 2 * M_PI * double (i) / n;
+	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 (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));
+      }
+}
+
+
+
+
+
+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);
+}
+
+Point<3> Ellipsoid :: GetSurfacePoint () const
+{
+  return a + v1;
+}
+
+
+
+void Ellipsoid :: GetTriangleApproximation 
+(TriangleApproximation & tas, 
+ const Box<3> & boundingbox, double facets) const
+{
+  int i, j;
+  double lg, bg;
+  int n = int(facets) + 1;  
+
+  for (j = 0; j <= n; j++)
+    for (i = 0; i <= n; i++)
+      {
+	lg = 2 * M_PI * double (i) / n;
+	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 (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));
+      }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+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 (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 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) 
+	  + ((r * cos(lg)) * n1) 
+	  + ((r * 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));
+      }
+}
+
+
+
+
+
+
+
+
+
+EllipticCylinder :: 
+EllipticCylinder (const Point<3> & aa,
+		  const Vec<3> & avl, const Vec<3> & avs)
+{
+  a = aa;
+  vl = avl;
+  vs = avs;
+
+  CalcData();
+  Print (cout);
+}
+
+
+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 = va * hvl + va * hvs - 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/vs.Length2 ();
+}
+
+Point<3> EllipticCylinder :: GetSurfacePoint () const
+{
+  return a + vl;
+}
+
+
+
+void EllipticCylinder :: GetTriangleApproximation 
+(TriangleApproximation & tas, 
+ const Box<3> & boundingbox, double facets) const
+{
+  int i, j;
+  double lg, bg;
+  int n = int(facets) + 1;  
+
+  Vec<3> axis = Cross (vl, vs);
+
+  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 * axis)
+	  + cos(lg) * vl + sin(lg) * vs;
+
+	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));
+      }
+}
+
+
+
+
+
+
+
+
+
+
+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 (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
+
+
+  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;
+
+  // (*testout) << "t0vec = " << t0vec << " t0 = " << t0 << endl;
+  // (*testout) << "t1vec = " << t1vec << " t1 = " << t1 << endl;
+  // PrintCoeff (*testout);
+}
+
+
+INSOLID_TYPE Cone :: BoxInSolid (const BoxSphere<3> & box) const
+{
+  double rp, dist;
+
+  Vec<3> cv(box.Center());
+
+  rp = cv * t1vec + t1;
+  dist = sqrt (CalcFunctionValue(box.Center()) + rp * rp) - rp;
+
+  if (dist - box.Diam() > 0) return IS_OUTSIDE;
+  if (dist + box.Diam() < 0) return IS_INSIDE;
+  return DOES_INTERSECT;
+}
+
+
+double Cone :: HesseNorm () const
+{
+  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/(3*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));
+      }
+}
+}
diff --git a/contrib/Netgen/libsrc/csg/algprim.hpp b/contrib/Netgen/libsrc/csg/algprim.hpp
new file mode 100644
index 0000000000..aeb829f9c1
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/algprim.hpp
@@ -0,0 +1,338 @@
+#ifndef FILE_ALGPRIM
+#define FILE_ALGPRIM
+
+
+/**************************************************************************/
+/* File:   algprim.hh                                                     */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   1. Dez. 95                                                     */
+/**************************************************************************/
+
+/*
+
+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;
+public:
+  ///
+  Plane (const Point<3> & ap, Vec<3> an);
+
+  virtual void GetPrimitiveData (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;
+
+};
+
+// typedef Plane Plane;
+
+
+///
+class Sphere : public QuadraticSurface
+{
+  ///
+  Point<3> c;
+  ///
+  double r;
+public:
+  ///
+  Sphere (const Point<3> & ac, double ar);
+
+  virtual void GetPrimitiveData (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> & 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);
+
+  virtual void GetPrimitiveData (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);
+
+  /*
+  static Primitive * CreateDefault ();
+  virtual void GetPrimitiveData (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;
+
+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 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;
+public:
+  ///
+  Cone (const Point<3> & aa, const Point<3> & ab, double ara, double arb);
+  ///
+  static Primitive * CreateDefault ();
+  virtual void GetPrimitiveData (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();
+};
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/csg/brick.cpp b/contrib/Netgen/libsrc/csg/brick.cpp
new file mode 100644
index 0000000000..f408e922f4
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/brick.cpp
@@ -0,0 +1,409 @@
+#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));
+}
+
+
+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;
+
+  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]->CalcFunctionValue (p);
+
+	  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] -> CalcFunctionValue (p);
+  for (int i = 1; i < 6; i++)
+    {
+      double val = faces[i] -> 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 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);
+  */
+}
+
+
+void Brick :: 
+GetPrimitiveData (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> p1 = pi[lface[i][0]-1];
+      const Point<3> p2 = pi[lface[i][1]-1];
+      const Point<3> p3 = pi[lface[i][2]-1];
+
+      Vec<3> n = Cross ((p2-p1), (p3-p1));
+      n.Normalize();
+      
+      for (j = 0; j < 3; j++)
+	{
+	  data[j] = p1(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;
+  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]->CalcFunctionValue (p);
+	  if (val > 0)  hasout = 1;
+	  else if (val < 0)  hasin = 1;
+	}
+      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 0000000000..11106b66d3
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/brick.hpp
@@ -0,0 +1,98 @@
+#ifndef FILE_BRICK
+#define FILE_BRICK
+
+
+/**************************************************************************/
+/* File:   brick.hh                                                       */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   11. Mar. 98                                                    */
+/**************************************************************************/
+
+/*
+
+  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;
+
+public:
+  Brick (Point<3> ap1, Point<3> ap2, Point<3> ap3, Point<3> ap4);
+  virtual ~Brick ();
+  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;
+
+  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 (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 0000000000..93b5e89621
--- /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 0000000000..e150860232
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/csg.hpp
@@ -0,0 +1,48 @@
+#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>
+
+namespace netgen
+{
+#include "surface.hpp"
+#include "solid.hpp"
+#include "identify.hpp"
+#include "singularref.hpp"
+#include "csgeom.hpp"
+
+#ifndef SMALLLIB
+#define _INCLUDE_MORE
+#endif
+#ifdef LINUX
+#define _INCLUDE_MORE
+#endif
+
+#ifdef _INCLUDE_MORE
+#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
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/csg/csgeom.cpp b/contrib/Netgen/libsrc/csg/csgeom.cpp
new file mode 100644
index 0000000000..f7c25ee9c6
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/csgeom.cpp
@@ -0,0 +1,1020 @@
+#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);
+  }
+
+  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), filename("")
+  {
+    ;
+  }
+
+  CSGeometry :: CSGeometry (const string & afilename)
+    : boundingbox (default_boundingbox),
+      identicsurfaces (100), filename(afilename)
+  {
+    changeval++;
+  }
+
+  CSGeometry :: ~CSGeometry ()
+  {
+    Clean();
+  }
+
+
+  void CSGeometry :: Clean ()
+  {
+    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 < surfaces.Size(); i++)
+      delete surfaces[i];
+    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();
+
+    changeval++;
+  }
+
+
+
+
+
+  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)
+      {
+	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 (ostream & ost) 
+  {
+    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 :: 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)
+  { 
+    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);
+      }
+  }
+
+  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;
+  }
+
+
+
+
+
+
+
+
+  class RemoveDummyIterator : public SolidIterator
+  {
+  public:
+  
+    RemoveDummyIterator() { ; }
+    virtual ~RemoveDummyIterator() { ; }
+    virtual void Do(Solid * sol);
+  };
+
+  void RemoveDummyIterator :: Do(Solid * sol)
+  {
+    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.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);
+	  }
+      }
+  }
+
+  void CSGeometry :: FindIdenticSurfaces (double eps)
+  {
+    int inv;
+    int nsurf = GetNSurf();
+
+    isidenticto.SetSize(nsurf);
+    for (int i = 0; i < nsurf; i++)
+      isidenticto[i] = i;
+  
+    for (int i = 0; i < nsurf; i++)
+      for (int j = i+1; j < nsurf; j++)
+	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 ::
+  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
+  {
+    Point<3> p2 = p + 1e-2 * v;
+    BoxSphere<3> box (p2, p2);
+    box.Increase (1e-3);
+    box.CalcDiamCenter();
+    GetIndependentSurfaceIndices (sol, box, locsurf);
+  }
+
+
+  void CSGeometry :: 
+  CalcTriangleApproximation(const Box<3> & boundingbox,
+			    double detail, double facets)
+  {
+    PrintMessage (1, "Calc Triangle Approximation");
+
+    //    FindIdenticSurfaces (1e-6);
+  
+    int ntlo = GetNTopLevelObjects();
+
+    for (int i = 0; i < triapprox.Size(); i++)
+      delete triapprox[i];
+    triapprox.SetSize (ntlo);
+
+    ARRAY<int> surfind;
+
+    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);
+
+		    RefineTriangleApprox (locsol, j, box, detail, 
+					  tria, *tams);
+		    delete locsol;
+		  }
+	      }
+	  }
+
+	tams->RemoveUnusedPoints ();
+	PrintMessage (2, "Object ", i, " has ", tams->GetNT(), " triangles");
+      }
+
+    Change();
+  }
+
+
+
+  void CSGeometry ::
+  RefineTriangleApprox (Solid * locsol, 
+			int surfind,
+			const BoxSphere<3> & box, 
+			double detail,
+			const TATriangle & tria, 
+			TriangleApproximation & tams)
+  {
+    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.Array();
+
+    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);
+	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);
+	      }
+	  
+	    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;
+		    if ( fabs (f1-f2) > 1e-20 )
+		      {
+			double l2 = -f1/(f2-f1);
+			double 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;
+
+		    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]);
+		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)
+      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-6);
+	nbox.CalcDiamCenter();
+
+	Solid * nsol = locsol -> GetReducedSolid (nbox);
+
+	if (nsol)
+	  {
+	    RefineTriangleApprox (nsol, surfind, nbox, 
+				  detail, ntri, tams);
+	  
+	    delete nsol;
+	  }
+      }
+  }
+
+
+
+
+  class ClearVisitedIt : public SolidIterator
+  {
+  public:
+    ClearVisitedIt () { ; }
+    virtual ~ClearVisitedIt () { ; }
+
+    virtual void Do (Solid * sol)
+    { 
+      sol -> visited = 0;
+    }
+  };
+
+
+  void CSGeometry :: 
+  IterateAllSolids (SolidIterator & it, int only_once)
+  {
+    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 0000000000..6e29a8163b
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/csgeom.hpp
@@ -0,0 +1,257 @@
+#ifndef FILE_CSGEOM
+#define FILE_CSGEOM
+
+/**************************************************************************/
+/* File:   csgeom.hh                                                      */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   27. Nov. 97                                                    */
+/**************************************************************************/
+
+/**
+  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
+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; }
+};
+
+
+/**
+   CSGeometry has the whole geometric information
+ */
+class CSGeometry
+{
+private:
+  /// all surfaces
+  SYMBOLTABLE<Surface*> surfaces;
+
+  /// all named solids
+  SYMBOLTABLE<Solid*> solids;
+
+  /// all top level objects: solids and surfaces
+  ARRAY<TopLevelObject*> toplevelobjects;
+
+  /// additional points specified by user
+  ARRAY<Point<3> > userpoints;
+
+  /// 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, ...)
+
+
+
+  /// filename of inputfile
+  string filename;
+
+public:
+  CSGeometry ();
+  CSGeometry (const string & afilename);
+  ~CSGeometry ();
+
+  void Clean ();
+
+  void Save (ostream & ost);
+  void Load (istream & ist);
+
+  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) { return solids[i]; }
+  const SYMBOLTABLE<Solid*> & GetSolids () const { return solids; }
+
+
+  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)
+  { 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)
+  { userpoints.Append (p); }
+  int GetNUserPoints () const
+  { return userpoints.Size(); }
+  const Point<3> & GetUserPoint (int nr) const
+  { return userpoints[nr]; }
+  
+
+  // quick implementations:
+  ARRAY<SingularFace*> singfaces;
+  ARRAY<SingularEdge*> singedges;
+  ARRAY<SingularPoint*> singpoints;
+  ARRAY<Identification*> identifications;
+
+  int GetNIdentifications () { return identifications.Size(); }
+  void AddIdentification (Identification * ident);
+
+
+  ///
+  void CalcTriangleApproximation(const Box<3> & boundingbox,
+				 double detail, double facets);
+
+  ///
+  void FindIdenticSurfaces (double eps);
+  ///
+  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;
+  ///
+  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, int only_once = 0);
+
+  void RefineTriangleApprox (Solid * locsol, 
+			     int surfind,
+			     const BoxSphere<3> & box, 
+			     double detail,
+			     const TATriangle & tria, 
+			     TriangleApproximation & tams);
+
+  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;
+
+
+
+  class BCModification {
+  public:
+    int si;
+    int tlonr;
+    int bcnr;
+  };
+  ARRAY<BCModification> bcmodifications;
+
+};
+#endif
+
diff --git a/contrib/Netgen/libsrc/csg/csgparser.cpp b/contrib/Netgen/libsrc/csg/csgparser.cpp
new file mode 100644
index 0000000000..b6d4878399
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/csgparser.cpp
@@ -0,0 +1,1156 @@
+#include <mystdlib.h>
+#include <myadt.hpp>
+
+#include <linalg.hpp>
+#include <csg.hpp>
+
+
+namespace netgen
+{
+  using 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_BOUNDINGBOX, TOK_BOUNDARYCONDITION,
+      TOK_END };
+
+  struct kwstruct
+  {
+    TOKEN_TYPE kw; 
+    char * name;
+  };
+
+  static kwstruct defkw[] =
+    {
+      { TOK_RECO,    "algebraic3d" },
+      { TOK_SOLID,   "solid" },
+      { TOK_TLO,     "tlo" },
+      { 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" },
+      { TOKEN_TYPE(0) }
+    };
+
+  enum PRIMITIVE_TYPE
+    {
+      TOK_SPHERE = 1, TOK_CYLINDER, TOK_PLANE, TOK_ELLIPTICCYLINDER, 
+      TOK_ELLIPSOID, TOK_CONE, 
+      TOK_ORTHOBRICK, TOK_POLYHEDRON, 
+      
+      TOK_TUBE, TOK_GENCYL, TOK_EXTRUSION, TOK_REVOLUTION,    // currently, out of order
+
+      TOK_TRANSLATE, TOK_MULTITRANSLATE, TOK_ROTATE, TOK_MULTIROTATE
+    };
+
+  struct primstruct
+  {
+    PRIMITIVE_TYPE kw; 
+    char * name;
+  };
+
+  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_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) }
+    };
+
+  static CSGeometry * geom;
+
+
+  
+  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 :: 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;
+	  }
+
+	// 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
+	      while (1)
+		{
+		  pi1 = (int) (ParseNumber (scan));
+		  ParseChar (scan, ',');
+		  pi2 = (int) (ParseNumber (scan));
+		  ParseChar (scan, ',');
+		  pi3 = (int) (ParseNumber (scan));
+
+		  polyhedron->AddFace(pi1-1,pi2-1,pi3-1);
+
+		  if (scan.GetToken() == TOK_COMMA)
+		    {
+		      ParseChar (scan, ',');
+		      pi4 = (int) (ParseNumber (scan));
+		      polyhedron->AddFace(pi1-1,pi3-1,pi4-1);
+		    }
+
+		  if (scan.GetToken() == ')')
+		    {
+		      scan.ReadNext();
+		      break;
+		    }
+		  scan.ReadNext();
+		}
+
+	      geom->AddSurfaces (polyhedron);
+	      return new Solid (polyhedron);
+	    }
+
+
+	  case TOK_EXTRUSION:     // not functional
+	    {   
+	      Point<3> p0;
+	      Vec<3> ex, ey;
+	      ARRAY<Point<2> > points;
+
+	      scan.ReadNext();
+	      
+	      ParseChar (scan, '(');
+	      p0(0) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      p0(1) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      p0(2) = ParseNumber (scan);
+	      ParseChar (scan, ';');
+
+	      ex(0) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      ex(1) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      ex(2) = ParseNumber (scan);
+	      ParseChar (scan, ';');
+
+	      ey(0) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      ey(1) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      ey(2) = ParseNumber (scan);
+	      ParseChar (scan, ';');
+
+	      cout << "p0 = " << p0 << endl;
+
+	      // int npseg = 0;
+	      // int nseg = 0;
+	      while (1)
+		{
+		  Point<2> p1, p2, p3;
+
+		  p1(0) = ParseNumber(scan);
+		  ParseChar (scan, ',');
+		  p1(1) = ParseNumber(scan);
+		  points.Append (p1);
+		  if (scan.GetToken() == ')')
+		    {
+		      scan.ReadNext();
+		      break;
+		    }
+		  scan.ReadNext();
+		}
+
+
+	      /*
+		while (1)
+		{
+		Point<2> p1, p2, p3;
+		  
+		p3 = p2;
+		p2 = p1;
+		p1(0) = ParseNumber(scan);
+		ParseChar (scan, ',');
+		p1(1) = ParseNumber(scan);
+		npseg++;
+		  
+		cout << "p1 = " << p1 << endl;
+
+		if (scan.GetToken() == ';' || scan.GetToken() == ')')
+		{
+		if (npseg == 2)
+		{
+		p3 = p2;
+		p2 = Center (p1, p3);
+		}
+		if (nseg == 0)
+		points.Append (p3);
+		points.Append (p2);
+		points.Append (p1);
+		npseg = 1;
+		nseg++;
+
+		cout << "p1, = " << p1 << ", p2 = " << p2 << ", p3 = " << p3 << endl;
+		}
+		  
+		if (scan.GetToken() == ')')
+		{
+		scan.ReadNext();
+		break;
+		}
+		if (scan.GetToken() == ';' || scan.GetToken() == ',')
+		{
+		scan.ReadNext();
+		}
+		}
+	      */
+	      cout << "p0 = " << p0 << endl;
+	      cout << ", ex = " << ex << ", ey = " << ey << endl;
+	      cout << "points = " << points << endl;
+	      
+	      Extrusion * extrusion = new Extrusion (p0, ex, ey, points);
+	      
+	      geom->AddSurfaces (extrusion);
+	      return new Solid (extrusion);
+	      
+	      /*
+	      // cout << "define cylinder, pa = " << pa << "; pb = " << pb
+	      // << ", rad = " << r << endl;
+	      OneSurfacePrimitive * surf = new Cylinder ( pa, pb, r );
+
+	      geom->AddSurface (surf);
+	      surf->SetSurfaceId (0, geom->GetNSurf()-1);
+
+	      return new Solid (surf);
+	      */
+	    }
+
+
+
+
+
+	  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_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;
+  }
+
+
+
+  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();
+		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 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)));
+		  }
+	      }
+	    
+	    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 = 
+			geom->GetTopLevelObject (geom->GetSolid(flags.GetStringFlag ("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);
+		      
+		      ParseChar (scan, ';');
+		      
+		      const Solid * sol = geom->GetSolid(name2);
+
+		      for (int i = 0; i < geom->GetNTopLevelObjects(); i++)
+			if (name1 == geom->GetTopLevelObject (i)->GetSolid()->Name())
+			  geom->singfaces.Append (new SingularFace (i+1, sol));
+
+		      break;
+		    }
+
+		  case TOK_EDGE:
+		    {
+		      scan.ReadNext();
+		      
+		      string name1 = scan.GetStringValue();
+		      scan.ReadNext();
+		      
+		      string name2 = scan.GetStringValue();
+		      scan.ReadNext();
+		      
+		      Flags flags;
+		      ParseFlags (scan, flags);
+		      
+		      ParseChar (scan, ';');
+		      
+		      const Solid * s1 = geom->GetSolid(name1);
+		      const Solid * s2 = geom->GetSolid(name2);
+		      geom->singedges.Append (new SingularEdge (1, s1, s2));
+		      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);
+		      
+		      ParseChar (scan, ';');
+		      
+		      const Solid * s1 = geom->GetSolid(name1);
+		      const Solid * s2 = geom->GetSolid(name2);
+		      const Solid * s3 = geom->GetSolid(name3);
+		      geom->singpoints.Append (new SingularPoint (1, s1, s2, s3));
+		      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, ')');
+		ParseChar (scan, ';');
+
+		geom->AddUserPoint (p);
+	      }
+
+	    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_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;
+		ARRAY<int> si;
+		
+		geom->GetSolid(name1)->GetSurfaceIndices(si);
+	
+		bcm.tlonr = -1;
+		int i;	
+		for (i = 0; i < geom->GetNTopLevelObjects(); i++)
+		  if (string (geom->GetTopLevelObject(i)->GetSolid()->Name())
+		      == name2)
+		    {
+		      bcm.tlonr = i;
+		      break;
+		    }
+		
+		bcm.bcnr = num;
+		for (i = 0; i < si.Size(); i++)
+		  {
+		    bcm.si = si[i];
+		    geom->bcmodifications.Append (bcm);
+		  }
+	      }
+
+	    else
+	      {
+		cout << "read unidentified token " << scan.GetToken() 
+		     << " string = " << scan.GetStringValue() << endl;
+		scan.ReadNext();
+	      }
+	  }
+      }
+    catch (string errstr)
+      {
+	cout << "caught error " << errstr << endl;
+	throw NgException (errstr);
+      }
+
+
+    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_dalibor.cpp b/contrib/Netgen/libsrc/csg/csgparser_dalibor.cpp
new file mode 100644
index 0000000000..7f640864a3
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/csgparser_dalibor.cpp
@@ -0,0 +1,1111 @@
+#include <mystdlib.h>
+#include <myadt.hpp>
+
+#include <linalg.hpp>
+#include <csg.hpp>
+
+
+namespace netgen
+{
+  using 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_TRANSLATE, TOK_MULTITRANSLATE, TOK_ROTATE, TOK_MULTIROTATE,
+      TOK_SINGULAR, TOK_EDGE, TOK_POINT, TOK_IDENTIFY, TOK_CLOSESURFACES,
+      TOK_CLOSEEDGES, TOK_PERIODIC,
+      TOK_SOLID, TOK_RECO, TOK_TLO, TOK_BOUNDINGBOX, TOK_BOUNDARYCONDITION,
+      TOK_END };
+
+  struct kwstruct
+  {
+    TOKEN_TYPE kw; 
+    char * name;
+  };
+
+  static kwstruct defkw[] =
+    {
+      { TOK_RECO,    "algebraic3d" },
+      { TOK_SOLID,   "solid" },
+      { TOK_TLO,     "tlo" },
+      { TOK_BOUNDINGBOX, "boundingbox" },
+      { TOK_OR,      "or" },
+      { TOK_AND,     "and" },
+      { TOK_NOT,     "not" },
+      { TOK_TRANSLATE, "translate" },
+      { TOK_MULTITRANSLATE, "multitranslate" },
+      { TOK_ROTATE,   "rotate" },
+      { TOK_MULTIROTATE, "multirotate" },
+      { TOK_SINGULAR, "singular" },
+      { TOK_EDGE,     "edge" },
+      { TOK_POINT,    "point" },
+      { TOK_IDENTIFY, "identify" },
+      { TOK_CLOSESURFACES, "closesurfaces" },
+      { TOK_CLOSEEDGES, "closeedges" },
+      { TOK_PERIODIC,  "periodic" },
+      { TOK_BOUNDARYCONDITION, "boundarycondition" },
+      { TOKEN_TYPE(0) }
+    };
+
+  enum PRIMITIVE_TYPE
+    { TOK_SPHERE = 1, TOK_CYLINDER, TOK_PLANE, TOK_ELLIPTICCYLINDER, 
+      TOK_ELLIPSOID,
+      TOK_CONE, TOK_TUBE,
+      TOK_GENCYL, TOK_ORTHOBRICK, TOK_POLYHEDRON, TOK_EXTRUSION, TOK_REVOLUTION };
+
+  struct primstruct
+  {
+    PRIMITIVE_TYPE kw; 
+    char * name;
+  };
+
+  static primstruct defprim[] =
+    {
+      { TOK_PLANE,     "plane" },
+      { TOK_SPHERE,    "sphere" },
+      { TOK_CYLINDER,  "cylinder" },
+      { TOK_CONE,      "cone" },
+      { TOK_ELLIPTICCYLINDER, "ellipticcylinder" },
+      { TOK_ELLIPSOID, "ellipsoid" },
+      { TOK_TUBE,      "tube" },
+      { TOK_GENCYL,    "gencyl" },
+      { TOK_ORTHOBRICK, "orthobrick" },
+      { TOK_POLYHEDRON, "polyhedron" },
+      { TOK_EXTRUSION,  "extrusion" },
+      { TOK_REVOLUTION, "revolution" },
+      { PRIMITIVE_TYPE(0) }
+    };
+
+
+
+  static CSGeometry * geom;
+
+  /*
+%token <solidtype> TOK_SPHERE TOK_CYLINDER TOK_CONE TOK_PLAIN TOK_TUBE TOK_GENCYL TOK_ORTHOBRICK TOK_POLYHEDRON TOK_REVOLUTION
+%left <solidtype> TOK_OR TOK_AND TOK_NOT
+%token <solidtype> TOK_TRANSLATE TOK_MULTITRANSLATE TOK_ROTATE TOK_MULTIROTATE
+%type <solidtype> solid solidprimitive 
+%type <void> splinesegmentlist splinesegment readbspline bsplinepointlist
+%type <chptr> anyident
+%token TOK_SINGULAR TOK_EDGE TOK_POINT
+%token TOK_IDENTIFY TOK_CLOSESURFACES TOK_CLOSEEDGES TOK_PERIODIC
+%token TOK_BOUNDARYCONDITION
+%type <void> polyhedronpoints polyhedronfaces polyhedronpoint polyhedronface
+%type <void> revolutionpoints revolutionpoint
+  */
+
+
+  
+  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; }
+
+    PRIMITIVE_TYPE GetPrimitiveToken() const
+    { return prim_token; }
+  
+    void ReadNext();
+    void Error (const string & err);
+  };
+
+
+  CSGScanner :: CSGScanner (istream & ascanin)
+  {
+    int i;
+
+    scanin = &ascanin;
+    token = TOK_END;
+    num_value = 0;
+    linenum = 1;
+  }
+
+
+  void CSGScanner :: ReadNext ()
+  {
+    char ch;
+  
+
+    // whitespaces ueberspringen
+    do
+      { 
+	scanin->get(ch);
+
+	if (ch == '\n') 
+	  linenum++;
+
+	// end of file reached
+	if (scanin->eof())
+	  {
+	    token = TOK_END;
+	    return;
+	  }
+
+	// 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))
+		{
+		  string_value += ch;
+		  scanin->get(ch);
+		}
+	      scanin->putback (ch);
+	    }
+	  /*
+	  (*scanin).putback (ch);
+	  (*scanin) >> string_value;
+	  */
+	  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)
+  {
+    char str[2];
+    str[0] = ch;
+    str[1] = 0;
+    if (scan.GetToken() != TOKEN_TYPE(ch)) 
+      scan.Error (string ("token '") + string(str) + 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;
+  }
+
+
+  Solid * ParseSolid (CSGScanner & scan);
+  Solid * ParseTerm (CSGScanner & scan);
+  Solid * ParsePrimary (CSGScanner & scan);
+ 
+
+  Solid * ParsePrimary (CSGScanner & scan)
+  {
+    if (scan.GetToken() == TOK_PRIMITIVE)
+      {
+	//	cout << "prim token = " << int (scan.GetPrimitiveToken()) << endl;
+	switch (scan.GetPrimitiveToken())
+	  {
+	  case TOK_PLANE:
+	    {
+	      Point<3> p;
+	      Vec<3> v;
+	      
+	      scan.ReadNext();
+	      
+	      ParseChar (scan, '(');
+	      p(0) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      p(1) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      p(2) = ParseNumber (scan);
+	      ParseChar (scan, ';');
+	      v(0) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      v(1) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      v(2) = ParseNumber (scan);
+	      ParseChar (scan, ')');
+
+	      // cout << "define plane, p = " << p << "; v = " << v << endl;
+	      OneSurfacePrimitive * surf = new Plane ( p, v );
+
+	      geom->AddSurface (surf);
+	      surf->SetSurfaceId (0, geom->GetNSurf()-1);
+
+	      return new Solid (surf);
+	    }
+	  case TOK_CYLINDER:
+	    {
+	      Point<3> pa, pb;
+	      double r;
+	      
+	      scan.ReadNext();
+	      
+	      ParseChar (scan, '(');
+	      pa(0) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      pa(1) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      pa(2) = ParseNumber (scan);
+	      ParseChar (scan, ';');
+	      pb(0) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      pb(1) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      pb(2) = ParseNumber (scan);
+	      ParseChar (scan, ';');
+	      r = ParseNumber (scan);
+	      ParseChar (scan, ')');
+	      
+	      OneSurfacePrimitive * surf = new Cylinder ( pa, pb, r );
+
+	      geom->AddSurface (surf);
+	      surf->SetSurfaceId (0, geom->GetNSurf()-1);
+
+	      return new Solid (surf);
+	    }
+
+	  case TOK_ELLIPTICCYLINDER:
+	    {
+	      Point<3> pa;
+	      Vec<3> vl, vs;
+	      
+	      scan.ReadNext();
+	      
+	      ParseChar (scan, '(');
+	      pa(0) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      pa(1) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      pa(2) = ParseNumber (scan);
+	      ParseChar (scan, ';');
+
+	      vl(0) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      vl(1) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      vl(2) = ParseNumber (scan);
+	      ParseChar (scan, ';');
+
+	      vs(0) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      vs(1) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      vs(2) = ParseNumber (scan);
+	      ParseChar (scan, ')');
+	      
+	      OneSurfacePrimitive * surf = new EllipticCylinder ( pa, vl, vs);
+
+	      geom->AddSurface (surf);
+	      surf->SetSurfaceId (0, geom->GetNSurf()-1);
+
+	      return new Solid (surf);
+	    }
+
+
+	  case TOK_ELLIPSOID:
+	    {
+	      Point<3> pa;
+	      Vec<3> v1, v2, v3;
+	      
+	      scan.ReadNext();
+	      
+	      ParseChar (scan, '(');
+	      pa(0) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      pa(1) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      pa(2) = ParseNumber (scan);
+	      ParseChar (scan, ';');
+
+	      v1(0) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      v1(1) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      v1(2) = ParseNumber (scan);
+	      ParseChar (scan, ';');
+
+	      v2(0) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      v2(1) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      v2(2) = ParseNumber (scan);
+	      ParseChar (scan, ';');
+
+	      v3(0) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      v3(1) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      v3(2) = ParseNumber (scan);
+	      ParseChar (scan, ')');
+	      
+	      OneSurfacePrimitive * surf = new Ellipsoid ( pa, v1, v2, v3);
+
+	      geom->AddSurface (surf);
+	      surf->SetSurfaceId (0, geom->GetNSurf()-1);
+
+	      return new Solid (surf);
+	    }
+
+
+	  case TOK_CONE:
+	    {
+	      Point<3> pa, pb;
+	      double ra, rb;
+	      
+	      scan.ReadNext();
+	      
+	      ParseChar (scan, '(');
+	      pa(0) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      pa(1) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      pa(2) = ParseNumber (scan);
+	      ParseChar (scan, ';');
+	      ra = ParseNumber (scan);
+	      ParseChar (scan, ';');
+	      pb(0) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      pb(1) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      pb(2) = ParseNumber (scan);
+	      ParseChar (scan, ';');
+	      rb = ParseNumber (scan);
+	      ParseChar (scan, ')');
+	      
+	      OneSurfacePrimitive * surf = new Cone ( pa, pb, ra, rb);
+
+	      geom->AddSurface (surf);
+	      surf->SetSurfaceId (0, geom->GetNSurf()-1);
+
+	      return new Solid (surf);
+	    }
+
+
+
+
+	  case TOK_SPHERE:
+	    {
+	      Point<3> p;
+	      double r;
+	      
+	      scan.ReadNext();
+	      
+	      ParseChar (scan, '(');
+	      p(0) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      p(1) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      p(2) = ParseNumber (scan);
+	      ParseChar (scan, ';');
+	      r = ParseNumber (scan);
+	      ParseChar (scan, ')');
+	      
+	      // cout << "define sphere, c = " << p << ", rad = " << r << endl;
+	      OneSurfacePrimitive * surf = new Sphere ( p, r );
+
+	      geom->AddSurface (surf);
+	      surf->SetSurfaceId (0, geom->GetNSurf()-1);
+
+	      return new Solid (surf);
+	    }
+
+	  case TOK_ORTHOBRICK:
+	    {
+	      Point<3> pa, pb;
+	      
+	      scan.ReadNext();
+	      
+	      ParseChar (scan, '(');
+	      pa(0) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      pa(1) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      pa(2) = ParseNumber (scan);
+	      ParseChar (scan, ';');
+	      pb(0) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      pb(1) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      pb(2) = ParseNumber (scan);
+	      ParseChar (scan, ')');
+	      
+	      // cout << "define orthobrick, p1 = " << pa << "; p2 = " << pb << endl;
+
+	      Primitive * nprim = new OrthoBrick (pa, pb);
+	      
+	      for (int j = 0; j < nprim->GetNSurfaces(); j++)
+		{
+		  geom->AddSurface (&nprim->GetSurface(j));
+		  nprim->SetSurfaceId (j, geom->GetNSurf()-1);
+		}
+	      return new Solid (nprim);
+	    } 
+
+
+	  case TOK_EXTRUSION:
+	    {
+	      Point<3> p0;
+	      Vec<3> ex, ey;
+	      ARRAY<Point<2> > points;
+
+	      scan.ReadNext();
+	      
+	      ParseChar (scan, '(');
+	      p0(0) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      p0(1) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      p0(2) = ParseNumber (scan);
+	      ParseChar (scan, ';');
+
+	      ex(0) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      ex(1) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      ex(2) = ParseNumber (scan);
+	      ParseChar (scan, ';');
+
+	      ey(0) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      ey(1) = ParseNumber (scan);
+	      ParseChar (scan, ',');
+	      ey(2) = ParseNumber (scan);
+	      ParseChar (scan, ';');
+
+	      cout << "p0 = " << p0 << endl;
+
+	      int npseg = 0;
+	      int nseg = 0;
+	      while (1)
+		{
+		  Point<2> p1, p2, p3;
+
+		  p1(0) = ParseNumber(scan);
+		  ParseChar (scan, ',');
+		  p1(1) = ParseNumber(scan);
+		  points.Append (p1);
+		  if (scan.GetToken() == ')')
+		    {
+		      scan.ReadNext();
+		      break;
+		    }
+		  scan.ReadNext();
+		}
+
+
+	      /*
+	      while (1)
+		{
+		  Point<2> p1, p2, p3;
+		  
+		  p3 = p2;
+		  p2 = p1;
+		  p1(0) = ParseNumber(scan);
+		  ParseChar (scan, ',');
+		  p1(1) = ParseNumber(scan);
+		  npseg++;
+		  
+		  cout << "p1 = " << p1 << endl;
+
+		  if (scan.GetToken() == ';' || scan.GetToken() == ')')
+		    {
+		      if (npseg == 2)
+			{
+			  p3 = p2;
+			  p2 = Center (p1, p3);
+			}
+		      if (nseg == 0)
+			points.Append (p3);
+		      points.Append (p2);
+		      points.Append (p1);
+		      npseg = 1;
+		      nseg++;
+
+		      cout << "p1, = " << p1 << ", p2 = " << p2 << ", p3 = " << p3 << endl;
+		    }
+		  
+		  if (scan.GetToken() == ')')
+		    {
+		      scan.ReadNext();
+		      break;
+		    }
+		  if (scan.GetToken() == ';' || scan.GetToken() == ',')
+		    {
+		      scan.ReadNext();
+		    }
+		}
+	      */
+	      cout << "p0 = " << p0 << endl;
+	      cout << ", ex = " << ex << ", ey = " << ey << endl;
+	      cout << "points = " << points << endl;
+	      
+	      Extrusion * extrusion = new Extrusion (p0, ex, ey, points);
+	      
+	      for (int i = 0; i < extrusion->GetNSurfaces(); i++)
+		{
+		  geom->AddSurface (&extrusion->GetSurface(i));
+		  extrusion->SetSurfaceId(i, geom->GetNSurf()-1);
+		}
+	      return new Solid (extrusion);
+		    
+	      /*
+	      // cout << "define cylinder, pa = " << pa << "; pb = " << pb
+	      // << ", rad = " << r << endl;
+	      OneSurfacePrimitive * surf = new Cylinder ( pa, pb, r );
+
+	      geom->AddSurface (surf);
+	      surf->SetSurfaceId (0, geom->GetNSurf()-1);
+
+	      return new Solid (surf);
+	      */
+	    }
+
+
+// Added by Dalibor Lukas, October 15, 2003
+	  case TOK_POLYHEDRON:
+	    {
+	      Point<3> p;
+	      int pi1, pi2, pi3, pi4;
+	      
+	      scan.ReadNext();
+	      ParseChar (scan, '(');
+	      
+	      Polyhedra * polyhedron = new Polyhedra;
+
+	      // scanning the points
+	      while (1)
+		{
+		  p(0) = ParseNumber (scan);
+		  ParseChar (scan, ',');
+		  p(1) = ParseNumber (scan);
+		  ParseChar (scan, ',');
+		  p(2) = ParseNumber (scan);
+		  ParseChar (scan, ';');
+
+		  cout << "point = " << p << endl;
+
+		  polyhedron->AddPoint(p);
+
+		  if (scan.GetToken() == ';')
+		    {
+		      scan.ReadNext();
+		      break;
+		    }
+		}
+
+	      // scanning the faces
+	      while (1)
+		{
+		  pi1 = (int) (ParseNumber (scan));
+		  ParseChar (scan, ',');
+		  pi2 = (int) (ParseNumber (scan));
+		  ParseChar (scan, ',');
+		  pi3 = (int) (ParseNumber (scan));
+		  ParseChar (scan, ',');
+		  pi4 = (int) (ParseNumber (scan));
+
+		  cout << "face = (" << pi1 << ", " << pi2 << ", " << pi3
+		       << ", " << pi4 << ")" << endl;
+
+		  polyhedron->AddFace(pi1-1,pi2-1,pi3-1);
+		  polyhedron->AddFace(pi1-1,pi3-1,pi4-1);
+
+		  if (scan.GetToken() == ')')
+		    {
+		      scan.ReadNext();
+		      break;
+		    }
+		  scan.ReadNext();
+		}
+	      
+	      int j;
+	      for (j = 0; j < polyhedron->GetNSurfaces(); j++)
+		{
+		  geom->AddSurface (&polyhedron->GetSurface(j));
+		  polyhedron->SetSurfaceId (j, geom->GetNSurf()-1);
+		}
+
+	      return new Solid (polyhedron);
+	    }
+// DL
+
+
+	  }
+	cout << "unknown primary " << scan.GetStringValue() << endl;
+      }
+
+    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;
+  }
+
+
+
+  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();
+		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 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, ';');
+
+		    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)));
+		  }
+	      }
+	    
+	    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);
+		      
+		      geom->AddIdentification 
+			(new CloseSurfaceIdentification 
+			 (geom->GetNIdentifications()+1, *geom, 
+			  geom->GetSurface (si1[0]), geom->GetSurface (si2[0]),
+			  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;
+		    }
+		  }
+		
+	      }
+	    
+	    else if (scan.GetToken() == TOK_POINT)
+	      {
+		Point<3> p;
+
+		scan.ReadNext();
+		ParseChar (scan, '(');
+		p(0) = ParseNumber (scan);
+		ParseChar (scan, ',');
+		p(1) = ParseNumber (scan);
+		ParseChar (scan, ',');
+		p(2) = ParseNumber (scan);
+		ParseChar (scan, ')');
+		ParseChar (scan, ';');
+
+		geom->AddUserPoint (p);
+	      }
+
+	    else if (scan.GetToken() == TOK_BOUNDINGBOX)
+	      {
+		Point<3> p1, p2;
+		
+		scan.ReadNext();
+		ParseChar (scan, '(');
+		p1(0) = ParseNumber (scan);
+		ParseChar (scan, ',');
+		p1(1) = ParseNumber (scan);
+		ParseChar (scan, ',');
+		p1(2) = ParseNumber (scan);
+		ParseChar (scan, ';');
+		p2(0) = ParseNumber (scan);
+		ParseChar (scan, ',');
+		p2(1) = ParseNumber (scan);
+		ParseChar (scan, ',');
+		p2(2) = ParseNumber (scan);
+		ParseChar (scan, ')');
+		ParseChar (scan, ';');
+
+		geom->SetBoundingBox (Box<3> (p1, p2));
+	      }
+
+	    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;
+		ARRAY<int> si;
+		
+		geom->GetSolid(name1)->GetSurfaceIndices(si);
+	
+		bcm.tlonr = -1;
+		int i;	
+		for (i = 0; i < geom->GetNTopLevelObjects(); i++)
+		  if (string (geom->GetTopLevelObject(i)->GetSolid()->Name())
+		      == name2)
+		    {
+		      bcm.tlonr = i;
+		      break;
+		    }
+		
+		bcm.bcnr = num;
+		for (i = 0; i < si.Size(); i++)
+		  {
+		    bcm.si = si[i];
+		    geom->bcmodifications.Append (bcm);
+		  }
+	      }
+
+	    else
+	      {
+		cout << "read unidentified token " << scan.GetToken() 
+		     << " string = " << scan.GetStringValue() << endl;
+		scan.ReadNext();
+	      }
+	  }
+      }
+    catch (string errstr)
+      {
+	cout << "caught error " << errstr << 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/csgscanner.cpp b/contrib/Netgen/libsrc/csg/csgscanner.cpp
new file mode 100644
index 0000000000..50e2213ea6
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/csgscanner.cpp
@@ -0,0 +1,205 @@
+#include <mystdlib.h>
+#include <myadt.hpp>
+
+#include <linalg.hpp>
+#include <csg.hpp>
+
+
+namespace netgen
+{
+  using namespace netgen;
+
+
+  enum TOKEN_TYPE
+    { 
+      LP = '(', RP = ')', EQU = '=', COMMA = ',', SEMICOLON = ';',
+      TOK_NUM, TOK_STRING, TOK_NAMED_SOLID, TOK_PRIMITIVE, 
+      TOK_OR, TOK_AND, TOK_NOT,
+      TOK_SOLID, TOK_RECO, TOK_TLO, TOK_BOUNDINGBOX,
+      TOK_END };
+
+  struct kwstruct
+  {
+    TOKEN_TYPE kw; 
+    string name;
+  };
+
+  static kwstruct defkw[] =
+    {
+      { TOK_OR,      "or" },
+      { TOK_AND,     "and" },
+      { TOKEN_TYPE(0) }
+    };
+
+  enum PRIMITIVE_TYPE { TOK_SPHERE, TOK_CYLINDER, TOK_PLANE };
+
+  /*
+%token <solidtype> TOK_SPHERE TOK_CYLINDER TOK_CONE TOK_PLAIN TOK_TUBE TOK_GENCYL TOK_ORTHOBRICK TOK_POLYHEDRON TOK_REVOLUTION
+%left <solidtype> TOK_OR TOK_AND TOK_NOT
+%token <solidtype> TOK_TRANSLATE TOK_MULTITRANSLATE TOK_ROTATE TOK_MULTIROTATE
+%type <solidtype> solid solidprimitive 
+%type <void> splinesegmentlist splinesegment readbspline bsplinepointlist
+%type <chptr> anyident
+%token TOK_SINGULAR TOK_EDGE TOK_POINT
+%token TOK_IDENTIFY TOK_CLOSESURFACES TOK_CLOSEEDGES TOK_PERIODIC
+%token TOK_BOUNDARYCONDITION
+%type <void> polyhedronpoints polyhedronfaces polyhedronpoint polyhedronface
+%type <void> revolutionpoints revolutionpoint
+  */
+
+
+  
+  class CSGScanner
+  {
+    TOKEN_TYPE 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; }
+  
+    void ReadNext();
+
+    void Error (const string & err);
+  };
+
+
+  CSGScanner :: CSGScanner (istream & ascanin)
+  {
+    int i;
+
+    scanin = &ascanin;
+    token = TOK_END;
+    num_value = 0;
+    linenum = 1;
+  }
+
+
+  void CSGScanner :: ReadNext ()
+  {
+    char ch;
+  
+
+    // whitespaces ueberspringen
+    do
+      { 
+	scanin->get(ch);
+
+	if (ch == '\n') 
+	  linenum++;
+
+	// end of file reached
+	if (scanin->eof())
+	  {
+	    token = TOK_END;
+	    return;
+	  }
+
+	// 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 ';':
+	{
+	  token = TOKEN_TYPE (ch);
+	  break;
+	}
+  
+      default:
+	{
+	  if (isdigit (ch) || ch == '.' || ch == '-')
+	    {
+	      scanin->putback (ch);
+	      (*scanin) >> num_value;
+	      token = TOK_NUM;
+	      return;
+	    }
+
+	  (*scanin).putback (ch);
+	  (*scanin) >> string_value;
+
+	  int nr = 0;
+	  while (defkw[nr].kw)
+	    {
+	      if (string_value == defkw[nr].name)
+		{
+		  token = defkw[nr].kw;
+		  return;
+		}
+	    }
+	
+	  token = TOK_STRING;
+	}
+      }
+  }
+
+  void CSGScanner :: Error (const string & err)
+  {
+    stringstream errstr;
+    errstr << "Parsing error in line " << linenum << ": " << endl << err << endl;
+    /*
+    errstr << "input continues with <<<";
+    for (int i = 0; i < 50; i++)
+      {
+	char ch;
+	scanin->get(ch);
+	errstr << ch;
+	if (scanin->eof())
+	  {
+	    errstr << "(end of file)";
+	    break;
+	  }
+      }
+    errstr << endl << ">>> stop parsing" << endl;
+    throw Exception (errstr.str());
+    */
+  }
+  
+
+
+  
+  
+  void ParseCSG (istream & istr)
+  {
+    CSGScanner scan(istr);
+
+    do
+      {
+	scan.ReadNext();
+	cout << "token = " << int(scan.GetToken()) << endl;
+      }
+    while (scan.GetToken() != TOK_END);
+  }
+
+
+};
+
diff --git a/contrib/Netgen/libsrc/csg/curve2d.cpp b/contrib/Netgen/libsrc/csg/curve2d.cpp
new file mode 100644
index 0000000000..7091e87af9
--- /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 0000000000..917bd53205
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/curve2d.hpp
@@ -0,0 +1,59 @@
+#ifndef FILE_CURVE2D
+#define FILE_CURVE2D
+
+/**************************************************************************/
+/* File:   curve2d.hh                                                     */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   24. Jul. 96                                                    */
+/**************************************************************************/
+
+/*
+
+  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 0000000000..50b425f84f
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/edgeflw.cpp
@@ -0,0 +1,1524 @@
+#include <mystdlib.h>
+#include <meshing.hpp>
+#include <csg.hpp>
+
+#undef DEVELOP
+
+namespace netgen
+{
+
+  EdgeCalculation :: 
+  EdgeCalculation (const CSGeometry & ageometry,
+		   ARRAY<SpecialPoint> & aspecpoints)
+    : geometry(ageometry), specpoints(aspecpoints)
+  {
+    Box<3> bbox;
+    if (specpoints.Size() >= 1)
+      bbox.Set (specpoints[0].p);
+    else
+      { bbox.Set (Point<3> (0,0,0)); bbox.Add (Point<3> (1,1,1)); }
+    for (int i = 1; i < specpoints.Size(); i++)
+      bbox.Add (specpoints[i].p);
+
+    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);
+  }
+
+  EdgeCalculation :: ~EdgeCalculation()
+  {
+    delete searchtree;
+    delete meshpoint_tree;
+  }
+
+
+  void EdgeCalculation :: Calc(double h, Mesh & mesh)
+  {
+    PrintMessage (1, "Find edges");
+    PushStatus ("Find edges");
+
+    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;
+
+    ARRAY<Point<3> > edgepoints;
+    ARRAY<double> curvelength;
+    int copyedge, copyfromedge = -1, copyedgeidentification = -1;
+
+    ARRAY<int> locsurfind, locind;
+
+    // double len, corr, lam;
+    // double steplen, cursteplen, loch, 
+    double hd;
+
+    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;
+      }
+
+
+    cntedge = 0;
+    INDEX_2_HASHTABLE<int> identification_used(100);  // identification i already used for startpoint j
+
+
+    while (hsp.Size())
+      {
+	SetThreadPercent(100 - 100 * double (hsp.Size()) / specpoints.Size());
+
+	edgepoints.SetSize (0);
+	curvelength.SetSize (0);
+      
+
+	pi1 = 0;
+	copyedge = 0;
+	// identifyable point available ?
+
+	//      (*testout) << endl;
+
+	for (int i = 0; i < geometry.identifications.Size() && !pi1; i++)
+	  for (int j = checkedcopy; j < startpoints.Size() && !pi1; j++)
+
+	    if (geometry.identifications[i]->IdentifyableCandidate (specpoints[startpoints[j]]))
+	      
+	      {
+		int pi1cand = 0;
+		double mindist = 1e10;
+		
+		for (int k = 0; k < hsp.Size() && !pi1; k++)
+		  {
+		    if (identification_used.Used (INDEX_2(i, startpoints[j])) ||
+			identification_used.Used (INDEX_2(i, hsp[k]))) continue;
+		    
+		    if (geometry.identifications[i]
+			->Identifyable(specpoints[startpoints[j]], specpoints[hsp[k]]) ||
+			geometry.identifications[i]
+			->Identifyable(specpoints[hsp[k]], specpoints[startpoints[j]]))
+		      {
+			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)
+	  {
+	    // only unconditional points available, choose first
+	    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 &&
+		  (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) << "edge nr " << cntedge << endl;
+	(*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;
+
+
+	// 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));
+	    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, 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;
+	(*testout) << "inv[1] = " << refedgesinv.Get(1) << endl;
+#endif
+      
+	if (!copyedge)
+	  {
+	    // int oldnseg = mesh.GetNSeg();
+
+	    if (!shortedge)
+	      StoreEdge (refedges, refedgesinv, 
+			 edgepoints, curvelength, layer, mesh);
+	    else
+	      StoreShortEdge (refedges, refedgesinv, 
+			      edgepoints, curvelength, layer, mesh);
+
+
+	    /*
+	      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);
+	  }
+
+      }
+  }
+
+
+
+
+
+  /*
+    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)--;
+      }
+
+    //    (*testout) << "osedges  = " << osedges << endl;
+
+    // flag one segment edges
+    for (int i = 0; i < cntedge; i++)
+      osedges[i] = (osedges[i] > 0) ? 1 : 0;
+
+    //    (*testout) << "osedges, now  = " << osedges << endl;
+
+    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.p1, seg.p2);
+		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;
+		      }
+		  }
+	      }
+	}
+
+
+    // 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.p1, seg.p2);
+	    i2.Sort ();
+	    if (osedgesht.Used (i2) &&
+		osedgesht.Get (i2) == 2 &&
+		osedges.Elem(seg.edgenr) == -1)
+	      {
+		Point<3> newp = Center (mesh[PointIndex(seg.p1)],
+					mesh[PointIndex(seg.p2)]);
+
+		ProjectToEdge (geometry.GetSurface(seg.surfnr1), 
+			       geometry.GetSurface(seg.surfnr2), 
+			       newp);
+
+		osedges.Elem(seg.edgenr) = 
+		  mesh.AddPoint (newp, mesh[PointIndex(seg.p1)].GetLayer());
+		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.p1 = osedges.Get(seg.edgenr);
+		seg.p2 = 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;
+    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 = specpoints[hsp.Get(pi1)].s1;
+    s2 = specpoints[hsp.Get(pi1)].s2;
+  
+    p = specpoints[hsp.Get(pi1)].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;
+
+    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);
+	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)
+	  {
+	    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) -> LocH (np, 3, 1, h), 
+		     geometry.GetSurface(s2) -> LocH (np, 3, 1, h));
+
+	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 pos, int layer,
+	       const ARRAY<Point<3> > & edgepoints,
+	       ARRAY<Segment> & refedges,
+	       ARRAY<bool> & refedgesinv)
+  {
+    int i, j, k, l;
+    int hi;
+    Point<3> hp;
+    Vec<3> t, a1, a2, m, n;
+    Segment seg;
+    Solid * locsol;
+    ARRAY<int> locsurfind;
+
+    /*
+      int pi1 = 0, pi2 = 0;
+      extern Mesh * mesh;
+      for (i = 1; i <= mesh->GetNP(); i++)
+      {
+      if (Dist2 (edgepoints.Get(1), mesh->Point(i)) < 1e-12)
+      pi1 = i;
+      if (Dist2 (edgepoints.Last(), mesh->Point(i)) < 1e-12)
+      pi2 = i;
+      }
+      (*testout) << "Analyze edge: " << pi1 << " - " << pi2 << ", pts = " << edgepoints.Size() << endl;
+      (*testout) << "p1 = " << edgepoints.Get(1) << " pl = " << edgepoints.Last() << endl;
+    */
+    bool debug = 0;
+    /*
+      Dist2 (Point<3> (1.824, -0.104, -0.95), edgepoints.Get(1)) < 1e-6 ||
+      Dist2 (Point<3> (1.824, -0.104, -0.95), edgepoints.Last()) < 1e-6 ||
+      Dist2 (Point<3> (1.72149, -0.26069, -0.95), edgepoints.Get(1)) < 1e-6 ||
+      Dist2 (Point<3> (1.72149, -0.26069, -0.95), edgepoints.Last()) < 1e-6;
+    */
+
+    if (debug)
+      {
+	(*testout) << "tubious edge !!!" << endl;
+	(*testout) << "edgepoints = " << edgepoints << endl;
+	(*testout) << "s1, s2 = " << s1 << " - " << s2 << endl;
+      }
+
+    refedges.SetSize(0);
+    refedgesinv.SetSize(0);
+    hp = Center (edgepoints[0], edgepoints[1]);
+    ProjectToEdge (geometry.GetSurface(s1), geometry.GetSurface(s2), hp);
+
+    geometry.GetSurface(s1) -> CalcGradient (hp, a1);
+    geometry.GetSurface(s2) -> CalcGradient (hp, a2);
+    t = Cross (a1, a2);
+    t.Normalize();
+    if (!pos) t *= -1;    
+  
+    for (i = 0; i < geometry.GetNTopLevelObjects(); i++)
+      {
+	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);
+
+	if (!locsol) continue;
+
+	BoxSphere<3> boxp (hp, hp);
+	boxp.Increase (1e-5);
+	boxp.CalcDiamCenter();
+      
+	ReducePrimitiveIterator rpi(boxp);
+	UnReducePrimitiveIterator urpi;
+      
+	((Solid*)locsol) -> IterateSolid (rpi);
+
+	locsol -> CalcSurfaceInverse ();
+      
+	if (!surf)
+	  {
+	    locsol -> GetSurfaceIndices (locsurfind);
+	  }
+	else
+	  {
+	    /*
+	      if (fabs (surf->CalcFunctionValue (hp)) < 1e-6)
+	      continue;
+	    */
+	    locsurfind.SetSize(1);
+	    locsurfind[0] = -1;
+	    for (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 (j = locsurfind.Size()-1; j >= 0; j--)
+	  if (fabs (geometry.GetSurface(locsurfind[j])
+		    ->CalcFunctionValue (hp) ) > 1e-6)
+	    locsurfind.Delete(j);
+      
+	if (debug)
+	  (*testout) << locsurfind.Size() << " faces on hp" << endl;
+
+	for (j = 0; j < locsurfind.Size(); j++)
+	  {      
+	    int lsi = locsurfind[j];
+	    int rlsi = geometry.GetSurfaceClassRepresentant(lsi);
+	  
+	    Vec<3> rn;
+
+	    // n is outer normal to solid
+	    n = geometry.GetSurface(lsi) -> GetNormalVector (hp);
+	    if (geometry.GetSurface (lsi)->Inverse())
+	      n *= -1;
+	  
+	    if (fabs (t * n) > 1e-4) continue;
+	    if (debug)
+	      {
+		(*testout) << "face " << locsurfind.Get(j) << ", rep = " << rlsi 
+			   << " has (t*n) = " << (t*n) << endl;
+		(*testout) << "n = " << n << endl;
+	      }
+	  
+	    // rn is normal to class representant
+	    rn = geometry.GetSurface(rlsi) -> GetNormalVector (hp);
+
+	    int sameasref = ((n * rn) > 0);
+	  
+	    m = Cross (t, rn);
+	    m.Normalize();
+	  
+	    if (debug)
+	      (*testout) << "m = " << m << endl;
+
+	    for (k = 1; k <= 2; k ++)
+	      {
+		bool edgeinv = (k == 2);
+	      
+		if (debug)
+		  {
+		    (*testout) << "onface(" << hp << ", " << m << ")= " << flush;
+		    (*testout) << locsol->OnFace (hp, m) << flush;
+		    (*testout) << " vec2in = "
+			       << locsol -> VectorIn2 (hp, m, n) << " and " 
+			       << locsol -> VectorIn2 (hp, m, -1 * n) << endl;
+		  }
+
+		//	      if (locsol -> OnFace (hp, m))
+		if (locsol -> VectorIn2 (hp, m, n) == 0 &&
+		    locsol -> VectorIn2 (hp, m, -1 * n) == 1)
+		  {
+		    if (debug)
+		      (*testout) << "is true" << endl;
+		    hi = 0;
+		    for (l = 1; l <= refedges.Size(); l++)
+		      {
+			if (refedges.Get(l).si == rlsi &&
+			    refedgesinv.Get(l) == edgeinv)
+			  hi = l;
+		      }
+		  
+		    if (!hi)
+		      {
+			seg.si = rlsi;
+			seg.domin = -1;
+			seg.domout = -1;
+			seg.tlosurf = -1;
+			seg.surfnr1 = s1;
+			seg.surfnr2 = s2;
+			hi = refedges.Append (seg);
+			refedgesinv.Append (edgeinv);
+		      }
+		  
+		    if (!surf)
+		      {
+			if (sameasref)
+			  refedges.Elem(hi).domin = i;
+			else 
+			  refedges.Elem(hi).domout = i;
+		      }
+		    else
+		      refedges.Elem(hi).tlosurf = i;
+
+		    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
+				 << endl;
+		  }
+		else
+		  {
+		    if (debug)
+		      (*testout) << "is false" << endl;
+		  }
+		m *= -1;
+	      } 
+	  }
+	delete locsol;          
+      }
+  }
+
+
+
+  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);
+
+    len = curvelength.Last();
+    ne = int (len + 0.5);
+    if (ne == 0) ne = 1;
+    if (Dist2 (edgepoints.Get(1), edgepoints.Last()) < 1e-8 && 
+	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;
+	}
+    */
+    ARRAY<int> locsearch;
+    meshpoint_tree -> GetIntersecting (p-Vec<3> (1e-6, 1e-6, 1e-6),
+				       p+Vec<3> (1e-6, 1e-6, 1e-6), locsearch);
+    if (locsearch.Size())
+      lastpi = locsearch[0];
+				       
+
+
+    if (lastpi == -1)
+      {
+	lastpi = mesh.AddPoint (p, layer);
+	meshpoint_tree -> Insert (p, lastpi); 
+      }
+  
+    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> (1e-6, 1e-6, 1e-6),
+					       np+Vec<3> (1e-6, 1e-6, 1e-6), locsearch);
+	    if (locsearch.Size())
+	      thispi = locsearch[0];
+	  }
+
+	if (thispi == -1)
+	  {
+	    ProjectToEdge (surf1, surf2, np);
+	    thispi = mesh.AddPoint (np, layer);
+	    meshpoint_tree -> Insert (np, thispi);
+	  }
+
+	for (k = 1; k <= refedges.Size(); k++)
+	  {
+	    if (refedgesinv.Get(k))
+	      {
+		seg.p1 = lastpi;
+		seg.p2 = thispi;
+	      }
+	    else
+	      {
+		seg.p1 = thispi;
+		seg.p2 = 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 " << seg.p1 << "-" << seg.p2 << 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)
+	{
+	  pi1 = pi;
+	  break;
+	}
+
+    if (pi1 == -1) 
+      {
+	pi1 = mesh.AddPoint (p, layer);
+	meshpoint_tree -> Insert (p, pi1);
+      }
+
+    p = edgepoints.Last();
+    PointIndex pi2 = -1;
+    for (pi = PointIndex::BASE; 
+	 pi < mesh.GetNP()+PointIndex::BASE; pi++)
+
+      if (Dist (mesh[pi], p) < 1e-6)
+	{
+	  pi2 = pi;
+	  break;
+	}
+    if (pi2==-1) 
+      {
+	pi2 = mesh.AddPoint (p, layer);
+	meshpoint_tree -> Insert (p, pi2);
+      }
+
+    /*
+  
+    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);
+    }
+    */
+  
+    for (int k = 1; k <= refedges.Size(); k++)
+      {
+	if (refedgesinv.Get(k))
+	  {
+	    seg.p1 = pi1;
+	    seg.p2 = pi2;
+	  }
+	else
+	  {
+	    seg.p1 = pi2;
+	    seg.p2 = 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.p1 << "-" << seg.p2 << 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;
+
+    // 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)
+	      frompi = pi;
+	    if (Dist2 (mesh[pi], top) <= 1e-16)
+	      topi = pi;
+	  }
+
+	if (topi == -1)
+	  {
+	    topi = mesh.AddPoint (top, layer);
+	    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);
+	  }
+	/*
+	  (*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.p1;
+	int pi2 = oldseg.p2;
+
+	int npi1 = geometry.identifications.Get(copyedgeidentification)
+	  -> GetIdentifiedPoint (mesh, pi1);
+	int npi2 = geometry.identifications.Get(copyedgeidentification)
+	  -> GetIdentifiedPoint (mesh, pi2);
+
+	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.p1 = npi1;
+		seg.p2 = npi2;
+	      }
+	    else
+	      {
+		seg.p1 = npi2;
+		seg.p2 = 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.p1 << "-" << seg.p2 << endl;
+#ifdef DEVELOP
+
+	    (*testout) << "copy seg, face = " << seg.si << ": " 
+		       << " inv = " << inv << ", refinv = " << refedgesinv.Get(k)
+		       << mesh.Point(seg.p1) << ", " << mesh.Point(seg.p2) << 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;
+
+    BitArray pointatsurface (nsurf);
+    Point<3> p1, p2;
+    Vec<3> nv, tv;
+    Solid * tansol;
+    ARRAY<int> tansurfind;
+    //  const Solid * sol;
+
+    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);
+		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);
+		mesh.AddPoint (p2, layer);
+		seg1.p1 = mesh.GetNP()-1;
+		seg1.p2 = mesh.GetNP();
+		seg2.p2 = mesh.GetNP()-1;
+		seg2.p1 = 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 0000000000..beb1f690a9
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/edgeflw.hpp
@@ -0,0 +1,99 @@
+#ifndef FILE_EDGEFLW
+#define FILE_EDGEFLW
+
+/**************************************************************************/
+/* File:   edgeflw.hh                                                     */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Okt. 95                                                    */
+/**************************************************************************/
+
+/*
+  
+   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;
+public:
+  EdgeCalculation (const CSGeometry & ageometry,
+		   ARRAY<SpecialPoint> & aspecpoints);
+
+  ~EdgeCalculation();
+
+  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 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/edgeflw2.cpp b/contrib/Netgen/libsrc/csg/edgeflw2.cpp
new file mode 100644
index 0000000000..87239c05bb
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/edgeflw2.cpp
@@ -0,0 +1,1404 @@
+#include <mystdlib.h>
+#include <meshing.hpp>
+#include <csg.hpp>
+
+#undef DEVELOP
+
+namespace netgen
+{
+
+  EdgeCalculation :: 
+  EdgeCalculation (const CSGeometry & ageometry,
+		   const ARRAY<SpecialPoint> & aspecpoints)
+    : geometry(ageometry), specpoints(aspecpoints)
+  {
+    ;
+  }
+
+
+
+  void EdgeCalculation :: Calc(double h, Mesh & mesh)
+  {
+    PrintMessage (1, "Find edges");
+    PushStatus ("Find edges");
+
+    CalcEdges1 (h, mesh);
+    SplitEqualOneSegEdges (mesh);
+    FindClosedSurfaces (h, mesh);
+    PrintMessage (3, cntedge, " edges found");
+
+    PopStatus ();
+  }
+
+
+
+
+  void EdgeCalculation :: CalcEdges1 (double h, Mesh & mesh)
+  {
+    ARRAY<SpecialPoint> hsp(specpoints.Size());
+    ARRAY<SpecialPoint> startpoints, endpoints;
+
+    int i, j, k, l, hi, pos, ep, ne;
+    int layer;
+
+    Vec<3> a1, a2, t, n, m;
+    Point<3> p, np, pnp, hp;
+
+    Segment seg;
+    int pi1, s1, s2;
+    int lastpi, thispi;
+
+    ARRAY<Point<3> > edgepoints;
+    ARRAY<double> curvelength;
+    int copyedge, copyfromedge, copyedgeidentification;
+
+    ARRAY<int> locsurfind;
+
+    double len, corr, lam;
+    double steplen, cursteplen, loch, hd;
+
+    int checkedcopy = 0;
+
+    double size = geometry.MaxSize(); // globflags.GetNumFlag ("maxsize", 500);
+    double epspointdist2 = size * 1e-6; // globflags.GetNumFlag ("epspointdist", size * 1e-6);
+    epspointdist2 = sqr (epspointdist2);
+
+
+    Solid * locsol;
+
+
+    // copy special points to work with
+    for (i = 0; i < specpoints.Size(); i++)
+      hsp[i] = specpoints[i];
+
+
+    cntedge = 0;
+
+    while (hsp.Size())
+      {
+	SetThreadPercent(100 - 100 * double (hsp.Size()) / specpoints.Size());
+
+	edgepoints.SetSize (0);
+	curvelength.SetSize (0);
+      
+
+	pi1 = 0;
+	copyedge = 0;
+	// identifyable point available ?
+
+	//      (*testout) << endl;
+
+	for (i = 1; i <= geometry.identifications.Size() && !pi1; i++)
+	  {
+	    for (j = checkedcopy+1; j <= startpoints.Size() && !pi1; j++)
+	      {
+
+		if (geometry.identifications.Get(i)->IdentifyableCandidate (startpoints.Get(j)))
+		
+		  {
+		    int pi1cand = 0;
+		    double mindist = 1e10;
+		  
+		    for (k = 1; k <= hsp.Size() && !pi1; k++)
+		      {
+#ifdef DEVELOP
+			(*testout) << "check kand = " << hsp.Get(k).p 
+				   << ", v = " << hsp.Get(k).v 
+				   << endl;		      
+#endif
+			if (geometry.identifications.Get(i)
+			    ->Identifyable(startpoints.Get(j), hsp.Get(k)) ||
+			    geometry.identifications.Get(i)
+			    ->Identifyable(hsp.Get(k), startpoints.Get(j)))
+			  {
+
+#ifdef DEVELOP
+			    (*testout) << "identifiable, dist = "
+				       << Dist (startpoints.Get(j).p, hsp.Get(k).p) << endl;
+#endif
+
+			    if (Dist (startpoints.Get(j).p, hsp.Get(k).p) < mindist)
+			      {
+				mindist = Dist (startpoints.Get(j).p, hsp.Get(k).p);
+				pi1cand = k;
+			      }
+			    /*
+			      pi1 = k;
+			      copyedge = 1;
+			      copyfromedge = j;
+			      copyedgeidentification = i;
+			  
+			      (*testout) << "copy edge startpoint from "
+			      << startpoints.Get(j).p << " - " 
+			      << startpoints.Get(j).v 
+			      << " to " 
+			      << hsp.Get(k).p << " - " << hsp.Get(k).v << endl;
+			    */
+			  }
+		      }
+
+		    if (pi1cand)
+		      {
+			pi1 = pi1cand;
+			copyedge = 1;
+			copyfromedge = j;
+			copyedgeidentification = i;
+#ifdef DEVELOP
+			(*testout) << "copy edge startpoint from "
+				   << startpoints.Get(j).p << " - " 
+				   << startpoints.Get(j).v 
+				   << " to " 
+				   << hsp.Get(pi1).p << " - " << hsp.Get(pi1).v << endl;
+#endif
+		      }
+		  }
+	      }
+	  }
+      
+      
+	// cannot copy from other ege ?
+	if (!pi1)
+	  checkedcopy = startpoints.Size();
+      
+	// unconditional special point available ?
+	if (!pi1)
+	  for (i = 1; i <= hsp.Size() && pi1 == 0; i++)
+	    if (hsp.Get(i).unconditional == 1)
+	      pi1 = i;
+ 
+     
+	if (!pi1)
+	  {
+	    // only unconditional points available, choose first
+	    pi1 = 1;
+	  }
+
+	layer = hsp.Get(pi1).GetLayer();
+      
+
+	if (!hsp.Get(pi1).unconditional)
+	  {
+	    hsp.Elem(pi1).unconditional = 1;
+	    for (i = 1; i <= hsp.Size(); i++)
+	      if (i != pi1 && Dist (hsp.Get(pi1).p, hsp.Get(i).p) < 1e-8 &&
+		  (hsp.Get(pi1).v + hsp.Get(i).v).Length() < 1e-4)
+		{
+		  // opposite direction
+		  hsp.Elem(i).unconditional = 1;
+		}
+	  }
+
+	cntedge++;
+	startpoints.Append (hsp.Get(pi1));
+
+#ifdef DEVELOP
+	(*testout) << "edge nr " << cntedge << endl;
+	(*testout) << "start followedge: p1 = " << hsp.Get(pi1).p << ", v = " << 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);
+	    continue;
+	  }
+
+
+
+	endpoints.Append (hsp.Get(ep));
+
+
+	double elen = 0;
+	for (i = 1; i <= edgepoints.Size()-1; i++)
+	  elen += Dist (edgepoints.Get(i), edgepoints.Get(i+1));
+
+
+	int shortedge = 0;
+	for (i = 1; i <= geometry.identifications.Size(); i++)
+	  if (geometry.identifications.Get(i)->ShortEdge(hsp.Get(pi1), hsp.Get(ep)))
+	    shortedge = 1;
+	(*testout) << "shortedge = " << shortedge << endl;
+
+
+	if (!shortedge)
+	  {
+	    mesh.RestrictLocalHLine (Point3d (hsp.Get(pi1).p), 
+				     Point3d (hsp.Get(ep).p), 
+				     elen / mparam.segmentsperedge);
+	  }
+      
+	s1 = hsp.Get(pi1).s1;
+	s2 = hsp.Get(pi1).s2;
+
+
+	// delete initial, terminal and conditional points
+
+#ifdef DEVELOP
+	(*testout) << "terminal point: p = " << hsp.Get(ep).p << ", v = " << hsp.Get(ep).v << endl;      
+#endif
+	if (ep > pi1)
+	  {
+	    hsp.DeleteElement (ep);
+	    hsp.DeleteElement (pi1);
+	  }
+	else
+	  {
+	    hsp.DeleteElement (pi1);
+	    hsp.DeleteElement (ep);
+	  }
+
+
+	for (j = 1; j <= edgepoints.Size()-1; j++)
+	  {
+	    p = edgepoints.Get(j);
+	    np = Center (p, edgepoints.Get(j+1));
+	    hd = Dist2 (p, np);
+ 
+	    for (i = 1; i <= hsp.Size(); i++)
+	      if ( hsp.Get(i).HasSurfaces (s1, s2) &&
+		   hsp.Get(i).unconditional == 0 &&
+		   Dist2 (np, hsp.Get(i).p) < 1.2 * hd)
+		{
+		  hsp.DeleteElement (i);
+		  i--;
+		}
+	  }
+
+      
+	ARRAY<Segment> refedges;
+	ARRAY<int> refedgesinv;
+      
+
+	AnalyzeEdge (s1, s2, pos, layer,
+		     edgepoints,
+		     refedges, refedgesinv);
+
+	for (i = 1; i <= refedges.Size(); i++)
+	  refedges.Elem(i).edgenr = cntedge;
+
+
+#ifdef DEVELOP
+	(*testout) << "edge " << cntedge << endl
+		   << "startp: " << startpoints.Last().p 
+		   << ", v = " << startpoints.Last().v << endl
+		   << "copy = " << copyedge << endl
+		   << refedges.Size() << " refedges: ";
+	for (i = 1; i <= refedges.Size(); i++)
+	  (*testout) << " " << refedges.Get(i).si;
+	(*testout) << endl;
+	(*testout) << "inv[1] = " << refedgesinv.Get(1) << endl;
+#endif
+      
+	if (!copyedge)
+	  {
+	    int oldnseg = mesh.GetNSeg();
+
+	    if (!shortedge)
+	      StoreEdge (refedges, refedgesinv, 
+			 edgepoints, curvelength, layer, mesh);
+	    else
+	      StoreShortEdge (refedges, refedgesinv, 
+			      edgepoints, curvelength, layer, mesh);
+
+
+	    /*
+	      for (i = oldnseg+1; i <= mesh.GetNSeg(); i++)
+	      for (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, 
+		      startpoints.Get(copyfromedge).p,
+		      endpoints.Get(copyfromedge).p,
+		      edgepoints.Get(1), edgepoints.Last(),
+		      copyedgeidentification, 
+		      layer,
+		      mesh);
+	  }
+
+      }
+  }
+
+
+
+  /*
+    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)--;
+      }
+
+    (*testout) << "osedges  = " << osedges << endl;
+
+    // flag one segment edges
+    for (i = 0; i < cntedge; i++)
+      osedges[i] = (osedges[i] > 0) ? 1 : 0;
+
+    (*testout) << "osedges, now  = " << osedges << endl;
+
+    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.p1, seg.p2);
+		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 !
+  
+    for (i = 1; i <= osedgesht.GetNBags(); i++)
+      for (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)
+		      {
+			PrintSysError ("Point on edge !!!");
+			cout << "seg: " << i2 << ", p = " << pi << endl;
+			osedgesht.Set (i2, 2);		      
+		      }
+		  }
+	      }
+	}
+
+
+    // 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.p1, seg.p2);
+	    i2.Sort ();
+	    if (osedgesht.Used (i2) &&
+		osedgesht.Get (i2) == 2 &&
+		osedges.Elem(seg.edgenr) == -1)
+	      {
+		Point<3> newp = Center (mesh[PointIndex(seg.p1)],
+					mesh[PointIndex(seg.p2)]);
+
+		ProjectToEdge (geometry.GetSurface(seg.surfnr1), 
+			       geometry.GetSurface(seg.surfnr2), 
+			       newp);
+
+		osedges.Elem(seg.edgenr) = 
+		  mesh.AddPoint (newp, mesh[PointIndex(seg.p1)].GetLayer());
+	      }
+	  }
+      }
+
+
+    for (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.p1 = osedges.Get(seg.edgenr);
+		seg.p2 = osedges.Get(seg.edgenr);
+		mesh.AddSegment (newseg);
+	      }
+	  }
+      }
+
+  }
+
+
+
+  void EdgeCalculation :: 
+  FollowEdge (int pi1, int & ep, int & pos,
+	      const ARRAY<SpecialPoint> & hsp,
+	      double h, const Mesh & mesh,
+	      ARRAY<Point<3> > & edgepoints,
+	      ARRAY<double> & curvelength)
+  {
+    int i, j, s1, s2;
+    double len, steplen, cursteplen, loch;
+    Point<3> p, np, pnp;
+    Vec<3> a1, a2, t;
+
+
+    double size = geometry.MaxSize();  
+    double epspointdist2 = size * 1e-6;
+    epspointdist2 = sqr (epspointdist2);
+    int uselocalh = mparam.uselocalh;
+
+
+    s1 = hsp.Get(pi1).s1;
+    s2 = hsp.Get(pi1).s2;
+  
+    p = hsp.Get(pi1).p;
+    geometry.GetSurface(s1) -> CalcGradient (p, a1);
+    geometry.GetSurface(s2) -> CalcGradient (p, a2);
+
+    t = Cross (a1, a2);
+    t.Normalize();
+
+    pos = (hsp.Get(pi1).v * t) > 0;
+    if (!pos) t *= -1;
+
+  
+    edgepoints.Append (p);
+    curvelength.Append (0);
+    len = 0;
+
+    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);
+	if (lh < loch)
+	  loch = lh;
+      }
+
+    steplen = 0.1 * loch;
+  
+    do
+      {
+	if (multithread.terminate)
+	  return;
+      
+	if (fabs (p(0)) + fabs (p(1)) + fabs (p(2)) > 10000)
+	  {
+	    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;
+      
+#ifdef MYGRAPH
+	if (silentflag <= 2)
+	  {
+	    MyLine3D (p, np, rot);
+	    MyDraw ();
+	  }
+#endif      
+
+	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));
+
+	for (i = 1; i <= hsp.Size(); i++)
+	  // if ( i != pi1 && hsp.Get(i).HasSurfaces (s1, s2) )
+	  {
+	    if (!boxp.IsIn (hsp.Get(i).p))
+	      continue;
+	  
+	    Vec<3> hv = hsp.Get(i).p - p;
+	    if (hv.Length2() > 9 * cursteplen * cursteplen)
+	      continue;
+
+	    /*
+	    if (!hsp.Get(i).HasSurfaces (s1, s2))
+	      continue;                  // test for dalibor-problem
+	    */
+
+	    double hvt = hv * t;
+	    hv -= hvt * t;
+	  
+	    if (hv.Length() < 0.2 * cursteplen &&
+		hvt > 0 && 
+		//		  hvt < 1.5 * cursteplen &&
+		hvt < hvtmin && 
+		hsp.Get(i).unconditional == 1 &&
+		(hsp.Get(i).v + t).Length() < 0.4  ) 
+	      {
+		Point<3> hep = hsp.Get(i).p;
+		ProjectToEdge (geometry.GetSurface(s1), 
+			       geometry.GetSurface(s2), hep);            
+	      
+	      
+		if (Dist2 (hep, 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 ( (hsp.Get(i).v + ept).Length() < 1e-4 )
+		      {
+			np = hsp.Get(i).p;
+			ep = i;
+			hvtmin = hvt;
+			//			  break;
+		      }
+		  }
+	      }
+	  }
+
+	loch = min2 (geometry.GetSurface(s1) -> LocH (np, 3, 1, h), 
+		     geometry.GetSurface(s2) -> LocH (np, 3, 1, h));
+
+	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 pos, int layer,
+	       const ARRAY<Point<3> > & edgepoints,
+	       ARRAY<Segment> & refedges,
+	       ARRAY<int> & refedgesinv)
+  {
+    int i, j, k, l;
+    int hi;
+    Point<3> hp;
+    Vec<3> t, a1, a2, m, n;
+    Segment seg;
+    Solid * locsol;
+    ARRAY<int> locsurfind;
+
+    /*
+      int pi1 = 0, pi2 = 0;
+      extern Mesh * mesh;
+      for (i = 1; i <= mesh->GetNP(); i++)
+      {
+      if (Dist2 (edgepoints.Get(1), mesh->Point(i)) < 1e-12)
+      pi1 = i;
+      if (Dist2 (edgepoints.Last(), mesh->Point(i)) < 1e-12)
+      pi2 = i;
+      }
+      (*testout) << "Analyze edge: " << pi1 << " - " << pi2 << ", pts = " << edgepoints.Size() << endl;
+      (*testout) << "p1 = " << edgepoints.Get(1) << " pl = " << edgepoints.Last() << endl;
+    */
+    int debug = 0;
+    /*
+      Dist2 (Point<3> (2.69642, 1.1866, 2.03), edgepoints.Get(1)) < 1e-6 ||
+      Dist2 (Point<3> (2.69642, 1.1866, 2.03), edgepoints.Last()) < 1e-6;
+    */
+
+    if (debug)
+      {
+	//      (*testout) << "tubious edge !!!" << endl;
+	(*testout) << "s1, s2 = " << s1 << " - " << s2 << endl;
+      }
+
+    refedges.SetSize(0);
+    refedgesinv.SetSize(0);
+    hp = Center (edgepoints.Get(1), edgepoints.Get(2));
+    ProjectToEdge (geometry.GetSurface(s1), geometry.GetSurface(s2), hp);
+
+    geometry.GetSurface(s1) -> CalcGradient (hp, a1);
+    geometry.GetSurface(s2) -> CalcGradient (hp, a2);
+    t = Cross (a1, a2);
+    t.Normalize();
+    if (!pos) t *= -1;    
+  
+    (*testout) << "t = " << t << endl;
+
+    for (i = 0; i < geometry.GetNTopLevelObjects(); i++)
+      {
+	(*testout) << "layer = " << layer 
+		   << ", tlo-layer = " << geometry.GetTopLevelObject(i)->GetLayer() << endl;
+	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);
+	if (!locsol) continue;
+
+	BoxSphere<3> boxp (hp, hp);
+	boxp.Increase (1e-5);
+	boxp.CalcDiamCenter();
+      
+	ReducePrimitiveIterator rpi(boxp);
+	UnReducePrimitiveIterator urpi;
+      
+	((Solid*)locsol) -> IterateSolid (rpi);
+
+	locsol -> CalcSurfaceInverse ();
+      
+
+	if (!surf)
+	  {
+	    locsol -> GetSurfaceIndices (locsurfind);
+	  }
+	else
+	  {
+	    /*
+	      if (fabs (surf->CalcFunctionValue (hp)) < 1e-6)
+	      continue;
+	    */
+	    locsurfind.SetSize(1);
+	    locsurfind[0] = -1;
+	    for (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 (j = locsurfind.Size()-1; j >= 0; j--)
+	  if (fabs (geometry.GetSurface(locsurfind[j])
+		    ->CalcFunctionValue (hp) ) > 1e-6)
+	    locsurfind.DeleteElement(j+1);
+      
+	if (debug)
+	  (*testout) << locsurfind.Size() << " faces on hp" << endl;
+
+	for (j = 0; j < locsurfind.Size(); j++)
+	  {      
+	    int lsi = locsurfind[j];
+	    int rlsi = geometry.GetSurfaceClassRepresentant(lsi);
+	  
+	    Vec<3> rn;
+
+	    // n is outer normal to solid
+	    geometry.GetSurface(lsi) -> GetNormalVector (hp, n);
+	    if (geometry.GetSurface (lsi)->Inverse())
+	      n *= -1;
+	  
+	    if (fabs (t * n) > 1e-4) continue;
+	    if (debug)
+	      {
+		(*testout) << "face " << locsurfind.Get(j) << ", rep = " << rlsi 
+			   << " has (t*n) = " << (t*n) << endl;
+		(*testout) << "n = " << n << endl;
+	      }
+	  
+	    // rn is normal to class representant
+	    geometry.GetSurface(rlsi) -> GetNormalVector (hp, rn);
+	  
+	    int sameasref = ((n * rn) > 0);
+	  
+	    m = Cross (t, rn);
+	    m.Normalize();
+	  
+
+	    for (k = 1; k <= 2; k ++)
+	      {
+		bool edgeinv = (k == 2);
+	      
+		if (debug)
+		  {
+		    (*testout) << "onface(" << hp << ", " << m << ")= " 
+			       << locsol->OnFace (hp, m);
+		    (*testout) << " vec2in = "
+			       << locsol -> VectorIn2 (hp, m, n) << " and " 
+			       << locsol -> VectorIn2 (hp, m, -1 * n) << endl;
+		  }
+
+		//	      if (locsol -> OnFace (hp, m))
+		if (locsol -> VectorIn2 (hp, m, n) == 0 &&
+		    locsol -> VectorIn2 (hp, m, -1 * n) == 1)
+		  {
+		    hi = 0;
+		    for (l = 1; l <= refedges.Size(); l++)
+		      {
+			if (refedges.Get(l).si == rlsi &&
+			    refedgesinv.Get(l) == edgeinv)
+			  hi = l;
+		      }
+		  
+		    if (!hi)
+		      {
+			seg.si = rlsi;
+			seg.domin = -1;
+			seg.domout = -1;
+			seg.tlosurf = -1;
+			seg.surfnr1 = s1;
+			seg.surfnr2 = s2;
+			hi = refedges.Append (seg);
+			refedgesinv.Append (edgeinv);
+		      }
+		  
+		    if (!surf)
+		      {
+			if (sameasref)
+			  refedges.Elem(hi).domin = i;
+			else 
+			  refedges.Elem(hi).domout = i;
+		      }
+		    else
+		      refedges.Elem(hi).tlosurf = i;
+
+		    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
+				 << endl;
+		  }
+		m *= -1;
+	      } 
+	  }
+	delete locsol;          
+      }
+  }
+
+
+
+  void EdgeCalculation :: 
+  StoreEdge (const ARRAY<Segment> & refedges,
+	     const ARRAY<int> & 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);
+
+    len = curvelength.Last();
+    ne = int (len + 0.5);
+    if (ne == 0) ne = 1;
+    if (Dist2 (edgepoints.Get(1), edgepoints.Last()) < 1e-8 && 
+	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;
+	}
+
+    if (lastpi == -1)
+      lastpi = mesh.AddPoint (p, layer);
+
+  
+    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;
+      
+	if (thispi == -1)
+	  {
+	    ProjectToEdge (surf1, surf2, np);
+	    thispi = mesh.AddPoint (np, layer);
+	  }
+
+	for (k = 1; k <= refedges.Size(); k++)
+	  {
+	    if (refedgesinv.Get(k))
+	      {
+		seg.p1 = lastpi;
+		seg.p2 = thispi;
+	      }
+	    else
+	      {
+		seg.p1 = thispi;
+		seg.p2 = 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 " << seg.p1 << "-" << seg.p2 << 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<int> & 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;
+    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)
+	{
+	  pi1 = pi;
+	  break;
+	}
+
+    if (pi1 == -1) pi1 = mesh.AddPoint (p, layer);
+
+    p = edgepoints.Last();
+    PointIndex pi2 = -1;
+    for (pi = PointIndex::BASE; 
+	 pi < mesh.GetNP()+PointIndex::BASE; pi++)
+
+      if (Dist (mesh[pi], p) < 1e-6)
+	{
+	  pi2 = pi;
+	  break;
+	}
+    if (pi2==-1) pi2 = mesh.AddPoint (p, layer);
+
+    /*
+  
+    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);
+    }
+    */
+  
+    for (k = 1; k <= refedges.Size(); k++)
+      {
+	if (refedgesinv.Get(k))
+	  {
+	    seg.p1 = pi1;
+	    seg.p2 = pi2;
+	  }
+	else
+	  {
+	    seg.p1 = pi2;
+	    seg.p2 = 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.p1 << "-" << seg.p2 << endl;
+      }
+  }
+  
+
+
+
+
+
+
+  void EdgeCalculation :: 
+  CopyEdge (const ARRAY<Segment> & refedges,
+	    const ARRAY<int> & 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 i, j, k;
+    PointIndex pi;
+
+    // copy start and end points
+    for (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)
+	      frompi = pi;
+	    if (Dist2 (mesh[pi], top) <= 1e-16)
+	      topi = pi;
+	  }
+
+	if (topi == -1)
+	  topi = mesh.AddPoint (top, layer);
+
+	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);
+	  }
+	/*
+	  (*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 (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.p1;
+	int pi2 = oldseg.p2;
+
+	int npi1 = geometry.identifications.Get(copyedgeidentification)
+	  -> GetIdentifiedPoint (mesh, pi1);
+	int npi2 = geometry.identifications.Get(copyedgeidentification)
+	  -> GetIdentifiedPoint (mesh, pi2);
+
+	Segment seg;
+
+	for (k = 1; k <= refedges.Size(); k++)
+	  {
+	    int inv = refedgesinv.Get(k);
+
+	    // other edge is inverse
+	    if (oldseg.seginfo == 1)
+	      inv = !inv;
+
+	    //	  (*testout) << "inv, now = " << inv << endl;
+
+	    if (inv)
+	      {
+		seg.p1 = npi1;
+		seg.p2 = npi2;
+	      }
+	    else
+	      {
+		seg.p1 = npi2;
+		seg.p2 = 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.p1 << "-" << seg.p2 << endl;
+#ifdef DEVELOP
+
+	    (*testout) << "copy seg, face = " << seg.si << ": " 
+		       << " inv = " << inv << ", refinv = " << refedgesinv.Get(k)
+		       << mesh.Point(seg.p1) << ", " << mesh.Point(seg.p2) << 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;
+
+    BitArray pointatsurface (nsurf);
+    Point<3> p1, p2;
+    Vec<3> nv, tv;
+    Solid * tansol;
+    ARRAY<int> tansurfind;
+    //  const Solid * sol;
+
+    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();
+	    s -> GetNormalVector (p1, nv);
+		    
+	    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);
+		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);
+		mesh.AddPoint (p2, layer);
+		seg1.p1 = mesh.GetNP()-1;
+		seg1.p2 = mesh.GetNP();
+		seg2.p2 = mesh.GetNP()-1;
+		seg2.p1 = 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_new.cpp b/contrib/Netgen/libsrc/csg/edgeflw_new.cpp
new file mode 100644
index 0000000000..8aa9f0263b
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/edgeflw_new.cpp
@@ -0,0 +1,1553 @@
+#include <mystdlib.h>
+#include <meshing.hpp>
+#include <csg.hpp>
+
+#undef DEVELOP
+
+namespace netgen
+{
+
+  EdgeCalculation :: 
+  EdgeCalculation (const CSGeometry & ageometry,
+		   ARRAY<SpecialPoint> & aspecpoints)
+    : geometry(ageometry), specpoints(aspecpoints)
+  {
+    Box<3> bbox;
+    if (specpoints.Size() >= 1)
+      bbox.Set (specpoints[0].p);
+    for (int i = 1; i < specpoints.Size(); i++)
+      bbox.Add (specpoints[i].p);
+
+    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);
+  }
+
+  EdgeCalculation :: ~EdgeCalculation()
+  {
+    delete searchtree;
+    delete meshpoint_tree;
+  }
+
+
+  void EdgeCalculation :: Calc(double h, Mesh & mesh)
+  {
+    (*testout) << "Find edges" << endl;
+    PrintMessage (1, "Find edges");
+    PushStatus ("Find edges");
+
+    CalcEdges1 (h, mesh);
+    SplitEqualOneSegEdges (mesh);
+    FindClosedSurfaces (h, mesh);
+    PrintMessage (3, cntedge, " edges found");
+
+    for (SegmentIndex si = 0; si < mesh.GetNSeg(); si++)
+      (*testout) << "seg " << si << " = " << mesh[si] << endl;
+    PopStatus ();
+  }
+
+
+
+
+
+  void EdgeCalculation :: CalcEdges1 (double h, Mesh & mesh)
+  {
+    ARRAY<int> hsp(specpoints.Size());
+    ARRAY<int> glob2hsp(specpoints.Size());
+    ARRAY<int> startpoints, endpoints;
+
+    int i, j, k, l, hi, pos, ep, ne;
+    int layer;
+
+    Vec<3> a1, a2, t, n, m;
+    Point<3> p, np, pnp, hp;
+
+    Segment seg;
+    int pi1, s1, s2;
+    int lastpi, thispi;
+
+    ARRAY<Point<3> > edgepoints;
+    ARRAY<double> curvelength;
+    int copyedge, copyfromedge, copyedgeidentification;
+
+    ARRAY<int> locsurfind, locind;
+
+    double len, corr, lam;
+    double steplen, cursteplen, loch, hd;
+
+    int checkedcopy = 0;
+
+    double size = geometry.MaxSize(); // globflags.GetNumFlag ("maxsize", 500);
+    double epspointdist2 = size * 1e-6; // globflags.GetNumFlag ("epspointdist", size * 1e-6);
+    epspointdist2 = sqr (epspointdist2);
+
+    
+
+    Solid * locsol;
+
+
+    // copy special points to work with
+    for (i = 0; i < specpoints.Size(); i++)
+      {
+	hsp[i] = i;
+	glob2hsp[i] = i;
+      }
+
+
+    cntedge = 0;
+
+    while (hsp.Size())
+      {
+	SetThreadPercent(100 - 100 * double (hsp.Size()) / specpoints.Size());
+
+	edgepoints.SetSize (0);
+	curvelength.SetSize (0);
+      
+
+	pi1 = 0;
+	copyedge = 0;
+	// identifyable point available ?
+
+	//      (*testout) << endl;
+
+	for (i = 1; i <= geometry.identifications.Size() && !pi1; i++)
+	  {
+	    for (j = checkedcopy+1; j <= startpoints.Size() && !pi1; j++)
+	      {
+
+		if (geometry.identifications.Get(i)->IdentifyableCandidate (specpoints[startpoints.Get(j)]))
+		
+		  {
+		    int pi1cand = 0;
+		    double mindist = 1e10;
+		  
+		    for (k = 1; k <= hsp.Size() && !pi1; k++)
+		      {
+#ifdef DEVELOP
+			(*testout) << "check kand = " << hsp.Get(k).p 
+				   << ", v = " << hsp.Get(k).v 
+				   << endl;		      
+#endif
+			if (geometry.identifications.Get(i)
+			    ->Identifyable(specpoints[startpoints.Get(j)], specpoints[hsp.Get(k)]) ||
+			    geometry.identifications.Get(i)
+			    ->Identifyable(specpoints[hsp.Get(k)], specpoints[startpoints.Get(j)]))
+			  {
+
+#ifdef DEVELOP
+			    (*testout) << "identifiable, dist = "
+				       << Dist (startpoints.Get(j).p, hsp.Get(k).p) << endl;
+#endif
+
+			    if (Dist (specpoints[startpoints.Get(j)].p, specpoints[hsp.Get(k)].p) < mindist)
+			      {
+				mindist = Dist (specpoints[startpoints.Get(j)].p, specpoints[hsp.Get(k)].p);
+				pi1cand = k;
+			      }
+			    /*
+			      pi1 = k;
+			      copyedge = 1;
+			      copyfromedge = j;
+			      copyedgeidentification = i;
+			  
+			      (*testout) << "copy edge startpoint from "
+			      << startpoints.Get(j).p << " - " 
+			      << startpoints.Get(j).v 
+			      << " to " 
+			      << hsp.Get(k).p << " - " << hsp.Get(k).v << endl;
+			    */
+			  }
+		      }
+
+		    if (pi1cand)
+		      {
+			pi1 = pi1cand;
+			copyedge = 1;
+			copyfromedge = j;
+			copyedgeidentification = i;
+#ifdef DEVELOP
+			(*testout) << "copy edge startpoint from "
+				   << startpoints.Get(j).p << " - " 
+				   << startpoints.Get(j).v 
+				   << " to " 
+				   << specpoints[hsp.Get(pi1)].p << " - " << hsp.Get(pi1).v << endl;
+#endif
+		      }
+		  }
+	      }
+	  }
+      
+      
+	// cannot copy from other ege ?
+	if (!pi1)
+	  checkedcopy = startpoints.Size();
+      
+	// unconditional special point available ?
+	if (!pi1)
+	  for (i = 1; i <= hsp.Size(); i++)
+	    if (specpoints[hsp.Get(i)].unconditional == 1)
+	      {
+		pi1 = i;
+		break;
+	      }
+ 
+     
+	if (!pi1)
+	  {
+	    // only unconditional points available, choose first
+	    pi1 = 1;
+	  }
+
+	layer = specpoints[hsp.Get(pi1)].GetLayer();
+      
+
+	if (!specpoints[hsp.Get(pi1)].unconditional)
+	  {
+	    specpoints[hsp.Elem(pi1)].unconditional = 1;
+	    for (i = 1; i <= hsp.Size(); i++)
+	      if (i != pi1 && 
+		  Dist (specpoints[hsp.Get(pi1)].p, specpoints[hsp.Get(i)].p) < 1e-8 &&
+		  (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) << "edge nr " << cntedge << endl;
+	(*testout) << "start followedge: p1 = " << hsp.Get(pi1).p << ", v = " << 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 (i = 1; i <= edgepoints.Size()-1; i++)
+	  elen += Dist (edgepoints.Get(i), edgepoints.Get(i+1));
+
+
+	int shortedge = 0;
+	for (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;
+
+
+	// delete initial, terminal and conditional points
+
+#ifdef DEVELOP
+	(*testout) << "terminal point: p = " << hsp.Get(ep).p << ", v = " << 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 (j = 1; j <= edgepoints.Size()-1; j++)
+	  {
+	    p = edgepoints.Get(j);
+	    np = Center (p, edgepoints.Get(j+1));
+	    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 (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 (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<int> refedgesinv;
+      
+
+	AnalyzeEdge (s1, s2, pos, layer,
+		     edgepoints,
+		     refedges, refedgesinv);
+
+	for (i = 1; i <= refedges.Size(); i++)
+	  refedges.Elem(i).edgenr = cntedge;
+
+
+#ifdef DEVELOP
+	(*testout) << "edge " << cntedge << endl
+		   << "startp: " << startpoints.Last().p 
+		   << ", v = " << startpoints.Last().v << endl
+		   << "copy = " << copyedge << endl
+		   << refedges.Size() << " refedges: ";
+	for (i = 1; i <= refedges.Size(); i++)
+	  (*testout) << " " << refedges.Get(i).si;
+	(*testout) << endl;
+	(*testout) << "inv[1] = " << refedgesinv.Get(1) << endl;
+#endif
+      
+	if (!copyedge)
+	  {
+	    int oldnseg = mesh.GetNSeg();
+
+	    if (!shortedge)
+	      StoreEdge (refedges, refedgesinv, 
+			 edgepoints, curvelength, layer, mesh);
+	    else
+	      StoreShortEdge (refedges, refedgesinv, 
+			      edgepoints, curvelength, layer, mesh);
+
+
+	    /*
+	      for (i = oldnseg+1; i <= mesh.GetNSeg(); i++)
+	      for (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);
+	  }
+
+      }
+  }
+
+
+
+  /*
+    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)--;
+      }
+
+    (*testout) << "osedges  = " << osedges << endl;
+
+    // flag one segment edges
+    for (i = 0; i < cntedge; i++)
+      osedges[i] = (osedges[i] > 0) ? 1 : 0;
+
+    (*testout) << "osedges, now  = " << osedges << endl;
+
+    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.p1, seg.p2);
+		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 !
+  
+    for (i = 1; i <= osedgesht.GetNBags(); i++)
+      for (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)
+		      {
+			PrintSysError ("Point on edge !!!");
+			cout << "seg: " << i2 << ", p = " << pi << endl;
+			osedgesht.Set (i2, 2);		      
+		      }
+		  }
+	      }
+	}
+
+
+    // 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.p1, seg.p2);
+	    i2.Sort ();
+	    if (osedgesht.Used (i2) &&
+		osedgesht.Get (i2) == 2 &&
+		osedges.Elem(seg.edgenr) == -1)
+	      {
+		Point<3> newp = Center (mesh[PointIndex(seg.p1)],
+					mesh[PointIndex(seg.p2)]);
+
+		ProjectToEdge (geometry.GetSurface(seg.surfnr1), 
+			       geometry.GetSurface(seg.surfnr2), 
+			       newp);
+
+		osedges.Elem(seg.edgenr) = 
+		  mesh.AddPoint (newp, mesh[PointIndex(seg.p1)].GetLayer());
+		meshpoint_tree -> Insert (newp, osedges.Elem(seg.edgenr));
+	      }
+	  }
+      }
+
+
+    for (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.p1 = osedges.Get(seg.edgenr);
+		seg.p2 = 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 i, j, s1, s2;
+    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 = specpoints[hsp.Get(pi1)].s1;
+    s2 = specpoints[hsp.Get(pi1)].s2;
+  
+    p = specpoints[hsp.Get(pi1)].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;
+
+    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);
+	if (lh < loch)
+	  loch = lh;
+      }
+
+    steplen = 0.1 * loch;
+  
+    do
+      {
+	if (multithread.terminate)
+	  return;
+      
+	if (fabs (p(0)) + fabs (p(1)) + fabs (p(2)) > 10000)
+	  {
+	    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;
+      
+#ifdef MYGRAPH
+	if (silentflag <= 2)
+	  {
+	    MyLine3D (p, np, rot);
+	    MyDraw ();
+	  }
+#endif      
+
+	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 (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 (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) -> LocH (np, 3, 1, h), 
+		     geometry.GetSurface(s2) -> LocH (np, 3, 1, h));
+
+	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 pos, int layer,
+	       const ARRAY<Point<3> > & edgepoints,
+	       ARRAY<Segment> & refedges,
+	       ARRAY<int> & refedgesinv)
+  {
+    int i, j, k, l;
+    int hi;
+    Point<3> hp;
+    Vec<3> t, a1, a2, m, n;
+    Segment seg;
+    Solid * locsol;
+    ARRAY<int> locsurfind;
+
+    /*
+      int pi1 = 0, pi2 = 0;
+      extern Mesh * mesh;
+      for (i = 1; i <= mesh->GetNP(); i++)
+      {
+      if (Dist2 (edgepoints.Get(1), mesh->Point(i)) < 1e-12)
+      pi1 = i;
+      if (Dist2 (edgepoints.Last(), mesh->Point(i)) < 1e-12)
+      pi2 = i;
+      }
+      (*testout) << "Analyze edge: " << pi1 << " - " << pi2 << ", pts = " << edgepoints.Size() << endl;
+      (*testout) << "p1 = " << edgepoints.Get(1) << " pl = " << edgepoints.Last() << endl;
+    */
+    int debug = 0;
+    /*
+      Dist2 (Point<3> (2.69642, 1.1866, 2.03), edgepoints.Get(1)) < 1e-6 ||
+      Dist2 (Point<3> (2.69642, 1.1866, 2.03), edgepoints.Last()) < 1e-6;
+    */
+
+    if (debug)
+      {
+	//      (*testout) << "tubious edge !!!" << endl;
+	(*testout) << "s1, s2 = " << s1 << " - " << s2 << endl;
+      }
+
+    refedges.SetSize(0);
+    refedgesinv.SetSize(0);
+    hp = Center (edgepoints.Get(1), edgepoints.Get(2));
+    ProjectToEdge (geometry.GetSurface(s1), geometry.GetSurface(s2), hp);
+
+    geometry.GetSurface(s1) -> CalcGradient (hp, a1);
+    geometry.GetSurface(s2) -> CalcGradient (hp, a2);
+    t = Cross (a1, a2);
+    t.Normalize();
+    if (!pos) t *= -1;    
+  
+    (*testout) << "t = " << t << endl;
+
+    for (i = 0; i < geometry.GetNTopLevelObjects(); i++)
+      {
+	(*testout) << "layer = " << layer 
+		   << ", tlo-layer = " << geometry.GetTopLevelObject(i)->GetLayer() << endl;
+	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);
+	if (!locsol) continue;
+
+	BoxSphere<3> boxp (hp, hp);
+	boxp.Increase (1e-5);
+	boxp.CalcDiamCenter();
+      
+	ReducePrimitiveIterator rpi(boxp);
+	UnReducePrimitiveIterator urpi;
+      
+	((Solid*)locsol) -> IterateSolid (rpi);
+
+	locsol -> CalcSurfaceInverse ();
+      
+
+	if (!surf)
+	  {
+	    locsol -> GetSurfaceIndices (locsurfind);
+	  }
+	else
+	  {
+	    /*
+	      if (fabs (surf->CalcFunctionValue (hp)) < 1e-6)
+	      continue;
+	    */
+	    locsurfind.SetSize(1);
+	    locsurfind[0] = -1;
+	    for (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 (j = locsurfind.Size()-1; j >= 0; j--)
+	  if (fabs (geometry.GetSurface(locsurfind[j])
+		    ->CalcFunctionValue (hp) ) > 1e-6)
+	    locsurfind.DeleteElement(j+1);
+      
+	if (debug)
+	  (*testout) << locsurfind.Size() << " faces on hp" << endl;
+
+	for (j = 0; j < locsurfind.Size(); j++)
+	  {      
+	    int lsi = locsurfind[j];
+	    int rlsi = geometry.GetSurfaceClassRepresentant(lsi);
+	  
+	    Vec<3> rn;
+
+	    // n is outer normal to solid
+	    geometry.GetSurface(lsi) -> GetNormalVector (hp, n);
+	    if (geometry.GetSurface (lsi)->Inverse())
+	      n *= -1;
+	  
+	    if (fabs (t * n) > 1e-4) continue;
+	    if (debug)
+	      {
+		(*testout) << "face " << locsurfind.Get(j) << ", rep = " << rlsi 
+			   << " has (t*n) = " << (t*n) << endl;
+		(*testout) << "n = " << n << endl;
+	      }
+	  
+	    // rn is normal to class representant
+	    geometry.GetSurface(rlsi) -> GetNormalVector (hp, rn);
+	  
+	    int sameasref = ((n * rn) > 0);
+	  
+	    m = Cross (t, rn);
+	    m.Normalize();
+	  
+
+	    for (k = 1; k <= 2; k ++)
+	      {
+		bool edgeinv = (k == 2);
+	      
+		if (debug)
+		  {
+		    (*testout) << "onface(" << hp << ", " << m << ")= " 
+			       << locsol->OnFace (hp, m);
+		    (*testout) << " vec2in = "
+			       << locsol -> VectorIn2 (hp, m, n) << " and " 
+			       << locsol -> VectorIn2 (hp, m, -1 * n) << endl;
+		  }
+
+		//	      if (locsol -> OnFace (hp, m))
+		if (locsol -> VectorIn2 (hp, m, n) == 0 &&
+		    locsol -> VectorIn2 (hp, m, -1 * n) == 1)
+		  {
+		    hi = 0;
+		    for (l = 1; l <= refedges.Size(); l++)
+		      {
+			if (refedges.Get(l).si == rlsi &&
+			    refedgesinv.Get(l) == edgeinv)
+			  hi = l;
+		      }
+		  
+		    if (!hi)
+		      {
+			seg.si = rlsi;
+			seg.domin = -1;
+			seg.domout = -1;
+			seg.tlosurf = -1;
+			seg.surfnr1 = s1;
+			seg.surfnr2 = s2;
+			hi = refedges.Append (seg);
+			refedgesinv.Append (edgeinv);
+		      }
+		  
+		    if (!surf)
+		      {
+			if (sameasref)
+			  refedges.Elem(hi).domin = i;
+			else 
+			  refedges.Elem(hi).domout = i;
+		      }
+		    else
+		      refedges.Elem(hi).tlosurf = i;
+
+		    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
+				 << endl;
+		  }
+		m *= -1;
+	      } 
+	  }
+	delete locsol;          
+      }
+  }
+
+
+
+  void EdgeCalculation :: 
+  StoreEdge (const ARRAY<Segment> & refedges,
+	     const ARRAY<int> & 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);
+
+    len = curvelength.Last();
+    ne = int (len + 0.5);
+    if (ne == 0) ne = 1;
+    if (Dist2 (edgepoints.Get(1), edgepoints.Last()) < 1e-8 && 
+	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;
+	}
+    */
+    ARRAY<int> locsearch;
+    meshpoint_tree -> GetIntersecting (p-Vec<3> (1e-6, 1e-6, 1e-6),
+				       p+Vec<3> (1e-6, 1e-6, 1e-6), locsearch);
+    if (locsearch.Size())
+      lastpi = locsearch[0];
+				       
+
+
+    if (lastpi == -1)
+      {
+	lastpi = mesh.AddPoint (p, layer);
+	meshpoint_tree -> Insert (p, lastpi); 
+      }
+  
+    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> (1e-6, 1e-6, 1e-6),
+					       np+Vec<3> (1e-6, 1e-6, 1e-6), locsearch);
+	    if (locsearch.Size())
+	      thispi = locsearch[0];
+	  }
+
+	if (thispi == -1)
+	  {
+	    ProjectToEdge (surf1, surf2, np);
+	    thispi = mesh.AddPoint (np, layer);
+	    meshpoint_tree -> Insert (np, thispi);
+	  }
+
+	for (k = 1; k <= refedges.Size(); k++)
+	  {
+	    if (refedgesinv.Get(k))
+	      {
+		seg.p1 = lastpi;
+		seg.p2 = thispi;
+	      }
+	    else
+	      {
+		seg.p1 = thispi;
+		seg.p2 = 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 " << seg.p1 << "-" << seg.p2 << 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<int> & 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;
+    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)
+	{
+	  pi1 = pi;
+	  break;
+	}
+
+    if (pi1 == -1) 
+      {
+	pi1 = mesh.AddPoint (p, layer);
+	meshpoint_tree -> Insert (p, pi1);
+      }
+
+    p = edgepoints.Last();
+    PointIndex pi2 = -1;
+    for (pi = PointIndex::BASE; 
+	 pi < mesh.GetNP()+PointIndex::BASE; pi++)
+
+      if (Dist (mesh[pi], p) < 1e-6)
+	{
+	  pi2 = pi;
+	  break;
+	}
+    if (pi2==-1) 
+      {
+	pi2 = mesh.AddPoint (p, layer);
+	meshpoint_tree -> Insert (p, pi2);
+      }
+
+    /*
+  
+    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);
+    }
+    */
+  
+    for (k = 1; k <= refedges.Size(); k++)
+      {
+	if (refedgesinv.Get(k))
+	  {
+	    seg.p1 = pi1;
+	    seg.p2 = pi2;
+	  }
+	else
+	  {
+	    seg.p1 = pi2;
+	    seg.p2 = 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.p1 << "-" << seg.p2 << endl;
+      }
+  }
+  
+
+
+
+
+
+
+  void EdgeCalculation :: 
+  CopyEdge (const ARRAY<Segment> & refedges,
+	    const ARRAY<int> & 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 i, j, k;
+    PointIndex pi;
+
+    // copy start and end points
+    for (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)
+	      frompi = pi;
+	    if (Dist2 (mesh[pi], top) <= 1e-16)
+	      topi = pi;
+	  }
+
+	if (topi == -1)
+	  {
+	    topi = mesh.AddPoint (top, layer);
+	    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);
+	  }
+	/*
+	  (*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 (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.p1;
+	int pi2 = oldseg.p2;
+
+	int npi1 = geometry.identifications.Get(copyedgeidentification)
+	  -> GetIdentifiedPoint (mesh, pi1);
+	int npi2 = geometry.identifications.Get(copyedgeidentification)
+	  -> GetIdentifiedPoint (mesh, pi2);
+
+	Segment seg;
+
+	for (k = 1; k <= refedges.Size(); k++)
+	  {
+	    int inv = refedgesinv.Get(k);
+
+	    // other edge is inverse
+	    if (oldseg.seginfo == 1)
+	      inv = !inv;
+
+	    //	  (*testout) << "inv, now = " << inv << endl;
+
+	    if (inv)
+	      {
+		seg.p1 = npi1;
+		seg.p2 = npi2;
+	      }
+	    else
+	      {
+		seg.p1 = npi2;
+		seg.p2 = 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.p1 << "-" << seg.p2 << endl;
+#ifdef DEVELOP
+
+	    (*testout) << "copy seg, face = " << seg.si << ": " 
+		       << " inv = " << inv << ", refinv = " << refedgesinv.Get(k)
+		       << mesh.Point(seg.p1) << ", " << mesh.Point(seg.p2) << 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;
+
+    BitArray pointatsurface (nsurf);
+    Point<3> p1, p2;
+    Vec<3> nv, tv;
+    Solid * tansol;
+    ARRAY<int> tansurfind;
+    //  const Solid * sol;
+
+    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();
+	    s -> GetNormalVector (p1, nv);
+		    
+	    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);
+		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);
+		mesh.AddPoint (p2, layer);
+		seg1.p1 = mesh.GetNP()-1;
+		seg1.p2 = mesh.GetNP();
+		seg2.p2 = mesh.GetNP()-1;
+		seg2.p1 = 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_old.cpp b/contrib/Netgen/libsrc/csg/edgeflw_old.cpp
new file mode 100644
index 0000000000..5321dfd17d
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/edgeflw_old.cpp
@@ -0,0 +1,1405 @@
+#include <mystdlib.h>
+#include <meshing.hpp>
+#include <csg.hpp>
+
+#undef DEVELOP
+
+namespace netgen
+{
+
+  EdgeCalculation :: 
+  EdgeCalculation (const CSGeometry & ageometry,
+		   ARRAY<SpecialPoint> & aspecpoints)
+    : geometry(ageometry), specpoints(aspecpoints)
+  {
+    ;
+  }
+
+  EdgeCalculation :: ~EdgeCalculation ()
+  { ; }
+
+  void EdgeCalculation :: Calc(double h, Mesh & mesh)
+  {
+    PrintMessage (1, "Find edges");
+    PushStatus ("Find edges");
+
+    CalcEdges1 (h, mesh);
+    SplitEqualOneSegEdges (mesh);
+    FindClosedSurfaces (h, mesh);
+    PrintMessage (3, cntedge, " edges found");
+
+    PopStatus ();
+  }
+
+
+
+
+  void EdgeCalculation :: CalcEdges1 (double h, Mesh & mesh)
+  {
+    ARRAY<SpecialPoint> hsp(specpoints.Size());
+    ARRAY<SpecialPoint> startpoints, endpoints;
+
+    int i, j, k, l, hi, pos, ep, ne;
+    int layer;
+
+    Vec<3> a1, a2, t, n, m;
+    Point<3> p, np, pnp, hp;
+
+    Segment seg;
+    int pi1, s1, s2;
+    int lastpi, thispi;
+
+    ARRAY<Point<3> > edgepoints;
+    ARRAY<double> curvelength;
+    int copyedge, copyfromedge, copyedgeidentification;
+
+    ARRAY<int> locsurfind;
+
+    double len, corr, lam;
+    double steplen, cursteplen, loch, hd;
+
+    int checkedcopy = 0;
+
+    double size = geometry.MaxSize(); // globflags.GetNumFlag ("maxsize", 500);
+    double epspointdist2 = size * 1e-6; // globflags.GetNumFlag ("epspointdist", size * 1e-6);
+    epspointdist2 = sqr (epspointdist2);
+
+
+    Solid * locsol;
+
+
+    // copy special points to work with
+    for (i = 0; i < specpoints.Size(); i++)
+      hsp[i] = specpoints[i];
+
+
+    cntedge = 0;
+
+    while (hsp.Size())
+      {
+	SetThreadPercent(100 - 100 * double (hsp.Size()) / specpoints.Size());
+
+	edgepoints.SetSize (0);
+	curvelength.SetSize (0);
+      
+
+	pi1 = 0;
+	copyedge = 0;
+	// identifyable point available ?
+
+	//      (*testout) << endl;
+
+	for (i = 1; i <= geometry.identifications.Size() && !pi1; i++)
+	  {
+	    for (j = checkedcopy+1; j <= startpoints.Size() && !pi1; j++)
+	      {
+
+		if (geometry.identifications.Get(i)->IdentifyableCandidate (startpoints.Get(j)))
+		
+		  {
+		    int pi1cand = 0;
+		    double mindist = 1e10;
+		  
+		    for (k = 1; k <= hsp.Size() && !pi1; k++)
+		      {
+#ifdef DEVELOP
+			(*testout) << "check kand = " << hsp.Get(k).p 
+				   << ", v = " << hsp.Get(k).v 
+				   << endl;		      
+#endif
+			if (geometry.identifications.Get(i)
+			    ->Identifyable(startpoints.Get(j), hsp.Get(k)) ||
+			    geometry.identifications.Get(i)
+			    ->Identifyable(hsp.Get(k), startpoints.Get(j)))
+			  {
+
+#ifdef DEVELOP
+			    (*testout) << "identifiable, dist = "
+				       << Dist (startpoints.Get(j).p, hsp.Get(k).p) << endl;
+#endif
+
+			    if (Dist (startpoints.Get(j).p, hsp.Get(k).p) < mindist)
+			      {
+				mindist = Dist (startpoints.Get(j).p, hsp.Get(k).p);
+				pi1cand = k;
+			      }
+			    /*
+			      pi1 = k;
+			      copyedge = 1;
+			      copyfromedge = j;
+			      copyedgeidentification = i;
+			  
+			      (*testout) << "copy edge startpoint from "
+			      << startpoints.Get(j).p << " - " 
+			      << startpoints.Get(j).v 
+			      << " to " 
+			      << hsp.Get(k).p << " - " << hsp.Get(k).v << endl;
+			    */
+			  }
+		      }
+
+		    if (pi1cand)
+		      {
+			pi1 = pi1cand;
+			copyedge = 1;
+			copyfromedge = j;
+			copyedgeidentification = i;
+#ifdef DEVELOP
+			(*testout) << "copy edge startpoint from "
+				   << startpoints.Get(j).p << " - " 
+				   << startpoints.Get(j).v 
+				   << " to " 
+				   << hsp.Get(pi1).p << " - " << hsp.Get(pi1).v << endl;
+#endif
+		      }
+		  }
+	      }
+	  }
+      
+      
+	// cannot copy from other ege ?
+	if (!pi1)
+	  checkedcopy = startpoints.Size();
+      
+	// unconditional special point available ?
+	if (!pi1)
+	  for (i = 1; i <= hsp.Size() && pi1 == 0; i++)
+	    if (hsp.Get(i).unconditional == 1)
+	      pi1 = i;
+ 
+     
+	if (!pi1)
+	  {
+	    // only unconditional points available, choose first
+	    pi1 = 1;
+	  }
+
+	layer = hsp.Get(pi1).GetLayer();
+      
+
+	if (!hsp.Get(pi1).unconditional)
+	  {
+	    hsp.Elem(pi1).unconditional = 1;
+	    for (i = 1; i <= hsp.Size(); i++)
+	      if (i != pi1 && Dist (hsp.Get(pi1).p, hsp.Get(i).p) < 1e-8 &&
+		  (hsp.Get(pi1).v + hsp.Get(i).v).Length() < 1e-4)
+		{
+		  // opposite direction
+		  hsp.Elem(i).unconditional = 1;
+		}
+	  }
+
+	cntedge++;
+	startpoints.Append (hsp.Get(pi1));
+
+#ifdef DEVELOP
+	(*testout) << "edge nr " << cntedge << endl;
+	(*testout) << "start followedge: p1 = " << hsp.Get(pi1).p << ", v = " << 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);
+	    continue;
+	  }
+
+
+
+	endpoints.Append (hsp.Get(ep));
+
+
+	double elen = 0;
+	for (i = 1; i <= edgepoints.Size()-1; i++)
+	  elen += Dist (edgepoints.Get(i), edgepoints.Get(i+1));
+
+
+	int shortedge = 0;
+	for (i = 1; i <= geometry.identifications.Size(); i++)
+	  if (geometry.identifications.Get(i)->ShortEdge(hsp.Get(pi1), hsp.Get(ep)))
+	    shortedge = 1;
+	(*testout) << "shortedge = " << shortedge << endl;
+
+
+	if (!shortedge)
+	  {
+	    mesh.RestrictLocalHLine (Point3d (hsp.Get(pi1).p), 
+				     Point3d (hsp.Get(ep).p), 
+				     elen / mparam.segmentsperedge);
+	  }
+      
+	s1 = hsp.Get(pi1).s1;
+	s2 = hsp.Get(pi1).s2;
+
+
+	// delete initial, terminal and conditional points
+
+#ifdef DEVELOP
+	(*testout) << "terminal point: p = " << hsp.Get(ep).p << ", v = " << hsp.Get(ep).v << endl;      
+#endif
+	if (ep > pi1)
+	  {
+	    hsp.DeleteElement (ep);
+	    hsp.DeleteElement (pi1);
+	  }
+	else
+	  {
+	    hsp.DeleteElement (pi1);
+	    hsp.DeleteElement (ep);
+	  }
+
+
+	for (j = 1; j <= edgepoints.Size()-1; j++)
+	  {
+	    p = edgepoints.Get(j);
+	    np = Center (p, edgepoints.Get(j+1));
+	    hd = Dist2 (p, np);
+ 
+	    for (i = 1; i <= hsp.Size(); i++)
+	      if ( hsp.Get(i).HasSurfaces (s1, s2) &&
+		   hsp.Get(i).unconditional == 0 &&
+		   Dist2 (np, hsp.Get(i).p) < 1.2 * hd)
+		{
+		  hsp.DeleteElement (i);
+		  i--;
+		}
+	  }
+
+      
+	ARRAY<Segment> refedges;
+	ARRAY<int> refedgesinv;
+      
+
+	AnalyzeEdge (s1, s2, pos, layer,
+		     edgepoints,
+		     refedges, refedgesinv);
+
+	for (i = 1; i <= refedges.Size(); i++)
+	  refedges.Elem(i).edgenr = cntedge;
+
+
+#ifdef DEVELOP
+	(*testout) << "edge " << cntedge << endl
+		   << "startp: " << startpoints.Last().p 
+		   << ", v = " << startpoints.Last().v << endl
+		   << "copy = " << copyedge << endl
+		   << refedges.Size() << " refedges: ";
+	for (i = 1; i <= refedges.Size(); i++)
+	  (*testout) << " " << refedges.Get(i).si;
+	(*testout) << endl;
+	(*testout) << "inv[1] = " << refedgesinv.Get(1) << endl;
+#endif
+      
+	if (!copyedge)
+	  {
+	    int oldnseg = mesh.GetNSeg();
+
+	    if (!shortedge)
+	      StoreEdge (refedges, refedgesinv, 
+			 edgepoints, curvelength, layer, mesh);
+	    else
+	      StoreShortEdge (refedges, refedgesinv, 
+			      edgepoints, curvelength, layer, mesh);
+
+
+	    /*
+	      for (i = oldnseg+1; i <= mesh.GetNSeg(); i++)
+	      for (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, 
+		      startpoints.Get(copyfromedge).p,
+		      endpoints.Get(copyfromedge).p,
+		      edgepoints.Get(1), edgepoints.Last(),
+		      copyedgeidentification, 
+		      layer,
+		      mesh);
+	  }
+
+      }
+  }
+
+
+
+  /*
+    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)--;
+      }
+
+    (*testout) << "osedges  = " << osedges << endl;
+
+    // flag one segment edges
+    for (i = 0; i < cntedge; i++)
+      osedges[i] = (osedges[i] > 0) ? 1 : 0;
+
+    (*testout) << "osedges, now  = " << osedges << endl;
+
+    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.p1, seg.p2);
+		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 !
+  
+    for (i = 1; i <= osedgesht.GetNBags(); i++)
+      for (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)
+		      {
+			PrintSysError ("Point on edge !!!");
+			cout << "seg: " << i2 << ", p = " << pi << endl;
+			osedgesht.Set (i2, 2);		      
+		      }
+		  }
+	      }
+	}
+
+
+    // 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.p1, seg.p2);
+	    i2.Sort ();
+	    if (osedgesht.Used (i2) &&
+		osedgesht.Get (i2) == 2 &&
+		osedges.Elem(seg.edgenr) == -1)
+	      {
+		Point<3> newp = Center (mesh[PointIndex(seg.p1)],
+					mesh[PointIndex(seg.p2)]);
+
+		ProjectToEdge (geometry.GetSurface(seg.surfnr1), 
+			       geometry.GetSurface(seg.surfnr2), 
+			       newp);
+
+		osedges.Elem(seg.edgenr) = 
+		  mesh.AddPoint (newp, mesh[PointIndex(seg.p1)].GetLayer());
+	      }
+	  }
+      }
+
+
+    for (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.p1 = osedges.Get(seg.edgenr);
+		seg.p2 = osedges.Get(seg.edgenr);
+		mesh.AddSegment (newseg);
+	      }
+	  }
+      }
+
+  }
+
+
+
+  void EdgeCalculation :: 
+  FollowEdge (int pi1, int & ep, int & pos,
+	      const ARRAY<SpecialPoint> & hsp,
+	      double h, const Mesh & mesh,
+	      ARRAY<Point<3> > & edgepoints,
+	      ARRAY<double> & curvelength)
+  {
+    int i, j, s1, s2;
+    double len, steplen, cursteplen, loch;
+    Point<3> p, np, pnp;
+    Vec<3> a1, a2, t;
+
+
+    double size = geometry.MaxSize();  
+    double epspointdist2 = size * 1e-6;
+    epspointdist2 = sqr (epspointdist2);
+    int uselocalh = mparam.uselocalh;
+
+
+    s1 = hsp.Get(pi1).s1;
+    s2 = hsp.Get(pi1).s2;
+  
+    p = hsp.Get(pi1).p;
+    geometry.GetSurface(s1) -> CalcGradient (p, a1);
+    geometry.GetSurface(s2) -> CalcGradient (p, a2);
+
+    t = Cross (a1, a2);
+    t.Normalize();
+
+    pos = (hsp.Get(pi1).v * t) > 0;
+    if (!pos) t *= -1;
+
+  
+    edgepoints.Append (p);
+    curvelength.Append (0);
+    len = 0;
+
+    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);
+	if (lh < loch)
+	  loch = lh;
+      }
+
+    steplen = 0.1 * loch;
+  
+    do
+      {
+	if (multithread.terminate)
+	  return;
+      
+	if (fabs (p(0)) + fabs (p(1)) + fabs (p(2)) > 10000)
+	  {
+	    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;
+      
+#ifdef MYGRAPH
+	if (silentflag <= 2)
+	  {
+	    MyLine3D (p, np, rot);
+	    MyDraw ();
+	  }
+#endif      
+
+	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));
+
+	for (i = 1; i <= hsp.Size(); i++)
+	  // if ( i != pi1 && hsp.Get(i).HasSurfaces (s1, s2) )
+	  {
+	    if (!boxp.IsIn (hsp.Get(i).p))
+	      continue;
+	  
+	    Vec<3> hv = hsp.Get(i).p - p;
+	    if (hv.Length2() > 9 * cursteplen * cursteplen)
+	      continue;
+
+	    /*
+	    if (!hsp.Get(i).HasSurfaces (s1, s2))
+	      continue;                  // test for dalibor-problem
+	    */
+
+	    double hvt = hv * t;
+	    hv -= hvt * t;
+	  
+	    if (hv.Length() < 0.2 * cursteplen &&
+		hvt > 0 && 
+		//		  hvt < 1.5 * cursteplen &&
+		hvt < hvtmin && 
+		hsp.Get(i).unconditional == 1 &&
+		(hsp.Get(i).v + t).Length() < 0.4  ) 
+	      {
+		Point<3> hep = hsp.Get(i).p;
+		ProjectToEdge (geometry.GetSurface(s1), 
+			       geometry.GetSurface(s2), hep);            
+	      
+	      
+		if (Dist2 (hep, 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 ( (hsp.Get(i).v + ept).Length() < 1e-4 )
+		      {
+			np = hsp.Get(i).p;
+			ep = i;
+			hvtmin = hvt;
+			//			  break;
+		      }
+		  }
+	      }
+	  }
+
+	loch = min2 (geometry.GetSurface(s1) -> LocH (np, 3, 1, h), 
+		     geometry.GetSurface(s2) -> LocH (np, 3, 1, h));
+
+	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 pos, int layer,
+	       const ARRAY<Point<3> > & edgepoints,
+	       ARRAY<Segment> & refedges,
+	       ARRAY<int> & refedgesinv)
+  {
+    int i, j, k, l;
+    int hi;
+    Point<3> hp;
+    Vec<3> t, a1, a2, m, n;
+    Segment seg;
+    Solid * locsol;
+    ARRAY<int> locsurfind;
+
+    /*
+      int pi1 = 0, pi2 = 0;
+      extern Mesh * mesh;
+      for (i = 1; i <= mesh->GetNP(); i++)
+      {
+      if (Dist2 (edgepoints.Get(1), mesh->Point(i)) < 1e-12)
+      pi1 = i;
+      if (Dist2 (edgepoints.Last(), mesh->Point(i)) < 1e-12)
+      pi2 = i;
+      }
+      (*testout) << "Analyze edge: " << pi1 << " - " << pi2 << ", pts = " << edgepoints.Size() << endl;
+      (*testout) << "p1 = " << edgepoints.Get(1) << " pl = " << edgepoints.Last() << endl;
+    */
+    int debug = 0;
+    /*
+      Dist2 (Point<3> (2.69642, 1.1866, 2.03), edgepoints.Get(1)) < 1e-6 ||
+      Dist2 (Point<3> (2.69642, 1.1866, 2.03), edgepoints.Last()) < 1e-6;
+    */
+
+    if (debug)
+      {
+	//      (*testout) << "tubious edge !!!" << endl;
+	(*testout) << "s1, s2 = " << s1 << " - " << s2 << endl;
+      }
+
+    refedges.SetSize(0);
+    refedgesinv.SetSize(0);
+    hp = Center (edgepoints.Get(1), edgepoints.Get(2));
+    ProjectToEdge (geometry.GetSurface(s1), geometry.GetSurface(s2), hp);
+
+    geometry.GetSurface(s1) -> CalcGradient (hp, a1);
+    geometry.GetSurface(s2) -> CalcGradient (hp, a2);
+    t = Cross (a1, a2);
+    t.Normalize();
+    if (!pos) t *= -1;    
+  
+    (*testout) << "t = " << t << endl;
+
+    for (i = 0; i < geometry.GetNTopLevelObjects(); i++)
+      {
+	(*testout) << "layer = " << layer 
+		   << ", tlo-layer = " << geometry.GetTopLevelObject(i)->GetLayer() << endl;
+	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);
+	if (!locsol) continue;
+
+	BoxSphere<3> boxp (hp, hp);
+	boxp.Increase (1e-5);
+	boxp.CalcDiamCenter();
+      
+	ReducePrimitiveIterator rpi(boxp);
+	UnReducePrimitiveIterator urpi;
+      
+	((Solid*)locsol) -> IterateSolid (rpi);
+
+	locsol -> CalcSurfaceInverse ();
+      
+
+	if (!surf)
+	  {
+	    locsol -> GetSurfaceIndices (locsurfind);
+	  }
+	else
+	  {
+	    /*
+	      if (fabs (surf->CalcFunctionValue (hp)) < 1e-6)
+	      continue;
+	    */
+	    locsurfind.SetSize(1);
+	    locsurfind[0] = -1;
+	    for (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 (j = locsurfind.Size()-1; j >= 0; j--)
+	  if (fabs (geometry.GetSurface(locsurfind[j])
+		    ->CalcFunctionValue (hp) ) > 1e-6)
+	    locsurfind.DeleteElement(j+1);
+      
+	if (debug)
+	  (*testout) << locsurfind.Size() << " faces on hp" << endl;
+
+	for (j = 0; j < locsurfind.Size(); j++)
+	  {      
+	    int lsi = locsurfind[j];
+	    int rlsi = geometry.GetSurfaceClassRepresentant(lsi);
+	  
+	    Vec<3> rn;
+
+	    // n is outer normal to solid
+	    geometry.GetSurface(lsi) -> GetNormalVector (hp, n);
+	    if (geometry.GetSurface (lsi)->Inverse())
+	      n *= -1;
+	  
+	    if (fabs (t * n) > 1e-4) continue;
+	    if (debug)
+	      {
+		(*testout) << "face " << locsurfind.Get(j) << ", rep = " << rlsi 
+			   << " has (t*n) = " << (t*n) << endl;
+		(*testout) << "n = " << n << endl;
+	      }
+	  
+	    // rn is normal to class representant
+	    geometry.GetSurface(rlsi) -> GetNormalVector (hp, rn);
+	  
+	    int sameasref = ((n * rn) > 0);
+	  
+	    m = Cross (t, rn);
+	    m.Normalize();
+	  
+
+	    for (k = 1; k <= 2; k ++)
+	      {
+		bool edgeinv = (k == 2);
+	      
+		if (debug)
+		  {
+		    (*testout) << "onface(" << hp << ", " << m << ")= " 
+			       << locsol->OnFace (hp, m);
+		    (*testout) << " vec2in = "
+			       << locsol -> VectorIn2 (hp, m, n) << " and " 
+			       << locsol -> VectorIn2 (hp, m, -1 * n) << endl;
+		  }
+
+		//	      if (locsol -> OnFace (hp, m))
+		if (locsol -> VectorIn2 (hp, m, n) == 0 &&
+		    locsol -> VectorIn2 (hp, m, -1 * n) == 1)
+		  {
+		    hi = 0;
+		    for (l = 1; l <= refedges.Size(); l++)
+		      {
+			if (refedges.Get(l).si == rlsi &&
+			    refedgesinv.Get(l) == edgeinv)
+			  hi = l;
+		      }
+		  
+		    if (!hi)
+		      {
+			seg.si = rlsi;
+			seg.domin = -1;
+			seg.domout = -1;
+			seg.tlosurf = -1;
+			seg.surfnr1 = s1;
+			seg.surfnr2 = s2;
+			hi = refedges.Append (seg);
+			refedgesinv.Append (edgeinv);
+		      }
+		  
+		    if (!surf)
+		      {
+			if (sameasref)
+			  refedges.Elem(hi).domin = i;
+			else 
+			  refedges.Elem(hi).domout = i;
+		      }
+		    else
+		      refedges.Elem(hi).tlosurf = i;
+
+		    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
+				 << endl;
+		  }
+		m *= -1;
+	      } 
+	  }
+	delete locsol;          
+      }
+  }
+
+
+
+  void EdgeCalculation :: 
+  StoreEdge (const ARRAY<Segment> & refedges,
+	     const ARRAY<int> & 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);
+
+    len = curvelength.Last();
+    ne = int (len + 0.5);
+    if (ne == 0) ne = 1;
+    if (Dist2 (edgepoints.Get(1), edgepoints.Last()) < 1e-8 && 
+	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;
+	}
+
+    if (lastpi == -1)
+      lastpi = mesh.AddPoint (p, layer);
+
+  
+    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;
+      
+	if (thispi == -1)
+	  {
+	    ProjectToEdge (surf1, surf2, np);
+	    thispi = mesh.AddPoint (np, layer);
+	  }
+
+	for (k = 1; k <= refedges.Size(); k++)
+	  {
+	    if (refedgesinv.Get(k))
+	      {
+		seg.p1 = lastpi;
+		seg.p2 = thispi;
+	      }
+	    else
+	      {
+		seg.p1 = thispi;
+		seg.p2 = 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 " << seg.p1 << "-" << seg.p2 << 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<int> & 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;
+    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)
+	{
+	  pi1 = pi;
+	  break;
+	}
+
+    if (pi1 == -1) pi1 = mesh.AddPoint (p, layer);
+
+    p = edgepoints.Last();
+    PointIndex pi2 = -1;
+    for (pi = PointIndex::BASE; 
+	 pi < mesh.GetNP()+PointIndex::BASE; pi++)
+
+      if (Dist (mesh[pi], p) < 1e-6)
+	{
+	  pi2 = pi;
+	  break;
+	}
+    if (pi2==-1) pi2 = mesh.AddPoint (p, layer);
+
+    /*
+  
+    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);
+    }
+    */
+  
+    for (k = 1; k <= refedges.Size(); k++)
+      {
+	if (refedgesinv.Get(k))
+	  {
+	    seg.p1 = pi1;
+	    seg.p2 = pi2;
+	  }
+	else
+	  {
+	    seg.p1 = pi2;
+	    seg.p2 = 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.p1 << "-" << seg.p2 << endl;
+      }
+  }
+  
+
+
+
+
+
+
+  void EdgeCalculation :: 
+  CopyEdge (const ARRAY<Segment> & refedges,
+	    const ARRAY<int> & 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 i, j, k;
+    PointIndex pi;
+
+    // copy start and end points
+    for (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)
+	      frompi = pi;
+	    if (Dist2 (mesh[pi], top) <= 1e-16)
+	      topi = pi;
+	  }
+
+	if (topi == -1)
+	  topi = mesh.AddPoint (top, layer);
+
+	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);
+	  }
+	/*
+	  (*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 (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.p1;
+	int pi2 = oldseg.p2;
+
+	int npi1 = geometry.identifications.Get(copyedgeidentification)
+	  -> GetIdentifiedPoint (mesh, pi1);
+	int npi2 = geometry.identifications.Get(copyedgeidentification)
+	  -> GetIdentifiedPoint (mesh, pi2);
+
+	Segment seg;
+
+	for (k = 1; k <= refedges.Size(); k++)
+	  {
+	    int inv = refedgesinv.Get(k);
+
+	    // other edge is inverse
+	    if (oldseg.seginfo == 1)
+	      inv = !inv;
+
+	    //	  (*testout) << "inv, now = " << inv << endl;
+
+	    if (inv)
+	      {
+		seg.p1 = npi1;
+		seg.p2 = npi2;
+	      }
+	    else
+	      {
+		seg.p1 = npi2;
+		seg.p2 = 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.p1 << "-" << seg.p2 << endl;
+#ifdef DEVELOP
+
+	    (*testout) << "copy seg, face = " << seg.si << ": " 
+		       << " inv = " << inv << ", refinv = " << refedgesinv.Get(k)
+		       << mesh.Point(seg.p1) << ", " << mesh.Point(seg.p2) << 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;
+
+    BitArray pointatsurface (nsurf);
+    Point<3> p1, p2;
+    Vec<3> nv, tv;
+    Solid * tansol;
+    ARRAY<int> tansurfind;
+    //  const Solid * sol;
+
+    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();
+	    s -> GetNormalVector (p1, nv);
+		    
+	    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);
+		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);
+		mesh.AddPoint (p2, layer);
+		seg1.p1 = mesh.GetNP()-1;
+		seg1.p2 = mesh.GetNP();
+		seg2.p2 = mesh.GetNP()-1;
+		seg2.p1 = 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/explicitcurve2d.cpp b/contrib/Netgen/libsrc/csg/explicitcurve2d.cpp
new file mode 100644
index 0000000000..b1eef537c8
--- /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;
+  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 0000000000..af405aed3c
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/explicitcurve2d.hpp
@@ -0,0 +1,109 @@
+#ifndef FILE_EXPLICITCURVE2D
+#define FILE_EXPLICITCURVE2D
+
+/**************************************************************************/
+/* File:   explicitcurve2d.hh                                             */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   14. Oct. 96                                                    */
+/**************************************************************************/
+
+/*
+
+  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 0000000000..acf9b863bc
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/extrusion.cpp
@@ -0,0 +1,175 @@
+#include <mystdlib.h>
+
+#include <linalg.hpp>
+#include <csg.hpp>
+
+namespace netgen
+{
+
+
+
+  ExtrusionSurface :: ExtrusionSurface (const Point<3> & ap0,
+					const Vec<3> & aex, 
+					const Vec<3> & aey,
+					BSplineCurve2d * acurve,
+					int asegnr)
+    : p0(ap0), ex(aex), ey(aey), curve(acurve), segnr(asegnr)
+  {
+    ;
+  }
+  
+  ExtrusionSurface :: ~ExtrusionSurface ()
+  {
+    ;
+  }
+
+  void ExtrusionSurface :: DefineTangentialPlane (const Point<3> & ap1, 
+						  const Point<3> & ap2)
+  {
+    ;
+  }
+
+  void ExtrusionSurface :: ToPlane (const Point<3> & p3d, Point<2> & pplane, 
+				    double h, int & zone) const
+  {
+    ;
+  }
+  
+  void ExtrusionSurface :: FromPlane (const Point<2> & pplane, 
+				      Point<3> & p3d, double h) const
+  {
+    ;
+  }
+  
+
+  void ExtrusionSurface :: Project (Point<3> & p) const
+  {
+    ;
+  }
+
+
+  double ExtrusionSurface :: CalcFunctionValue (const Point<3> & point) const
+  {
+    return 0;
+  }
+
+  void ExtrusionSurface :: CalcGradient (const Point<3> & point, Vec<3> & grad) const
+  {
+    ;
+  }
+
+  Point<3> ExtrusionSurface :: GetSurfacePoint () const
+  {
+    return Point<3> (0,0,0);
+  }
+
+  double ExtrusionSurface :: HesseNorm () const
+  {
+    return 1;
+  }
+
+  void ExtrusionSurface :: Print (ostream & str) const
+  {
+    ;
+  }
+
+  void ExtrusionSurface :: GetTriangleApproximation (TriangleApproximation & tas, 
+						     const Box<3> & boundingbox, 
+						     double facets) const
+  {
+    Point<2> p2d;
+    Point<3> p;
+    int n = int(facets)+1;
+    Vec<3> ez = Cross (ex, ey);
+    cout << "ex = " << ex << endl;
+    cout << "ey = " << ey << endl;
+    for (double t = 0; t < 1.0001; t += 1.0 / n)
+      {
+	cout << "t = " << t << endl;
+	p2d = curve -> Eval (segnr+t);
+	p = p0 + p2d(0) * ex + p2d(1) * ey;
+	cout << "p2d = " << p2d << endl;
+	cout << "add point " << p << endl;
+	tas.AddPoint (p);
+	tas.AddPoint (p + ez);
+      }
+
+    for (int i = 0; i < n; i++)
+      {
+	cout << "add trig " << endl;
+	tas.AddTriangle (TATriangle (0, 2*i, 2*i+2, 2*i+1));
+	tas.AddTriangle (TATriangle (0, 2*i+2, 2*i+3, 2*i+1));
+      }
+  }
+  
+
+
+
+Extrusion :: Extrusion (const Point<3> & ap0,
+			const Vec<3> & aex, 
+			const Vec<3> & aey,
+			const ARRAY< Point<2> > & points)
+  : p0(ap0), ex(aex), ey(aey)
+{
+  int i;
+  
+  ex.Normalize();
+  ey -= (ex*ey) * ex;
+  ey.Normalize();
+
+  for (i = 0; i < points.Size(); i++)
+    curve.AddPoint (points[i]);
+
+  surfs.SetSize (points.Size()/2);
+  for (i = 0; i < surfs.Size(); i++)
+    surfs = new ExtrusionSurface (p0, ex, ey, &curve, i);
+}
+
+Extrusion :: ~Extrusion ()
+{
+  int i;
+  for (i = 0; i < surfs.Size(); i++)
+    delete surfs[i];
+}
+ 
+
+INSOLID_TYPE Extrusion :: BoxInSolid (const BoxSphere<3> & box) const
+{
+  Vec<3> p0c = box.Center() - p0;
+  Point<2> p2d (ex*p0c, ey*p0c);
+  double r = box.Diam() / 2;
+  double dist;
+  bool inside =
+    curve.Inside (p2d, dist);
+
+  if (inside && dist > r) return IS_INSIDE;
+  if (!inside && dist > r) return IS_OUTSIDE;
+  return DOES_INTERSECT;
+}
+
+
+INSOLID_TYPE Extrusion :: PointInSolid (const Point<3> & p,
+					double eps) const
+{
+  Vec<3> p0c = p - p0;
+  Point<2> p2d (ex*p0c, ey*p0c);
+  double dist;
+  bool inside =
+    curve.Inside (p2d, dist);
+  
+  if (dist < eps) return DOES_INTERSECT;
+  if (inside) return IS_INSIDE;
+  return IS_OUTSIDE;
+}
+
+
+INSOLID_TYPE Extrusion :: VecInSolid (const Point<3> & p,
+				      const Vec<3> & v,
+				      double eps) const
+{
+  Point<3> p2 = p + (1e-3/(v.Length()+1e-16)) * v;
+  return PointInSolid (p2, eps);
+}
+
+
+}
diff --git a/contrib/Netgen/libsrc/csg/extrusion.hpp b/contrib/Netgen/libsrc/csg/extrusion.hpp
new file mode 100644
index 0000000000..ff5a47b4e1
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/extrusion.hpp
@@ -0,0 +1,89 @@
+#ifndef FILE_EXTRUSION
+#define FILE_EXTRUSION
+
+/**************************************************************************/
+/* File:   extrusion.hpp                                                  */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   17. Mar. 2003                                                  */
+/**************************************************************************/
+
+/*
+
+extrusion of 2D curve
+  
+*/
+
+
+class ExtrusionSurface : public Surface
+{
+protected:
+  BSplineCurve2d * curve;
+  int segnr;
+  Point<3> p0;
+  Vec<3> ex, ey;
+public:
+  ExtrusionSurface (const Point<3> & ap0,
+		    const Vec<3> & aex, 
+		    const Vec<3> & aey,
+		    BSplineCurve2d * acurve,
+		    int asegnr);
+  virtual ~ExtrusionSurface ();
+
+  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 double CalcFunctionValue (const Point<3> & point) const;
+
+  virtual void CalcGradient (const Point<3> & point, Vec<3> & grad) const;
+
+  virtual Point<3> GetSurfacePoint () const;
+
+  virtual double HesseNorm () const;
+
+  virtual void Print (ostream & str) const;  
+  virtual void GetTriangleApproximation (TriangleApproximation & tas, 
+					 const Box<3> & boundingbox, 
+					 double facets) const;
+};
+
+
+class Extrusion : public Primitive
+{
+protected:
+  Point<3> p0;
+  Vec<3> ex, ey;
+  BSplineCurve2d curve;
+  ARRAY<ExtrusionSurface*> surfs;
+
+public:
+  Extrusion (const Point<3> & ap0,
+	     const Vec<3> & aex, 
+	     const Vec<3> & aey,
+	     const ARRAY< Point<2> > & points);
+  virtual ~Extrusion ();
+  
+  
+
+  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 int GetNSurfaces() const { return surfs.Size(); }
+  virtual Surface & GetSurface (int i) { return *surfs[i]; }
+  virtual const Surface & GetSurface (int i) const { return *surfs[i]; }
+};
+
+#endif
diff --git a/contrib/Netgen/libsrc/csg/gencyl.cpp b/contrib/Netgen/libsrc/csg/gencyl.cpp
new file mode 100644
index 0000000000..01c893d4a3
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/gencyl.cpp
@@ -0,0 +1,209 @@
+#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(1, 1) = (1 - curvpp(0) * curvpp(0) ) / dist;  
+  h2d(1, 2) = h2d(2, 1) = (- curvpp(0) * curvpp(1) ) / dist;  
+  h2d(2, 2) = (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);
+}
+  
+#ifdef MYGRAPH  
+void GeneralizedCylinder :: Plot (const class ROT3D & rot) const
+{
+  Point<2> p2d;
+  Point<3> p, oldp;
+  double t, tmin, tmax, dt;
+  
+  tmin = crosssection.MinParam();
+  tmax = crosssection.MaxParam();
+  dt = (tmax - tmin)/ 500;
+  
+  p2d = crosssection.Eval(tmin);
+  p = planep + p2d(0) * planee1 + p2d(1) * planee2;
+  
+  for (t = tmin; t <= tmax+dt; t += dt)
+    {
+      if (crosssection.SectionUsed (t))
+	MySetColor (RED);
+      else
+	MySetColor (BLUE);
+      
+      oldp = p;
+      p2d = crosssection.Eval(t);
+      p = planep + p2d(0) * planee1 + p2d(1) * planee2;
+      MyLine3D (p, oldp, rot);
+    }
+
+}
+
+#endif  
+}
diff --git a/contrib/Netgen/libsrc/csg/gencyl.hpp b/contrib/Netgen/libsrc/csg/gencyl.hpp
new file mode 100644
index 0000000000..424c867a92
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/gencyl.hpp
@@ -0,0 +1,64 @@
+#ifndef FILE_GENCYL
+#define FILE_GENCYL
+
+/**************************************************************************/
+/* File:   gencyl.hh                                                      */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   14. Oct. 96                                                    */
+/**************************************************************************/
+
+/*
+  
+  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 0000000000..c052624a42
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/genmesh.cpp
@@ -0,0 +1,684 @@
+#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");
+
+    char * savetask = multithread.task;
+    multithread.task = "Find points";
+
+    for (int i = 0; i < geom.GetNUserPoints(); i++)
+      {
+	mesh.AddPoint (geom.GetUserPoint (i));
+	mesh.AddLockedPoint (PointIndex (i+1));
+      }
+
+    SpecialPointCalculation spc;
+
+    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)
+  {
+    EdgeCalculation ec (geom, specpoints);
+    ec.Calc (mparam.maxh, mesh);
+
+    for (int i = 0; i < geom.singedges.Size(); i++)
+      geom.singedges[i]->FindPointsOnEdge (mesh);
+    for (int i = 0; i < geom.singpoints.Size(); i++)
+      geom.singpoints[i]->FindPoints (mesh);
+
+    for (int i = 1; i <= mesh.GetNSeg(); i++)
+      {
+	int ok = 0;
+	for (int k = 1; k <= mesh.GetNFD(); k++)
+	  if (mesh.GetFaceDescriptor(k).SegmentFits (mesh.LineSegment(i)))
+	    ok = k;
+
+	if (!ok)
+	  ok = mesh.AddFaceDescriptor (FaceDescriptor (mesh.LineSegment(i)));
+
+	mesh.LineSegment(i).si = ok;
+      }
+
+    for (int i = 0; i < geom.identifications.Size(); i++)
+      geom.identifications[i]->IdentifyPoints (mesh);
+    for (int i = 0; i < geom.identifications.Size(); i++)
+      geom.identifications[i]->IdentifyFaces (mesh);
+
+
+
+    // find intersecting segments
+    PrintMessage (3, "Check intersecting edges");
+
+    if (!ec.point_on_edge_problem)
+      for (SegmentIndex si = 0; si < mesh.GetNSeg(); si++)
+	for (SegmentIndex sj = 0; sj < si; sj++)
+	  {
+	    if (!mesh[si].seginfo || !mesh[sj].seginfo) continue;
+	    if (mesh[mesh[si].p1].GetLayer() != mesh[mesh[sj].p2].GetLayer()) continue;
+	    
+	    Point<3> pi1 = mesh[mesh[si].p1];
+	    Point<3> pi2 = mesh[mesh[si].p2];
+	    Point<3> pj1 = mesh[mesh[sj].p1];
+	    Point<3> pj2 = mesh[mesh[sj].p2];
+	    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 j = 0; j < 3; j++)
+	      { 
+		mat(j,0) = vi(j); 
+		mat(j,1) = -vj(j); 
+		rhs(j) = pj1(j)-pi1(j); 
+	    }
+	    
+	    mat.Solve (rhs, sol);
+
+	    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;
+		cout << "Intersection at " << ip << endl;
+		
+		geom.AddUserPoint (ip);
+		spoints.Append (MeshPoint (ip, mesh[mesh[si].p1].GetLayer()));
+		mesh.AddPoint (ip);
+	      }
+	  }
+  }  
+
+
+
+
+
+
+  static void MeshSurface (CSGeometry & geom, Mesh & mesh)
+  {
+    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++)
+      {
+	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);
+	  }      
+
+	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))
+	      {
+		fd.SetBCProperty (geom.bcmodifications[l].bcnr);
+	      }
+	  }
+      }
+
+
+    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.domin_singular = 1;
+		  if (geom.singfaces[j]->GetDomainNr() == fd.DomainOut())
+		    fd.domout_singular = 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, geom.BoundingBox());
+	meshing.SetStartTime (starttime);
+
+	for (PointIndex pi = PointIndex::BASE; pi < noldp+PointIndex::BASE; pi++)
+	  meshing.AddPoint (mesh[pi], pi);
+  
+	segments.SetSize (0);
+
+	for (SegmentIndex si = 0; si < mesh.GetNSeg(); si++)
+	  if (mesh[si].si == k)
+	    segments.Append (mesh[si]);
+
+	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].p1 + 1 - PointIndex::BASE, 
+					segments[si].p2 + 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, maxh, k);
+
+	if (res != MESHING2_OK)
+	  {
+	    PrintError ("Problem in Surface mesh generation");
+	    throw NgException ("Problem in Surface mesh generation");
+	  }
+
+	if (multithread.terminate) return;
+      
+	for (int i = oldnf+1; i <= mesh.GetNSE(); i++)
+	  mesh.SurfaceElement(i).SetIndex (k);
+
+
+	//      mesh.CalcSurfacesOfNode();
+	if (segments.Size())   
+	  { 
+	    // surface was meshed, not copied
+	    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 (0.2);
+		  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 (0.2);
+		  meshopt.SetWriteStatus (0);
+
+		  meshopt.ImproveMesh (mesh);
+		}
+
+		{
+		  MeshOptimize2dSurfaces meshopt(geom);
+		  meshopt.SetFaceIndex (k);
+		  meshopt.SetImproveEdges (0);
+		  meshopt.SetMetricWeight (0.2);
+		  meshopt.SetWriteStatus (0);
+
+		  meshopt.CombineImprove (mesh);
+		  //		mesh.CalcSurfacesOfNode();
+		}
+
+		if (multithread.terminate) return;
+		{
+		  MeshOptimize2dSurfaces meshopt(geom);
+		  meshopt.SetFaceIndex (k);
+		  meshopt.SetImproveEdges (0);
+		  meshopt.SetMetricWeight (0.2);
+		  meshopt.SetWriteStatus (0);
+
+		  meshopt.ImproveMesh (mesh);
+		}
+	      }
+	  }
+
+
+	PrintMessage (3, (mesh.GetNSE() - oldnf), " elements, ", mesh.GetNP(), " points");
+
+#ifdef OPENGL
+	extern void Render();
+	Render();
+#endif
+      }
+    
+    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 (int i = oldnf+1; i <= mesh.GetNSE(); i++)
+	      mesh.SurfaceElement(i).SetIndex (k);
+
+
+	    if (!segments.Size())
+	      {
+		masterface.Elem(k) = k;
+		changed = 1; 
+	      }
+
+	    PrintMessage (3, (mesh.GetNSE() - oldnf), " elements, ", mesh.GetNP(), " points");
+	  }
+      
+#ifdef OPENGL
+	extern void Render();
+	Render();
+#endif
+      }
+    while (changed);
+
+    mesh.SplitSeparatedFaces();
+    mesh.CalcSurfacesOfNode();
+
+    multithread.task = savetask;
+  }
+
+
+
+
+
+
+
+  int GenerateMesh (CSGeometry & geom,
+		    Mesh *& mesh,
+		    int perfstepsstart, int perfstepsend,
+		    const char * optstr)
+  {
+
+    if (mesh && mesh->GetNSE() &&
+	!geom.GetNSolids())
+      {
+	if (perfstepsstart < MESHCONST_MESHVOLUME)
+	  perfstepsstart = MESHCONST_MESHVOLUME;
+      }
+
+
+
+    if (perfstepsstart <= MESHCONST_ANALYSE)
+      {
+	delete mesh;
+	mesh = new Mesh();
+
+	mesh->SetGlobalH (mparam.maxh);
+
+	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);
+	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();
+	    mesh->DeleteMesh();
+	  
+	    FindPoints (geom, *mesh);
+	    if (multithread.terminate) return TCL_OK;
+	    FindEdges (geom, *mesh);
+	    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();      
+	    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/geometry.cpp b/contrib/Netgen/libsrc/csg/geometry.cpp
new file mode 100644
index 0000000000..14806ee9cb
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/geometry.cpp
@@ -0,0 +1,1792 @@
+/* A Bison parser, made from geometry.yy
+   by GNU bison 1.35.  */
+
+#define YYBISON 1  /* Identify Bison output.  */
+
+# define	NUM	257
+# define	TOK_SOLID	258
+# define	TOK_RECO	259
+# define	TOK_TLO	260
+# define	TOK_BOUNDINGBOX	261
+# define	IDENT	262
+# define	IDENTSOLID	263
+# define	TOK_SPHERE	264
+# define	TOK_CYLINDER	265
+# define	TOK_CONE	266
+# define	TOK_PLAIN	267
+# define	TOK_TUBE	268
+# define	TOK_GENCYL	269
+# define	TOK_ORTHOBRICK	270
+# define	TOK_POLYHEDRON	271
+# define	TOK_REVOLUTION	272
+# define	TOK_OR	273
+# define	TOK_AND	274
+# define	TOK_NOT	275
+# define	TOK_TRANSLATE	276
+# define	TOK_MULTITRANSLATE	277
+# define	TOK_ROTATE	278
+# define	TOK_MULTIROTATE	279
+# define	TOK_SINGULAR	280
+# define	TOK_EDGE	281
+# define	TOK_POINT	282
+# define	TOK_IDENTIFY	283
+# define	TOK_CLOSESURFACES	284
+# define	TOK_CLOSEEDGES	285
+# define	TOK_PERIODIC	286
+# define	TOK_BOUNDARYCONDITION	287
+
+#line 1 "geometry.yy"
+
+//define YYDEBUG 1
+
+extern int yylex ();
+
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+
+#include <linalg.hpp>
+#include <csg.hpp>
+
+namespace netgen
+{
+netgen::CSGeometry * parsegeom;
+}
+
+using namespace netgen;
+
+// extern ARRAY<Surface*> surfaces;
+// extern SYMBOLTABLE<Solid*> solids;
+
+
+int yyerror (char * s);
+splinetube * tube;
+spline3d * middlecurve;
+Point<3> splinep1;
+BSplineCurve2d *bspline;
+Flags parseflags;
+extern int linenum;
+ARRAY<double> doublearray;
+ARRAY<char*> stringarray;
+
+Polyhedra * polyhedron;
+// Revolution * revolution;
+
+#line 38 "geometry.yy"
+#ifndef YYSTYPE
+typedef union {
+double val;
+char * chptr;
+Solid * solidtype;
+} yystype;
+# define YYSTYPE yystype
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+
+
+#define	YYFINAL		260
+#define	YYFLAG		-32768
+#define	YYNTBASE	42
+
+/* YYTRANSLATE(YYLEX) -- Bison token number corresponding to YYLEX. */
+#define YYTRANSLATE(x) ((unsigned)(x) <= 287 ? yytranslate[x] : 67)
+
+/* YYTRANSLATE[YYLEX] -- Bison token number corresponding to YYLEX. */
+static const char yytranslate[] =
+{
+       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+      36,    37,     2,     2,    38,    39,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,    34,
+       2,    35,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,    40,     2,    41,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     1,     3,     4,     5,
+       6,     7,     8,     9,    10,    11,    12,    13,    14,    15,
+      16,    17,    18,    19,    20,    21,    22,    23,    24,    25,
+      26,    27,    28,    29,    30,    31,    32,    33
+};
+
+#if YYDEBUG
+static const short yyprhs[] =
+{
+       0,     0,     1,     6,     7,    11,    12,    19,    21,    23,
+      27,    31,    34,    38,    49,    66,    85,   100,   115,   116,
+     125,   136,   149,   166,   185,   189,   191,   195,   197,   203,
+     209,   217,   221,   223,   227,   229,   233,   245,   246,   252,
+     253,   254,   261,   262,   266,   272,   279,   285,   291,   296,
+     300,   305,   320,   329,   334,   335,   338,   339,   342,   345,
+     350,   355,   360,   365,   366,   371,   373,   377,   378,   383,
+     385,   389,   391
+};
+static const short yyrhs[] =
+{
+      -1,    43,     5,    44,    54,     0,     0,    44,    45,    34,
+       0,     0,     4,     8,    35,    47,    46,    56,     0,    48,
+       0,     9,     0,    47,    19,    47,     0,    47,    20,    47,
+       0,    21,    47,     0,    36,    47,    37,     0,    10,    36,
+       3,    38,     3,    38,     3,    34,     3,    37,     0,    11,
+      36,     3,    38,     3,    38,     3,    34,     3,    38,     3,
+      38,     3,    34,     3,    37,     0,    12,    36,     3,    38,
+       3,    38,     3,    34,     3,    34,     3,    38,     3,    38,
+       3,    34,     3,    37,     0,    13,    36,     3,    38,     3,
+      38,     3,    34,     3,    38,     3,    38,     3,    37,     0,
+      16,    36,     3,    38,     3,    38,     3,    34,     3,    38,
+       3,    38,     3,    37,     0,     0,    17,    36,    49,    50,
+      34,    34,    51,    37,     0,    22,    36,     3,    38,     3,
+      38,     3,    34,    47,    37,     0,    23,    36,     3,    38,
+       3,    38,     3,    34,     3,    34,    47,    37,     0,    24,
+      36,     3,    38,     3,    38,     3,    34,     3,    38,     3,
+      38,     3,    34,    47,    37,     0,    25,    36,     3,    38,
+       3,    38,     3,    34,     3,    38,     3,    38,     3,    34,
+       3,    34,    47,    37,     0,    50,    34,    52,     0,    52,
+       0,    51,    34,    53,     0,    53,     0,     3,    38,     3,
+      38,     3,     0,     3,    38,     3,    38,     3,     0,     3,
+      38,     3,    38,     3,    38,     3,     0,    67,    34,    68,
+       0,    68,     0,     3,    38,     3,     0,    70,     0,    70,
+      38,    69,     0,     3,    38,     3,    38,     3,    38,     3,
+      38,     3,    38,     3,     0,     0,     3,    38,     3,    72,
+      73,     0,     0,     0,    38,     3,    38,     3,    74,    73,
+       0,     0,    54,    55,    34,     0,    26,    27,     3,     9,
+       9,     0,    26,    28,     3,     9,     9,     9,     0,    29,
+      30,     9,     9,    56,     0,    29,    31,     9,     9,     9,
+       0,    29,    32,     9,     9,     0,     6,     9,    56,     0,
+       6,     9,     9,    56,     0,     7,    36,     3,    38,     3,
+      38,     3,    34,     3,    38,     3,    38,     3,    37,     0,
+      28,    36,     3,    38,     3,    38,     3,    37,     0,    33,
+       9,     9,     3,     0,     0,    57,    58,     0,     0,    59,
+      58,     0,    39,    66,     0,    39,    66,    35,    66,     0,
+      39,    66,    35,     3,     0,    39,    66,    35,    60,     0,
+      39,    66,    35,    63,     0,     0,    40,    61,    62,    41,
+       0,     3,     0,    62,    38,     3,     0,     0,    40,    64,
+      65,    41,     0,    66,     0,    65,    38,    66,     0,     8,
+       0,     9,     0
+};
+
+#endif
+
+#if YYDEBUG
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const short yyrline[] =
+{
+       0,    61,    61,    79,    81,    84,    84,    94,    96,    97,
+      98,    99,   100,   103,   112,   123,   135,   144,   158,   158,
+     197,   206,   222,   231,   285,   287,   289,   291,   293,   300,
+     306,   315,   317,   319,   331,   333,   335,   345,   345,   354,
+     356,   356,   372,   374,   377,   385,   394,   409,   425,   439,
+     453,   470,   477,   482,   511,   511,   516,   518,   521,   524,
+     526,   528,   530,   535,   535,   540,   542,   545,   545,   556,
+     563,   574,   576
+};
+#endif
+
+
+#if (YYDEBUG) || defined YYERROR_VERBOSE
+
+/* YYTNAME[TOKEN_NUM] -- String name of the token TOKEN_NUM. */
+static const char *const yytname[] =
+{
+  "$", "error", "$undefined.", "NUM", "TOK_SOLID", "TOK_RECO", "TOK_TLO", 
+  "TOK_BOUNDINGBOX", "IDENT", "IDENTSOLID", "TOK_SPHERE", "TOK_CYLINDER", 
+  "TOK_CONE", "TOK_PLAIN", "TOK_TUBE", "TOK_GENCYL", "TOK_ORTHOBRICK", 
+  "TOK_POLYHEDRON", "TOK_REVOLUTION", "TOK_OR", "TOK_AND", "TOK_NOT", 
+  "TOK_TRANSLATE", "TOK_MULTITRANSLATE", "TOK_ROTATE", "TOK_MULTIROTATE", 
+  "TOK_SINGULAR", "TOK_EDGE", "TOK_POINT", "TOK_IDENTIFY", 
+  "TOK_CLOSESURFACES", "TOK_CLOSEEDGES", "TOK_PERIODIC", 
+  "TOK_BOUNDARYCONDITION", "';'", "'='", "'('", "')'", "','", "'-'", 
+  "'['", "']'", "input", "@1", "recsoliddef", "soliddef", "@2", "solid", 
+  "solidprimitive", "@3", "polyhedronpoints", "polyhedronfaces", 
+  "polyhedronpoint", "polyhedronface", "recadddef", "adddef", "flaglist", 
+  "@6", "recflaglist", "flag", "numlistbrack", "@7", "numlist", 
+  "stringlistbrack", "@8", "stringlist", "anyident", 0
+};
+#endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const short yyr1[] =
+{
+       0,    43,    42,    44,    44,    46,    45,    47,    47,    47,
+      47,    47,    47,    48,    48,    48,    48,    48,    49,    48,
+      48,    48,    48,    48,    50,    50,    51,    51,    52,    53,
+      53,    67,    67,    68,    69,    69,    70,    72,    71,    73,
+      74,    73,    54,    54,    55,    55,    55,    55,    55,    55,
+      55,    55,    55,    55,    57,    56,    58,    58,    59,    59,
+      59,    59,    59,    61,    60,    62,    62,    64,    63,    65,
+      65,    66,    66
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const short yyr2[] =
+{
+       0,     0,     4,     0,     3,     0,     6,     1,     1,     3,
+       3,     2,     3,    10,    16,    18,    14,    14,     0,     8,
+      10,    12,    16,    18,     3,     1,     3,     1,     5,     5,
+       7,     3,     1,     3,     1,     3,    11,     0,     5,     0,
+       0,     6,     0,     3,     5,     6,     5,     5,     4,     3,
+       4,    14,     8,     4,     0,     2,     0,     2,     2,     4,
+       4,     4,     4,     0,     4,     1,     3,     0,     4,     1,
+       3,     1,     1
+};
+
+/* YYDEFACT[S] -- default rule to reduce with in state S when YYTABLE
+   doesn't specify something else to do.  Zero means the default is an
+   error. */
+static const short yydefact[] =
+{
+       1,     0,     3,    42,     0,     0,     2,     0,     4,     0,
+       0,     0,     0,     0,     0,     0,     0,    54,     0,     0,
+       0,     0,     0,     0,     0,     0,    43,     8,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       5,     7,    54,    49,    56,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,    18,    11,
+       0,     0,     0,     0,     0,     0,     0,    54,    50,     0,
+      55,    56,     0,     0,     0,     0,    54,     0,    48,    53,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+      12,     9,    10,     6,    71,    72,    58,    57,     0,    44,
+       0,     0,    46,    47,     0,     0,     0,     0,     0,     0,
+       0,    25,     0,     0,     0,     0,     0,     0,    45,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,    60,    67,    61,    62,    59,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,    24,     0,     0,     0,     0,
+       0,     0,     0,    52,     0,     0,     0,     0,     0,     0,
+       0,     0,    27,     0,     0,     0,     0,    65,     0,     0,
+      69,     0,     0,     0,     0,     0,     0,    28,     0,     0,
+      19,     0,     0,     0,     0,     0,    64,     0,    68,     0,
+       0,     0,     0,     0,     0,     0,    26,     0,     0,     0,
+       0,    66,    70,     0,    13,     0,     0,     0,     0,     0,
+      20,     0,     0,     0,     0,     0,     0,     0,     0,    29,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,    21,
+       0,     0,    51,     0,     0,     0,     0,    30,     0,     0,
+       0,     0,    16,    17,     0,     0,     0,     0,     0,     0,
+      14,     0,    22,     0,     0,     0,    15,    23,     0,     0,
+       0
+};
+
+static const short yydefgoto[] =
+{
+     258,     1,     3,     5,    67,    40,    41,    85,   110,   161,
+     111,   162,     6,    15,    43,    44,    70,    71,   133,   150,
+     168,   134,   151,   169,    96
+};
+
+static const short yypact[] =
+{
+  -32768,     5,-32768,    18,    21,     7,    14,    11,-32768,    52,
+      35,    36,    39,    28,    56,    42,     2,    58,    66,    74,
+      75,    76,    71,    72,    73,    77,-32768,-32768,    48,    49,
+      51,    53,    55,    57,     2,    59,    60,    61,    62,     2,
+      54,-32768,-32768,-32768,    44,    50,    81,    83,    63,    85,
+      90,    91,    99,   100,   101,   102,   103,   104,-32768,-32768,
+     105,   106,   107,   108,    -3,     2,     2,-32768,-32768,    47,
+  -32768,    44,   109,   110,   111,   112,-32768,   113,-32768,-32768,
+      78,    79,    80,    86,    87,   118,    88,    89,    92,    93,
+  -32768,-32768,-32768,-32768,-32768,-32768,    94,-32768,    95,-32768,
+     114,    96,-32768,-32768,   125,   129,   132,   133,   134,   115,
+     116,-32768,   135,   136,   137,   138,    -1,   139,-32768,   140,
+     117,   119,   120,   121,   122,   141,     1,   123,   124,   126,
+     127,-32768,   142,-32768,-32768,-32768,   144,   130,   143,   145,
+     146,   148,   149,   128,   151,-32768,   153,   160,   165,   166,
+     167,    47,   168,-32768,   147,   150,   152,   154,   155,   169,
+     156,   -28,-32768,   157,   158,   159,   161,-32768,    -8,    16,
+  -32768,   162,   170,   171,   172,   173,   176,-32768,   177,   151,
+  -32768,     2,   179,   180,   182,   184,-32768,    47,-32768,   187,
+     164,   174,   163,   175,   178,   183,-32768,    25,   181,   185,
+     186,-32768,-32768,   188,-32768,   193,   195,   196,   199,   200,
+  -32768,     2,   201,   202,   203,   189,   190,   191,   192,   194,
+      29,   197,   198,   204,   205,   206,   208,   211,   214,-32768,
+     215,   217,-32768,   209,   207,   210,   212,-32768,   216,   218,
+     219,   222,-32768,-32768,     2,   228,   220,   221,    31,   224,
+  -32768,   230,-32768,     2,   223,    33,-32768,-32768,   234,   237,
+  -32768
+};
+
+static const short yypgoto[] =
+{
+  -32768,-32768,-32768,-32768,-32768,   -34,-32768,-32768,-32768,-32768,
+     -13,   -65,-32768,-32768,   -39,-32768,   213,-32768,-32768,-32768,
+  -32768,-32768,-32768,-32768,  -115
+};
+
+
+#define	YYLAST		284
+
+
+static const short yytable[] =
+{
+      59,   135,   131,    68,   109,    64,   179,    94,    95,   180,
+       2,    27,    28,    29,    30,    31,    65,    66,    32,    33,
+       9,    10,     4,    34,    35,    36,    37,    38,    93,     7,
+     185,    91,    92,   186,    90,   144,   170,   102,    39,   132,
+      11,     8,    12,    13,    65,    66,    16,    14,    65,    66,
+      65,    66,    65,    66,   187,    94,    95,   188,    22,    23,
+      24,    17,   210,    19,    20,    25,   229,    42,   252,    45,
+     257,    18,   202,    65,    66,    21,    26,    46,    47,    48,
+      49,    50,    51,    69,    53,    54,    52,    55,    72,    56,
+      73,    57,    74,    58,    76,    60,    61,    62,    63,    77,
+      78,    75,    79,    80,    81,    82,    83,    84,    86,    87,
+      88,    89,    98,   145,   196,   101,   104,   105,   106,    99,
+     100,   109,   103,   118,   107,   108,   112,   113,   120,   116,
+     114,   115,   121,   117,   119,   122,   123,   124,   127,   128,
+     129,   130,   136,   137,   143,   -63,   154,   197,   155,   156,
+     126,   157,   158,   125,   160,   138,   163,   139,   140,   141,
+     142,   146,   147,   164,   148,   149,   159,   153,   165,   166,
+     167,   171,   177,   190,   191,   192,   193,   220,   152,   194,
+     195,   172,   198,   199,   173,   200,   174,   201,   175,   176,
+     203,   181,   182,   183,   178,   184,   215,   206,   216,   217,
+     189,   204,   218,   219,   221,   222,   223,     0,   233,   234,
+     248,   235,   205,   207,   236,   211,   208,   237,   238,   255,
+     239,   209,   246,   212,   213,   247,   214,   224,   225,   226,
+     227,   249,   228,   254,   259,   230,   231,   260,     0,     0,
+       0,   232,     0,   240,     0,   241,     0,   242,     0,   243,
+     244,     0,   245,     0,     0,   251,     0,   250,   253,     0,
+     256,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,    97
+};
+
+static const short yycheck[] =
+{
+      34,   116,     3,    42,     3,    39,    34,     8,     9,    37,
+       5,     9,    10,    11,    12,    13,    19,    20,    16,    17,
+       6,     7,     4,    21,    22,    23,    24,    25,    67,     8,
+      38,    65,    66,    41,    37,    34,   151,    76,    36,    40,
+      26,    34,    28,    29,    19,    20,    35,    33,    19,    20,
+      19,    20,    19,    20,    38,     8,     9,    41,    30,    31,
+      32,     9,    37,    27,    28,     9,    37,     9,    37,     3,
+      37,    36,   187,    19,    20,    36,    34,     3,     3,     3,
+       9,     9,     9,    39,    36,    36,     9,    36,    38,    36,
+       9,    36,     9,    36,     9,    36,    36,    36,    36,     9,
+       9,    38,     3,     3,     3,     3,     3,     3,     3,     3,
+       3,     3,     3,   126,   179,     3,    38,    38,    38,     9,
+       9,     3,     9,     9,    38,    38,    38,    38,     3,    35,
+      38,    38,     3,    38,    38,     3,     3,     3,     3,     3,
+       3,     3,     3,     3,     3,     3,     3,   181,     3,     3,
+      34,     3,     3,    38,     3,    38,     3,    38,    38,    38,
+      38,    38,    38,     3,    38,    38,    38,    37,     3,     3,
+       3,     3,     3,     3,     3,     3,     3,   211,    34,     3,
+       3,    34,     3,     3,    34,     3,    34,     3,    34,    34,
+       3,    34,    34,    34,    38,    34,     3,    34,     3,     3,
+      38,    37,     3,     3,     3,     3,     3,    -1,     3,     3,
+     244,     3,    38,    38,     3,    34,    38,     3,     3,   253,
+       3,    38,     3,    38,    38,     3,    38,    38,    38,    38,
+      38,     3,    38,     3,     0,    38,    38,     0,    -1,    -1,
+      -1,    37,    -1,    34,    -1,    38,    -1,    37,    -1,    37,
+      34,    -1,    34,    -1,    -1,    34,    -1,    37,    34,    -1,
+      37,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    71
+};
+/* -*-C-*-  Note some compilers choke on comments on `#line' lines.  */
+#line 3 "/usr/share/bison/bison.simple"
+
+/* Skeleton output parser for bison,
+
+   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002 Free Software
+   Foundation, Inc.
+
+   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, 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.  */
+
+/* As a special exception, when this file is copied by Bison into a
+   Bison output file, you may use that output file without restriction.
+   This special exception was added by the Free Software Foundation
+   in version 1.24 of Bison.  */
+
+/* This is the parser code that is written into each bison parser when
+   the %semantic_parser declaration is not specified in the grammar.
+   It was written by Richard Stallman by simplifying the hairy parser
+   used when %semantic_parser is specified.  */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+   infringing on user name space.  This should be done even for local
+   variables, as they might otherwise be expanded by user macros.
+   There are some unavoidable exceptions within include files to
+   define necessary library symbols; they are noted "INFRINGES ON
+   USER NAME SPACE" below.  */
+
+#ifndef YYPARSE_RETURN_TYPE
+#define YYPARSE_RETURN_TYPE int
+#endif
+
+#if ! defined (yyoverflow) || defined (YYERROR_VERBOSE)
+
+/* The parser invokes alloca or malloc; define the necessary symbols.  */
+
+# if YYSTACK_USE_ALLOCA
+#  define YYSTACK_ALLOC alloca
+# else
+#  ifndef YYSTACK_USE_ALLOCA
+#   if defined (alloca) || defined (_ALLOCA_H)
+#    define YYSTACK_ALLOC alloca
+#   else
+#    ifdef __GNUC__
+#     define YYSTACK_ALLOC __builtin_alloca
+#    endif
+#   endif
+#  endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+   /* Pacify GCC's `empty if-body' warning. */
+#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
+# else
+#  if defined (__STDC__) || defined (__cplusplus)
+#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#   define YYSIZE_T size_t
+#  endif
+#  define YYSTACK_ALLOC malloc
+#  define YYSTACK_FREE free
+# endif
+#endif /* ! defined (yyoverflow) || defined (YYERROR_VERBOSE) */
+
+
+#if (! defined (yyoverflow) \
+     && (! defined (__cplusplus) \
+	 || ((YYLTYPE_IS_TRIVIAL || ! YYLSP_NEEDED) && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member.  */
+union yyalloc
+{
+  short yyss;
+  YYSTYPE yyvs;
+# if YYLSP_NEEDED
+  YYLTYPE yyls;
+# endif
+};
+
+/* The size of the maximum gap between one aligned stack and the next.  */
+# define YYSTACK_GAP_MAX (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+   N elements.  */
+# if YYLSP_NEEDED
+#  define YYSTACK_BYTES(N) \
+     ((N) * (sizeof (short) + sizeof (YYSTYPE) + sizeof (YYLTYPE))	\
+      + 2 * YYSTACK_GAP_MAX)
+# else
+#  define YYSTACK_BYTES(N) \
+     ((N) * (sizeof (short) + sizeof (YYSTYPE))				\
+      + YYSTACK_GAP_MAX)
+# endif
+
+/* Copy COUNT objects from FROM to TO.  The source and destination do
+   not overlap.  */
+# ifndef YYCOPY
+#  if 1 < __GNUC__
+#   define YYCOPY(To, From, Count) \
+      __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+#  else
+#   define YYCOPY(To, From, Count)		\
+      do					\
+	{					\
+	  register YYSIZE_T yyi;		\
+	  for (yyi = 0; yyi < (Count); yyi++)	\
+	    (To)[yyi] = (From)[yyi];		\
+	}					\
+      while (0)
+#  endif
+# endif
+
+/* Relocate STACK from its old location to the new one.  The
+   local variables YYSIZE and YYSTACKSIZE give the old and new number of
+   elements in the stack, and YYPTR gives the new location of the
+   stack.  Advance YYPTR to a properly aligned location for the next
+   stack.  */
+# define YYSTACK_RELOCATE(Stack)					\
+    do									\
+      {									\
+	YYSIZE_T yynewbytes;						\
+	YYCOPY (&yyptr->Stack, Stack, yysize);				\
+	Stack = &yyptr->Stack;						\
+	yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAX;	\
+	yyptr += yynewbytes / sizeof (*yyptr);				\
+      }									\
+    while (0)
+
+#endif
+
+
+#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__)
+# define YYSIZE_T __SIZE_TYPE__
+#endif
+#if ! defined (YYSIZE_T) && defined (size_t)
+# define YYSIZE_T size_t
+#endif
+#if ! defined (YYSIZE_T)
+# if defined (__STDC__) || defined (__cplusplus)
+#  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYSIZE_T size_t
+# endif
+#endif
+#if ! defined (YYSIZE_T)
+# define YYSIZE_T unsigned int
+#endif
+
+#define yyerrok		(yyerrstatus = 0)
+#define yyclearin	(yychar = YYEMPTY)
+#define YYEMPTY		-2
+#define YYEOF		0
+#define YYACCEPT	goto yyacceptlab
+#define YYABORT 	goto yyabortlab
+#define YYERROR		goto yyerrlab1
+/* Like YYERROR except do call yyerror.  This remains here temporarily
+   to ease the transition to the new meaning of YYERROR, for GCC.
+   Once GCC version 2 has supplanted version 1, this can go.  */
+#define YYFAIL		goto yyerrlab
+#define YYRECOVERING()  (!!yyerrstatus)
+#define YYBACKUP(Token, Value)					\
+do								\
+  if (yychar == YYEMPTY && yylen == 1)				\
+    {								\
+      yychar = (Token);						\
+      yylval = (Value);						\
+      yychar1 = YYTRANSLATE (yychar);				\
+      YYPOPSTACK;						\
+      goto yybackup;						\
+    }								\
+  else								\
+    { 								\
+      yyerror ("syntax error: cannot back up");			\
+      YYERROR;							\
+    }								\
+while (0)
+
+#define YYTERROR	1
+#define YYERRCODE	256
+
+
+/* YYLLOC_DEFAULT -- Compute the default location (before the actions
+   are run).
+
+   When YYLLOC_DEFAULT is run, CURRENT is set the location of the
+   first token.  By default, to implement support for ranges, extend
+   its range to the last symbol.  */
+
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N)       	\
+   Current.last_line   = Rhs[N].last_line;	\
+   Current.last_column = Rhs[N].last_column;
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments.  */
+
+#if YYPURE
+# if YYLSP_NEEDED
+#  ifdef YYLEX_PARAM
+#   define YYLEX		yylex (&yylval, &yylloc, YYLEX_PARAM)
+#  else
+#   define YYLEX		yylex (&yylval, &yylloc)
+#  endif
+# else /* !YYLSP_NEEDED */
+#  ifdef YYLEX_PARAM
+#   define YYLEX		yylex (&yylval, YYLEX_PARAM)
+#  else
+#   define YYLEX		yylex (&yylval)
+#  endif
+# endif /* !YYLSP_NEEDED */
+#else /* !YYPURE */
+# define YYLEX			yylex ()
+#endif /* !YYPURE */
+
+
+/* Enable debugging if requested.  */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args)			\
+do {						\
+  if (yydebug)					\
+    YYFPRINTF Args;				\
+} while (0)
+/* Nonzero means print parse trace.  It is left uninitialized so that
+   multiple parsers can coexist.  */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+#endif /* !YYDEBUG */
+
+/* YYINITDEPTH -- initial size of the parser's stacks.  */
+#ifndef	YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+   if the built-in stack extension method is used).
+
+   Do not make this value too large; the results are undefined if
+   SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH)
+   evaluated with infinite-precision integer arithmetic.  */
+
+#if YYMAXDEPTH == 0
+# undef YYMAXDEPTH
+#endif
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+#ifdef YYERROR_VERBOSE
+
+# ifndef yystrlen
+#  if defined (__GLIBC__) && defined (_STRING_H)
+#   define yystrlen strlen
+#  else
+/* Return the length of YYSTR.  */
+static YYSIZE_T
+#   if defined (__STDC__) || defined (__cplusplus)
+yystrlen (const char *yystr)
+#   else
+yystrlen (yystr)
+     const char *yystr;
+#   endif
+{
+  register const char *yys = yystr;
+
+  while (*yys++ != '\0')
+    continue;
+
+  return yys - yystr - 1;
+}
+#  endif
+# endif
+
+# ifndef yystpcpy
+#  if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE)
+#   define yystpcpy stpcpy
+#  else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+   YYDEST.  */
+static char *
+#   if defined (__STDC__) || defined (__cplusplus)
+yystpcpy (char *yydest, const char *yysrc)
+#   else
+yystpcpy (yydest, yysrc)
+     char *yydest;
+     const char *yysrc;
+#   endif
+{
+  register char *yyd = yydest;
+  register const char *yys = yysrc;
+
+  while ((*yyd++ = *yys++) != '\0')
+    continue;
+
+  return yyd - 1;
+}
+#  endif
+# endif
+#endif
+
+#line 319 "/usr/share/bison/bison.simple"
+
+
+/* The user can define YYPARSE_PARAM as the name of an argument to be passed
+   into yyparse.  The argument should have type void *.
+   It should actually point to an object.
+   Grammar actions can access the variable by casting it
+   to the proper pointer type.  */
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+#  define YYPARSE_PARAM_ARG void *YYPARSE_PARAM
+#  define YYPARSE_PARAM_DECL
+# else
+#  define YYPARSE_PARAM_ARG YYPARSE_PARAM
+#  define YYPARSE_PARAM_DECL void *YYPARSE_PARAM;
+# endif
+#else /* !YYPARSE_PARAM */
+# define YYPARSE_PARAM_ARG
+# define YYPARSE_PARAM_DECL
+#endif /* !YYPARSE_PARAM */
+
+/* Prevent warning if -Wstrict-prototypes.  */
+#ifdef __GNUC__
+# ifdef YYPARSE_PARAM
+YYPARSE_RETURN_TYPE yyparse (void *);
+# else
+YYPARSE_RETURN_TYPE yyparse (void);
+# endif
+#endif
+
+/* YY_DECL_VARIABLES -- depending whether we use a pure parser,
+   variables are global, or local to YYPARSE.  */
+
+#define YY_DECL_NON_LSP_VARIABLES			\
+/* The lookahead symbol.  */				\
+int yychar;						\
+							\
+/* The semantic value of the lookahead symbol. */	\
+YYSTYPE yylval;						\
+							\
+/* Number of parse errors so far.  */			\
+int yynerrs;
+
+#if YYLSP_NEEDED
+# define YY_DECL_VARIABLES			\
+YY_DECL_NON_LSP_VARIABLES			\
+						\
+/* Location data for the lookahead symbol.  */	\
+YYLTYPE yylloc;
+#else
+# define YY_DECL_VARIABLES			\
+YY_DECL_NON_LSP_VARIABLES
+#endif
+
+
+/* If nonreentrant, generate the variables here. */
+
+#if !YYPURE
+YY_DECL_VARIABLES
+#endif  /* !YYPURE */
+
+YYPARSE_RETURN_TYPE
+yyparse (YYPARSE_PARAM_ARG)
+     YYPARSE_PARAM_DECL
+{
+  /* If reentrant, generate the variables here. */
+#if YYPURE
+  YY_DECL_VARIABLES
+#endif  /* !YYPURE */
+
+  register int yystate;
+  register int yyn;
+  int yyresult;
+  /* Number of tokens to shift before error messages enabled.  */
+  int yyerrstatus;
+  /* Lookahead token as an internal (translated) token number.  */
+  int yychar1 = 0;
+
+  /* Three stacks and their tools:
+     `yyss': related to states,
+     `yyvs': related to semantic values,
+     `yyls': related to locations.
+
+     Refer to the stacks thru separate pointers, to allow yyoverflow
+     to reallocate them elsewhere.  */
+
+  /* The state stack. */
+  short	yyssa[YYINITDEPTH];
+  short *yyss = yyssa;
+  register short *yyssp;
+
+  /* The semantic value stack.  */
+  YYSTYPE yyvsa[YYINITDEPTH];
+  YYSTYPE *yyvs = yyvsa;
+  register YYSTYPE *yyvsp;
+
+#if YYLSP_NEEDED
+  /* The location stack.  */
+  YYLTYPE yylsa[YYINITDEPTH];
+  YYLTYPE *yyls = yylsa;
+  YYLTYPE *yylsp;
+#endif
+
+#if YYLSP_NEEDED
+# define YYPOPSTACK   (yyvsp--, yyssp--, yylsp--)
+#else
+# define YYPOPSTACK   (yyvsp--, yyssp--)
+#endif
+
+  YYSIZE_T yystacksize = YYINITDEPTH;
+
+
+  /* The variables used to return semantic value and location from the
+     action routines.  */
+  YYSTYPE yyval;
+#if YYLSP_NEEDED
+  YYLTYPE yyloc;
+#endif
+
+  /* When reducing, the number of symbols on the RHS of the reduced
+     rule. */
+  int yylen;
+
+  YYDPRINTF ((stderr, "Starting parse\n"));
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY;		/* Cause a token to be read.  */
+
+  /* Initialize stack pointers.
+     Waste one element of value and location stack
+     so that they stay on the same level as the state stack.
+     The wasted elements are never initialized.  */
+
+  yyssp = yyss;
+  yyvsp = yyvs;
+#if YYLSP_NEEDED
+  yylsp = yyls;
+#endif
+  goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate.  |
+`------------------------------------------------------------*/
+ yynewstate:
+  /* In all cases, when you get here, the value and location stacks
+     have just been pushed. so pushing a state here evens the stacks.
+     */
+  yyssp++;
+
+ yysetstate:
+  *yyssp = yystate;
+
+  if (yyssp >= yyss + yystacksize - 1)
+    {
+      /* Get the current used size of the three stacks, in elements.  */
+      YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      {
+	/* Give user a chance to reallocate the stack. Use copies of
+	   these so that the &'s don't force the real ones into
+	   memory.  */
+	YYSTYPE *yyvs1 = yyvs;
+	short *yyss1 = yyss;
+
+	/* Each stack pointer address is followed by the size of the
+	   data in use in that stack, in bytes.  */
+# if YYLSP_NEEDED
+	YYLTYPE *yyls1 = yyls;
+	/* This used to be a conditional around just the two extra args,
+	   but that might be undefined if yyoverflow is a macro.  */
+	yyoverflow ("parser stack overflow",
+		    &yyss1, yysize * sizeof (*yyssp),
+		    &yyvs1, yysize * sizeof (*yyvsp),
+		    &yyls1, yysize * sizeof (*yylsp),
+		    &yystacksize);
+	yyls = yyls1;
+# else
+	yyoverflow ("parser stack overflow",
+		    &yyss1, yysize * sizeof (*yyssp),
+		    &yyvs1, yysize * sizeof (*yyvsp),
+		    &yystacksize);
+# endif
+	yyss = yyss1;
+	yyvs = yyvs1;
+      }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+      goto yyoverflowlab;
+# else
+      /* Extend the stack our own way.  */
+      if (yystacksize >= YYMAXDEPTH)
+	goto yyoverflowlab;
+      yystacksize *= 2;
+      if (yystacksize > YYMAXDEPTH)
+	yystacksize = YYMAXDEPTH;
+
+      {
+	short *yyss1 = yyss;
+	union yyalloc *yyptr =
+	  (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+	if (! yyptr)
+	  goto yyoverflowlab;
+	YYSTACK_RELOCATE (yyss);
+	YYSTACK_RELOCATE (yyvs);
+# if YYLSP_NEEDED
+	YYSTACK_RELOCATE (yyls);
+# endif
+# undef YYSTACK_RELOCATE
+	if (yyss1 != yyssa)
+	  YYSTACK_FREE (yyss1);
+      }
+# endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + yysize - 1;
+      yyvsp = yyvs + yysize - 1;
+#if YYLSP_NEEDED
+      yylsp = yyls + yysize - 1;
+#endif
+
+      YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+		  (unsigned long int) yystacksize));
+
+      if (yyssp >= yyss + yystacksize - 1)
+	YYABORT;
+    }
+
+  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+  goto yybackup;
+
+
+/*-----------.
+| yybackup.  |
+`-----------*/
+yybackup:
+
+/* Do appropriate processing given the current state.  */
+/* Read a lookahead token if we need one and don't already have one.  */
+/* yyresume: */
+
+  /* First try to decide what to do without reference to lookahead token.  */
+
+  yyn = yypact[yystate];
+  if (yyn == YYFLAG)
+    goto yydefault;
+
+  /* Not known => get a lookahead token if don't already have one.  */
+
+  /* yychar is either YYEMPTY or YYEOF
+     or a valid token in external form.  */
+
+  if (yychar == YYEMPTY)
+    {
+      YYDPRINTF ((stderr, "Reading a token: "));
+      yychar = YYLEX;
+    }
+
+  /* Convert token to internal form (in yychar1) for indexing tables with */
+
+  if (yychar <= 0)		/* This means end of input. */
+    {
+      yychar1 = 0;
+      yychar = YYEOF;		/* Don't call YYLEX any more */
+
+      YYDPRINTF ((stderr, "Now at end of input.\n"));
+    }
+  else
+    {
+      yychar1 = YYTRANSLATE (yychar);
+
+#if YYDEBUG
+     /* We have to keep this `#if YYDEBUG', since we use variables
+	which are defined only if `YYDEBUG' is set.  */
+      if (yydebug)
+	{
+	  YYFPRINTF (stderr, "Next token is %d (%s",
+		     yychar, yytname[yychar1]);
+	  /* Give the individual parser a way to print the precise
+	     meaning of a token, for further debugging info.  */
+# ifdef YYPRINT
+	  YYPRINT (stderr, yychar, yylval);
+# endif
+	  YYFPRINTF (stderr, ")\n");
+	}
+#endif
+    }
+
+  yyn += yychar1;
+  if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1)
+    goto yydefault;
+
+  yyn = yytable[yyn];
+
+  /* yyn is what to do for this token type in this state.
+     Negative => reduce, -yyn is rule number.
+     Positive => shift, yyn is new state.
+       New state is final state => don't bother to shift,
+       just return success.
+     0, or most negative number => error.  */
+
+  if (yyn < 0)
+    {
+      if (yyn == YYFLAG)
+	goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+  else if (yyn == 0)
+    goto yyerrlab;
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  /* Shift the lookahead token.  */
+  YYDPRINTF ((stderr, "Shifting token %d (%s), ",
+	      yychar, yytname[yychar1]));
+
+  /* Discard the token being shifted unless it is eof.  */
+  if (yychar != YYEOF)
+    yychar = YYEMPTY;
+
+  *++yyvsp = yylval;
+#if YYLSP_NEEDED
+  *++yylsp = yylloc;
+#endif
+
+  /* Count tokens shifted since error; after three, turn off error
+     status.  */
+  if (yyerrstatus)
+    yyerrstatus--;
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state.  |
+`-----------------------------------------------------------*/
+yydefault:
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+  goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction.  |
+`-----------------------------*/
+yyreduce:
+  /* yyn is the number of a rule to reduce with.  */
+  yylen = yyr2[yyn];
+
+  /* If YYLEN is nonzero, implement the default value of the action:
+     `$$ = $1'.
+
+     Otherwise, the following line sets YYVAL to the semantic value of
+     the lookahead token.  This behavior is undocumented and Bison
+     users should not rely upon it.  Assigning to YYVAL
+     unconditionally makes the parser a bit smaller, and it avoids a
+     GCC warning that YYVAL may be used uninitialized.  */
+  yyval = yyvsp[1-yylen];
+
+#if YYLSP_NEEDED
+  /* Similarly for the default location.  Let the user run additional
+     commands if for instance locations are ranges.  */
+  yyloc = yylsp[1-yylen];
+  YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen);
+#endif
+
+#if YYDEBUG
+  /* We have to keep this `#if YYDEBUG', since we use variables which
+     are defined only if `YYDEBUG' is set.  */
+  if (yydebug)
+    {
+      int yyi;
+
+      YYFPRINTF (stderr, "Reducing via rule %d (line %d), ",
+		 yyn, yyrline[yyn]);
+
+      /* Print the symbols being reduced, and their result.  */
+      for (yyi = yyprhs[yyn]; yyrhs[yyi] > 0; yyi++)
+	YYFPRINTF (stderr, "%s ", yytname[yyrhs[yyi]]);
+      YYFPRINTF (stderr, " -> %s\n", yytname[yyr1[yyn]]);
+    }
+#endif
+
+  switch (yyn) {
+
+case 1:
+#line 62 "geometry.yy"
+{
+		  linenum = 1;
+		;
+    break;}
+case 2:
+#line 69 "geometry.yy"
+{
+		int i;
+		extern ARRAY<char*> parsestrings;
+		for (i = 0; i < parsestrings.Size(); i++)
+ 		  delete [] parsestrings[i];
+		parsestrings.SetSize(0);
+		;
+    break;}
+case 5:
+#line 86 "geometry.yy"
+{ parsegeom->SetSolid(yyvsp[-2].chptr, new Solid (Solid::ROOT, yyvsp[0].solidtype)); 
+		  ;
+    break;}
+case 6:
+#line 89 "geometry.yy"
+{ 
+		  parsegeom->SetFlags(yyvsp[-4].chptr, parseflags); 
+		  ;
+    break;}
+case 8:
+#line 96 "geometry.yy"
+{ yyval.solidtype = (Solid*)parsegeom->GetSolid(yyvsp[0].chptr);  ;
+    break;}
+case 9:
+#line 97 "geometry.yy"
+{ yyval.solidtype = new Solid (Solid::UNION, yyvsp[-2].solidtype, yyvsp[0].solidtype); ;
+    break;}
+case 10:
+#line 98 "geometry.yy"
+{ yyval.solidtype = new Solid (Solid::SECTION, yyvsp[-2].solidtype, yyvsp[0].solidtype); ;
+    break;}
+case 11:
+#line 99 "geometry.yy"
+{ yyval.solidtype = new Solid (Solid::SUB, yyvsp[0].solidtype); ;
+    break;}
+case 12:
+#line 100 "geometry.yy"
+{ yyval.solidtype = yyvsp[-1].solidtype; ;
+    break;}
+case 13:
+#line 106 "geometry.yy"
+{ 
+		   OneSurfacePrimitive * surf = new Sphere (Point<3> (yyvsp[-7].val, yyvsp[-5].val, yyvsp[-3].val), yyvsp[-1].val); 
+		   parsegeom -> AddSurface (surf);
+		   surf->SetSurfaceId (0, parsegeom->GetNSurf()-1);
+                   yyval.solidtype = new Solid (surf);
+                 ;
+    break;}
+case 14:
+#line 116 "geometry.yy"
+{
+		   OneSurfacePrimitive * surf = new Cylinder (Point<3> (yyvsp[-13].val, yyvsp[-11].val, yyvsp[-9].val),
+	                                       Point<3> (yyvsp[-7].val, yyvsp[-5].val, yyvsp[-3].val), yyvsp[-1].val);
+		   parsegeom->AddSurface (surf);
+		   surf->SetSurfaceId (0, parsegeom->GetNSurf()-1);
+                   yyval.solidtype = new Solid (surf);
+                 ;
+    break;}
+case 15:
+#line 128 "geometry.yy"
+{
+		   OneSurfacePrimitive * surf = new Cone (Point<3> (yyvsp[-15].val, yyvsp[-13].val, yyvsp[-11].val),
+	                                     	 Point<3> (yyvsp[-7].val, yyvsp[-5].val, yyvsp[-3].val), yyvsp[-9].val, yyvsp[-1].val);
+		   parsegeom->AddSurface (surf);
+		   surf->SetSurfaceId (0, parsegeom->GetNSurf()-1);
+                   yyval.solidtype = new Solid (surf);
+                 ;
+    break;}
+case 16:
+#line 137 "geometry.yy"
+{
+		   OneSurfacePrimitive * surf = new Plane ( Point<3> (yyvsp[-11].val, yyvsp[-9].val, yyvsp[-7].val),
+	                                        Vec<3> (yyvsp[-5].val, yyvsp[-3].val, yyvsp[-1].val) );
+		   parsegeom->AddSurface (surf);
+		   surf->SetSurfaceId (0, parsegeom->GetNSurf()-1);
+                   yyval.solidtype = new Solid (surf);
+                 ;
+    break;}
+case 17:
+#line 146 "geometry.yy"
+{
+		  Primitive * nprim = new OrthoBrick (Point<3> (yyvsp[-11].val, yyvsp[-9].val, yyvsp[-7].val),
+		                                      Point<3> (yyvsp[-5].val, yyvsp[-3].val, yyvsp[-1].val));
+                  for (int j = 0; j < nprim->GetNSurfaces(); j++)
+		    {
+		      parsegeom->AddSurface (&nprim->GetSurface(j));
+		      nprim->SetSurfaceId (j, parsegeom->GetNSurf()-1);
+		      yyval.solidtype = new Solid (nprim);
+		    }
+		;
+    break;}
+case 18:
+#line 159 "geometry.yy"
+{
+		  polyhedron = new Polyhedra ();
+		;
+    break;}
+case 19:
+#line 164 "geometry.yy"
+{
+		  int j;
+                  for (j = 0; j < polyhedron->GetNSurfaces(); j++)
+		    {
+		      parsegeom->AddSurface (&polyhedron->GetSurface(j));
+		      polyhedron->SetSurfaceId (j, parsegeom->GetNSurf()-1);
+		      yyval.solidtype = new Solid (polyhedron);
+		    }	
+		;
+    break;}
+case 20:
+#line 198 "geometry.yy"
+{
+                  Solid * nsol = yyvsp[-1].solidtype -> Copy(*parsegeom);
+                  Vec<3> v(yyvsp[-7].val, yyvsp[-5].val, yyvsp[-3].val);
+                  Transformation<3> trans(v);
+		  nsol -> Transform (trans);
+                  yyval.solidtype = nsol;
+		;
+    break;}
+case 21:
+#line 207 "geometry.yy"
+{
+		  int i;
+                  Solid * hsol = yyvsp[-1].solidtype;
+	          for (i = 1; i <= yyvsp[-3].val; i++)
+                    {
+                      Solid * nsol = yyvsp[-1].solidtype -> Copy(*parsegeom);
+                      Vec<3> v(yyvsp[-9].val, yyvsp[-7].val, yyvsp[-5].val);
+		      v *= i;
+                      Transformation<3> trans(v);
+		      nsol -> Transform (trans);
+                      hsol = new Solid (Solid::UNION, hsol, nsol); 
+                    }
+		  yyval.solidtype = hsol;
+		;
+    break;}
+case 22:
+#line 223 "geometry.yy"
+{
+                  Solid * nsol = yyvsp[-1].solidtype -> Copy(*parsegeom);
+                  Point<3> c(yyvsp[-13].val, yyvsp[-11].val, yyvsp[-9].val);
+                  Transformation<3> rot(c, yyvsp[-7].val, yyvsp[-5].val, yyvsp[-3].val);
+		  nsol -> Transform (rot);
+                  yyval.solidtype = nsol;
+		;
+    break;}
+case 23:
+#line 234 "geometry.yy"
+{
+		  int i;
+                  Solid * hsol = yyvsp[-1].solidtype;                      
+
+		  Point<3> c(yyvsp[-15].val, yyvsp[-13].val, yyvsp[-11].val);
+                  Transformation<3> trans(c, yyvsp[-9].val, yyvsp[-7].val, yyvsp[-5].val);
+		  Transformation<3> multi(Vec<3>(0,0,0));
+		  Transformation<3> ht;
+
+	          for (i = 1; i <= yyvsp[-3].val; i++)
+                    {
+                      Solid * nsol = yyvsp[-1].solidtype -> Copy(*parsegeom);
+		      nsol -> Transform (multi);
+                      hsol = new Solid (Solid::UNION, hsol, nsol); 
+
+		      ht=multi;
+                      multi.Combine (trans, ht);
+                    }
+		  yyval.solidtype = hsol;
+		;
+    break;}
+case 28:
+#line 295 "geometry.yy"
+{
+		polyhedron->AddPoint (Point<3> (yyvsp[-4].val, yyvsp[-2].val, yyvsp[0].val));
+ 		cout << " " << yyvsp[-4].val << " " << yyvsp[-2].val << " " << yyvsp[0].val << endl;
+	;
+    break;}
+case 29:
+#line 302 "geometry.yy"
+{ 
+		polyhedron->AddFace (int(yyvsp[-4].val)-1, int(yyvsp[-2].val)-1, int(yyvsp[0].val)-1);
+		cout << yyvsp[-4].val << " " << yyvsp[-2].val << " " << yyvsp[0].val << endl; 
+	;
+    break;}
+case 30:
+#line 307 "geometry.yy"
+{ 	
+		cout << "face, 1 = " << yyvsp[-6].val << " " << yyvsp[-4].val << " "  << yyvsp[-2].val << " " << yyvsp[0].val << endl; 
+		polyhedron->AddFace (int(yyvsp[-6].val)-1, int(yyvsp[-4].val)-1, int(yyvsp[-2].val)-1);
+		polyhedron->AddFace (int(yyvsp[-6].val)-1, int(yyvsp[-2].val)-1, int(yyvsp[0].val)-1);
+		cout << yyvsp[-6].val << yyvsp[-4].val << yyvsp[-2].val << yyvsp[0].val << endl; 
+	;
+    break;}
+case 33:
+#line 321 "geometry.yy"
+{
+//		revolution->AddPoint (Point<2> ($1, $3));
+ 		cout << " " << yyvsp[-2].val << " " << yyvsp[0].val << endl;
+	;
+    break;}
+case 36:
+#line 338 "geometry.yy"
+{
+	        middlecurve->AddSegment (splinep1, Point<3> (yyvsp[-10].val, yyvsp[-8].val, yyvsp[-6].val), Point<3> (yyvsp[-4].val, yyvsp[-2].val, yyvsp[0].val));
+		splinep1(0) = yyvsp[-4].val; splinep1(1) = yyvsp[-2].val; splinep1(2) = yyvsp[0].val;
+		;
+    break;}
+case 37:
+#line 347 "geometry.yy"
+{
+                bspline = new BSplineCurve2d;
+                bspline -> AddPoint (Point<2> (yyvsp[-2].val, yyvsp[0].val));
+		cout << "first point" << endl;
+                ;
+    break;}
+case 39:
+#line 355 "geometry.yy"
+{ ;
+    break;}
+case 40:
+#line 357 "geometry.yy"
+{
+               bspline -> AddPoint (Point<2> (yyvsp[-2].val, yyvsp[0].val));
+		cout << "Add Point: " << yyvsp[-2].val << "-" << yyvsp[0].val << endl;
+               ;
+    break;}
+case 44:
+#line 379 "geometry.yy"
+{ cout << "singular edge:" << yyvsp[-2].val << " between "
+		 << yyvsp[-1].chptr << " and " << yyvsp[0].chptr << endl; 
+	  parsegeom->singedges.Append 
+	    (new SingularEdge (yyvsp[-2].val, parsegeom->GetSolid(yyvsp[-1].chptr), 
+				parsegeom->GetSolid(yyvsp[0].chptr)));
+	;
+    break;}
+case 45:
+#line 387 "geometry.yy"
+{ cout << "singular point:" << yyvsp[-3].val << " between "
+		 << yyvsp[-2].chptr << ", " << yyvsp[-1].chptr << " and " << yyvsp[0].chptr << endl; 
+	  parsegeom->singpoints.Append 
+	    (new SingularPoint (yyvsp[-3].val, parsegeom->GetSolid(yyvsp[-2].chptr), 
+				parsegeom->GetSolid(yyvsp[-1].chptr),
+				parsegeom->GetSolid(yyvsp[0].chptr)));
+	;
+    break;}
+case 46:
+#line 396 "geometry.yy"
+{ 	
+	  ARRAY<int> si1, si2;
+	  parsegeom->GetSolid(yyvsp[-2].chptr)->GetSurfaceIndices(si1);
+	  parsegeom->GetSolid(yyvsp[-1].chptr)->GetSurfaceIndices(si2);
+
+	  parsegeom->AddIdentification (
+		new CloseSurfaceIdentification (
+			parsegeom->GetNIdentifications()+1,
+			*parsegeom, 
+			parsegeom->GetSurface (si1[0]),
+			parsegeom->GetSurface (si2[0]),
+			parseflags));
+	;
+    break;}
+case 47:
+#line 411 "geometry.yy"
+{ 	
+	  ARRAY<int> si1, si2, si3;
+	  parsegeom->GetSolid(yyvsp[-2].chptr)->GetSurfaceIndices(si1);
+	  parsegeom->GetSolid(yyvsp[-1].chptr)->GetSurfaceIndices(si2);
+	  parsegeom->GetSolid(yyvsp[0].chptr)->GetSurfaceIndices(si3);
+
+	  parsegeom->AddIdentification (
+		new CloseEdgesIdentification (
+			parsegeom->GetNIdentifications()+1,
+			*parsegeom, 
+			parsegeom->GetSurface (si1.Get(1)),
+			parsegeom->GetSurface (si2.Get(1)),
+			parsegeom->GetSurface (si3.Get(1))));
+	;
+    break;}
+case 48:
+#line 427 "geometry.yy"
+{ 
+	  ARRAY<int> si1, si2;
+	  parsegeom->GetSolid(yyvsp[-1].chptr)->GetSurfaceIndices(si1);
+	  parsegeom->GetSolid(yyvsp[0].chptr)->GetSurfaceIndices(si2);
+
+	  parsegeom->AddIdentification (
+		new PeriodicIdentification (
+			parsegeom->GetNIdentifications()+1,
+			*parsegeom,
+			parsegeom->GetSurface (si1.Get(1)),
+			parsegeom->GetSurface (si2.Get(1))));
+	;
+    break;}
+case 49:
+#line 441 "geometry.yy"
+{
+	  int tlonr = 
+            parsegeom->SetTopLevelObject ((Solid*)parsegeom->GetSolid(yyvsp[-1].chptr));
+	  TopLevelObject * tlo = parsegeom->GetTopLevelObject (tlonr);
+	  if (parseflags.NumListFlagDefined ("col"))
+	    {
+	      const ARRAY<double> & col = parseflags.GetNumListFlag ("col");
+	      tlo->SetRGB (col[0], col[1], col[2]);
+	    }
+	  if (parseflags.GetDefineFlag ("transparent"))
+	    tlo->SetTransparent (1);
+	;
+    break;}
+case 50:
+#line 455 "geometry.yy"
+{
+	  ARRAY<int> si;
+	  parsegeom->GetSolid(yyvsp[-1].chptr)->GetSurfaceIndices(si);
+	  int tlonr = 
+	    parsegeom->SetTopLevelObject ((Solid*)parsegeom->GetSolid(yyvsp[-2].chptr),
+					  (Surface*)parsegeom->GetSurface(si.Get(1)));
+	  TopLevelObject * tlo = parsegeom->GetTopLevelObject (tlonr);
+	  if (parseflags.NumListFlagDefined ("col"))
+	    {
+	      const ARRAY<double> & col = parseflags.GetNumListFlag ("col");
+	      tlo->SetRGB (col.Get(1), col.Get(2), col.Get(3));
+	    }
+	  if (parseflags.GetDefineFlag ("transparent"))
+	    tlo->SetTransparent (1);
+	;
+    break;}
+case 51:
+#line 473 "geometry.yy"
+{
+	  parsegeom->SetBoundingBox (Box<3> (Point<3> (yyvsp[-11].val, yyvsp[-9].val, yyvsp[-7].val), 
+                                             Point<3> (yyvsp[-5].val, yyvsp[-3].val, yyvsp[-1].val)));
+	;
+    break;}
+case 52:
+#line 479 "geometry.yy"
+{
+	  parsegeom->AddUserPoint (Point<3> (yyvsp[-5].val, yyvsp[-3].val, yyvsp[-1].val));
+	;
+    break;}
+case 53:
+#line 484 "geometry.yy"
+{
+  	  CSGeometry::BCModification bcm;
+	  ARRAY<int> si;
+
+	  parsegeom->GetSolid(yyvsp[-2].chptr)->GetSurfaceIndices(si);
+	
+          bcm.tlonr = -1;
+	  int i;	
+	  for (i = 0; i < parsegeom->GetNTopLevelObjects(); i++)
+	    if (strcmp (parsegeom->GetTopLevelObject(i)->GetSolid()->Name(), yyvsp[-1].chptr) == 0)
+	      {
+	        bcm.tlonr = i;
+	        break;
+              }
+
+	  bcm.bcnr = int(yyvsp[0].val);
+	  for (i = 0; i < si.Size(); i++)
+            {
+	      bcm.si = si[i];
+              parsegeom->bcmodifications.Append (bcm);
+            }
+	;
+    break;}
+case 54:
+#line 512 "geometry.yy"
+{ parseflags.DeleteFlags (); ;
+    break;}
+case 58:
+#line 523 "geometry.yy"
+{ parseflags.SetFlag (yyvsp[0].chptr); ;
+    break;}
+case 59:
+#line 525 "geometry.yy"
+{ parseflags.SetFlag (yyvsp[-2].chptr, yyvsp[0].chptr); ;
+    break;}
+case 60:
+#line 527 "geometry.yy"
+{ parseflags.SetFlag (yyvsp[-2].chptr, yyvsp[0].val); ;
+    break;}
+case 61:
+#line 529 "geometry.yy"
+{ parseflags.SetFlag (yyvsp[-2].chptr, doublearray); ;
+    break;}
+case 62:
+#line 531 "geometry.yy"
+{ parseflags.SetFlag (yyvsp[-2].chptr, stringarray); ;
+    break;}
+case 63:
+#line 536 "geometry.yy"
+{ doublearray.SetSize (0); ;
+    break;}
+case 65:
+#line 541 "geometry.yy"
+{ doublearray.Append (yyvsp[0].val); ;
+    break;}
+case 66:
+#line 542 "geometry.yy"
+{ doublearray.Append (yyvsp[0].val); ;
+    break;}
+case 67:
+#line 547 "geometry.yy"
+{
+	  int i;
+	  for (i = 0; i < stringarray.Size(); i++)
+	     delete stringarray[i];
+	  stringarray.SetSize (0);
+	;
+    break;}
+case 69:
+#line 558 "geometry.yy"
+{
+			stringarray.Append (new char[strlen(yyvsp[0].chptr)+1]);
+			strcpy (stringarray.Last(), yyvsp[0].chptr);
+		;
+    break;}
+case 70:
+#line 564 "geometry.yy"
+{
+			stringarray.Append (new char[strlen(yyvsp[0].chptr)+1]);
+			strcpy (stringarray.Last(), yyvsp[0].chptr);
+		;
+    break;}
+case 71:
+#line 575 "geometry.yy"
+{ yyval.chptr = yyvsp[0].chptr; ;
+    break;}
+case 72:
+#line 576 "geometry.yy"
+{ yyval.chptr = yyvsp[0].chptr; ;
+    break;}
+}
+
+#line 709 "/usr/share/bison/bison.simple"
+
+
+  yyvsp -= yylen;
+  yyssp -= yylen;
+#if YYLSP_NEEDED
+  yylsp -= yylen;
+#endif
+
+#if YYDEBUG
+  if (yydebug)
+    {
+      short *yyssp1 = yyss - 1;
+      YYFPRINTF (stderr, "state stack now");
+      while (yyssp1 != yyssp)
+	YYFPRINTF (stderr, " %d", *++yyssp1);
+      YYFPRINTF (stderr, "\n");
+    }
+#endif
+
+  *++yyvsp = yyval;
+#if YYLSP_NEEDED
+  *++yylsp = yyloc;
+#endif
+
+  /* Now `shift' the result of the reduction.  Determine what state
+     that goes to, based on the state we popped back to and the rule
+     number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
+  if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTBASE];
+
+  goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+  /* If not already recovering from an error, report this error.  */
+  if (!yyerrstatus)
+    {
+      ++yynerrs;
+
+#ifdef YYERROR_VERBOSE
+      yyn = yypact[yystate];
+
+      if (yyn > YYFLAG && yyn < YYLAST)
+	{
+	  YYSIZE_T yysize = 0;
+	  char *yymsg;
+	  int yyx, yycount;
+
+	  yycount = 0;
+	  /* Start YYX at -YYN if negative to avoid negative indexes in
+	     YYCHECK.  */
+	  for (yyx = yyn < 0 ? -yyn : 0;
+	       yyx < (int) (sizeof (yytname) / sizeof (char *)); yyx++)
+	    if (yycheck[yyx + yyn] == yyx)
+	      yysize += yystrlen (yytname[yyx]) + 15, yycount++;
+	  yysize += yystrlen ("parse error, unexpected ") + 1;
+	  yysize += yystrlen (yytname[YYTRANSLATE (yychar)]);
+	  yymsg = (char *) YYSTACK_ALLOC (yysize);
+	  if (yymsg != 0)
+	    {
+	      char *yyp = yystpcpy (yymsg, "parse error, unexpected ");
+	      yyp = yystpcpy (yyp, yytname[YYTRANSLATE (yychar)]);
+
+	      if (yycount < 5)
+		{
+		  yycount = 0;
+		  for (yyx = yyn < 0 ? -yyn : 0;
+		       yyx < (int) (sizeof (yytname) / sizeof (char *));
+		       yyx++)
+		    if (yycheck[yyx + yyn] == yyx)
+		      {
+			const char *yyq = ! yycount ? ", expecting " : " or ";
+			yyp = yystpcpy (yyp, yyq);
+			yyp = yystpcpy (yyp, yytname[yyx]);
+			yycount++;
+		      }
+		}
+	      yyerror (yymsg);
+	      YYSTACK_FREE (yymsg);
+	    }
+	  else
+	    yyerror ("parse error; also virtual memory exhausted");
+	}
+      else
+#endif /* defined (YYERROR_VERBOSE) */
+	yyerror ("parse error");
+    }
+  goto yyerrlab1;
+
+
+/*--------------------------------------------------.
+| yyerrlab1 -- error raised explicitly by an action |
+`--------------------------------------------------*/
+yyerrlab1:
+  if (yyerrstatus == 3)
+    {
+      /* If just tried and failed to reuse lookahead token after an
+	 error, discard it.  */
+
+      /* return failure if at end of input */
+      if (yychar == YYEOF)
+	YYABORT;
+      YYDPRINTF ((stderr, "Discarding token %d (%s).\n",
+		  yychar, yytname[yychar1]));
+      yychar = YYEMPTY;
+    }
+
+  /* Else will try to reuse lookahead token after shifting the error
+     token.  */
+
+  yyerrstatus = 3;		/* Each real token shifted decrements this */
+
+  goto yyerrhandle;
+
+
+/*-------------------------------------------------------------------.
+| yyerrdefault -- current state does not do anything special for the |
+| error token.                                                       |
+`-------------------------------------------------------------------*/
+yyerrdefault:
+#if 0
+  /* This is wrong; only states that explicitly want error tokens
+     should shift them.  */
+
+  /* If its default is to accept any token, ok.  Otherwise pop it.  */
+  yyn = yydefact[yystate];
+  if (yyn)
+    goto yydefault;
+#endif
+
+
+/*---------------------------------------------------------------.
+| yyerrpop -- pop the current state because it cannot handle the |
+| error token                                                    |
+`---------------------------------------------------------------*/
+yyerrpop:
+  if (yyssp == yyss)
+    YYABORT;
+  yyvsp--;
+  yystate = *--yyssp;
+#if YYLSP_NEEDED
+  yylsp--;
+#endif
+
+#if YYDEBUG
+  if (yydebug)
+    {
+      short *yyssp1 = yyss - 1;
+      YYFPRINTF (stderr, "Error: state stack now");
+      while (yyssp1 != yyssp)
+	YYFPRINTF (stderr, " %d", *++yyssp1);
+      YYFPRINTF (stderr, "\n");
+    }
+#endif
+
+/*--------------.
+| yyerrhandle.  |
+`--------------*/
+yyerrhandle:
+  yyn = yypact[yystate];
+  if (yyn == YYFLAG)
+    goto yyerrdefault;
+
+  yyn += YYTERROR;
+  if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR)
+    goto yyerrdefault;
+
+  yyn = yytable[yyn];
+  if (yyn < 0)
+    {
+      if (yyn == YYFLAG)
+	goto yyerrpop;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+  else if (yyn == 0)
+    goto yyerrpop;
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  YYDPRINTF ((stderr, "Shifting error token, "));
+
+  *++yyvsp = yylval;
+#if YYLSP_NEEDED
+  *++yylsp = yylloc;
+#endif
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here.  |
+`-------------------------------------*/
+yyacceptlab:
+  yyresult = 0;
+  goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here.  |
+`-----------------------------------*/
+yyabortlab:
+  yyresult = 1;
+  goto yyreturn;
+
+/*---------------------------------------------.
+| yyoverflowab -- parser overflow comes here.  |
+`---------------------------------------------*/
+yyoverflowlab:
+  yyerror ("parser stack overflow");
+  yyresult = 2;
+  /* Fall through.  */
+
+yyreturn:
+#ifndef yyoverflow
+  if (yyss != yyssa)
+    YYSTACK_FREE (yyss);
+#endif
+  return yyresult;
+}
+#line 577 "geometry.yy"
+
+
+
+int yyerror (char * s)
+{	
+  cerr << s << " in line " << linenum << endl;
+  return 0;
+}
+
diff --git a/contrib/Netgen/libsrc/csg/geometry.h b/contrib/Netgen/libsrc/csg/geometry.h
new file mode 100644
index 0000000000..d84e8db183
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/geometry.h
@@ -0,0 +1,48 @@
+#ifndef BISON_GEOMETRY_H
+# define BISON_GEOMETRY_H
+
+#ifndef YYSTYPE
+typedef union {
+double val;
+char * chptr;
+Solid * solidtype;
+} yystype;
+# define YYSTYPE yystype
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+# define	NUM	257
+# define	TOK_SOLID	258
+# define	TOK_RECO	259
+# define	TOK_TLO	260
+# define	TOK_BOUNDINGBOX	261
+# define	IDENT	262
+# define	IDENTSOLID	263
+# define	TOK_SPHERE	264
+# define	TOK_CYLINDER	265
+# define	TOK_CONE	266
+# define	TOK_PLAIN	267
+# define	TOK_TUBE	268
+# define	TOK_GENCYL	269
+# define	TOK_ORTHOBRICK	270
+# define	TOK_POLYHEDRON	271
+# define	TOK_REVOLUTION	272
+# define	TOK_OR	273
+# define	TOK_AND	274
+# define	TOK_NOT	275
+# define	TOK_TRANSLATE	276
+# define	TOK_MULTITRANSLATE	277
+# define	TOK_ROTATE	278
+# define	TOK_MULTIROTATE	279
+# define	TOK_SINGULAR	280
+# define	TOK_EDGE	281
+# define	TOK_POINT	282
+# define	TOK_IDENTIFY	283
+# define	TOK_CLOSESURFACES	284
+# define	TOK_CLOSEEDGES	285
+# define	TOK_PERIODIC	286
+# define	TOK_BOUNDARYCONDITION	287
+
+
+extern YYSTYPE yylval;
+
+#endif /* not BISON_GEOMETRY_H */
diff --git a/contrib/Netgen/libsrc/csg/geometry.ll b/contrib/Netgen/libsrc/csg/geometry.ll
new file mode 100644
index 0000000000..bb2edbc9f5
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/geometry.ll
@@ -0,0 +1,94 @@
+%{
+#include <mystdlib.h>
+#include <myadt.hpp> 
+
+#include <linalg.hpp> 
+#include <csg.hpp>
+
+
+
+
+// extern SYMBOLTABLE<Solid*> solids;
+namespace netgen {
+extern CSGeometry * parsegeom;
+}
+using namespace netgen;
+
+#include "geometry.h"
+
+
+ARRAY<char*> parsestrings;
+int linenum;
+%}
+
+dig     [0-9]
+id      [a-zA-Z][a-zA-Z0-9]*
+num1    [-+]?{dig}+\.?([eE][-+]?{dig}+)?
+num2    [-+]?{dig}*\.{dig}+([eE][-+]?{dig}+)?
+number  {num1}|{num2}
+%x      incl
+%x      comment
+
+%%
+algebraic3d     { return TOK_RECO; }
+solid           { return TOK_SOLID; }
+tlo		{ return TOK_TLO; }
+and             { return TOK_AND; }
+or              { return TOK_OR; }
+not             { return TOK_NOT; }
+translate	{ return TOK_TRANSLATE; }
+multitranslate	{ return TOK_MULTITRANSLATE; }
+rotate		{ return TOK_ROTATE; }
+multirotate	{ return TOK_MULTIROTATE; }
+sphere          { return TOK_SPHERE; }
+cylinder        { return TOK_CYLINDER; }
+cone		{ return TOK_CONE; }
+plain           { return TOK_PLAIN; }
+plane		{ return TOK_PLAIN; }
+tube	 	{ return TOK_TUBE; }
+gencyl		{ return TOK_GENCYL; }
+orthobrick	{ return TOK_ORTHOBRICK; }
+polyhedron	{ return TOK_POLYHEDRON; }
+revolution	{ return TOK_REVOLUTION; }
+
+singular	{ return TOK_SINGULAR; }
+edge		{ return TOK_EDGE; }
+point		{ return TOK_POINT; }
+
+identify	{ return TOK_IDENTIFY; }
+closesurfaces 	{ return TOK_CLOSESURFACES; }
+closeedges 	{ return TOK_CLOSEEDGES; }
+periodic	{ return TOK_PERIODIC; }
+boundarycondition { return TOK_BOUNDARYCONDITION; }
+boundingbox	{ return TOK_BOUNDINGBOX; }
+
+{number}        { yylval.val = atof (YYText()); return NUM; }
+{id}+           {
+                  yylval.chptr = new char [YYLeng()+1];
+		  parsestrings.Append (yylval.chptr);
+                  strcpy (yylval.chptr, YYText());
+                  if (parsegeom->GetSolid (yylval.chptr))
+                    return IDENTSOLID;
+                  else
+                    return IDENT;
+                }
+[ \t]             /* eat up ws */
+.               { return int(*YYText()); }
+\n		{ linenum++; }
+"##".*\n        { linenum++; cout << (YYText()+2) ; }  /* line comment */
+"#".*\n        { linenum++; }  /* line comment */
+
+
+%%
+
+extern FlexLexer * lexer;
+
+int yylex ()
+  {
+  return lexer -> yylex();
+  }
+
+extern "C" int yywrap ()
+  {
+  return 1;
+  }
diff --git a/contrib/Netgen/libsrc/csg/geometry.yy b/contrib/Netgen/libsrc/csg/geometry.yy
new file mode 100644
index 0000000000..49dd63a53a
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/geometry.yy
@@ -0,0 +1,585 @@
+%{
+//define YYDEBUG 1
+
+extern int yylex ();
+
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+
+#include <linalg.hpp>
+#include <csg.hpp>
+
+namespace netgen
+{
+netgen::CSGeometry * parsegeom;
+}
+
+using namespace netgen;
+
+// extern ARRAY<Surface*> surfaces;
+// extern SYMBOLTABLE<Solid*> solids;
+
+
+int yyerror (char * s);
+splinetube * tube;
+spline3d * middlecurve;
+Point<3> splinep1;
+BSplineCurve2d *bspline;
+Flags parseflags;
+extern int linenum;
+ARRAY<double> doublearray;
+ARRAY<char*> stringarray;
+
+Polyhedra * polyhedron;
+// Revolution * revolution;
+%}
+
+%union {
+double val;
+char * chptr;
+Solid * solidtype;
+}
+
+%token <val> NUM
+%token TOK_SOLID, TOK_RECO, TOK_TLO, TOK_BOUNDINGBOX
+%token <chptr> IDENT IDENTSOLID
+%token <solidtype> TOK_SPHERE TOK_CYLINDER TOK_CONE TOK_PLAIN TOK_TUBE TOK_GENCYL TOK_ORTHOBRICK TOK_POLYHEDRON TOK_REVOLUTION
+%left <solidtype> TOK_OR TOK_AND TOK_NOT
+%token <solidtype> TOK_TRANSLATE TOK_MULTITRANSLATE TOK_ROTATE TOK_MULTIROTATE
+%type <solidtype> solid solidprimitive 
+%type <void> splinesegmentlist splinesegment readbspline bsplinepointlist
+%type <chptr> anyident
+%token TOK_SINGULAR TOK_EDGE TOK_POINT
+%token TOK_IDENTIFY TOK_CLOSESURFACES TOK_CLOSEEDGES TOK_PERIODIC
+%token TOK_BOUNDARYCONDITION
+%type <void> polyhedronpoints polyhedronfaces polyhedronpoint polyhedronface
+%type <void> revolutionpoints revolutionpoint
+
+
+%%
+input: 	
+		{
+		  linenum = 1;
+		}
+	TOK_RECO
+	recsoliddef
+	recadddef
+	
+		{
+		int i;
+		extern ARRAY<char*> parsestrings;
+		for (i = 0; i < parsestrings.Size(); i++)
+ 		  delete [] parsestrings[i];
+		parsestrings.SetSize(0);
+		}
+	;
+
+
+recsoliddef:
+	/* empty */
+        | recsoliddef soliddef ';'
+        ;
+
+soliddef:
+          TOK_SOLID IDENT '=' solid
+                  { parsegeom->SetSolid($2, new Solid (Solid::ROOT, $4)); 
+		  }
+	  flaglist
+		  { 
+		  parsegeom->SetFlags($2, parseflags); 
+		  }		
+        ;
+
+solid:
+          solidprimitive
+        | IDENTSOLID              { $$ = (Solid*)parsegeom->GetSolid($1);  }
+        | solid TOK_OR solid      { $$ = new Solid (Solid::UNION, $1, $3); }
+        | solid TOK_AND solid     { $$ = new Solid (Solid::SECTION, $1, $3); }
+        | TOK_NOT solid           { $$ = new Solid (Solid::SUB, $2); }
+        | '(' solid ')'           { $$ = $2; }
+        ;
+
+solidprimitive:
+          TOK_SPHERE '(' NUM ',' NUM ',' NUM ';'
+                         NUM ')'
+                 { 
+		   OneSurfacePrimitive * surf = new Sphere (Point<3> ($3, $5, $7), $9); 
+		   parsegeom -> AddSurface (surf);
+		   surf->SetSurfaceId (0, parsegeom->GetNSurf()-1);
+                   $$ = new Solid (surf);
+                 }
+        | TOK_CYLINDER '(' NUM ',' NUM ',' NUM ';'
+                           NUM ',' NUM ',' NUM ';'
+                           NUM ')'
+
+                 {
+		   OneSurfacePrimitive * surf = new Cylinder (Point<3> ($3, $5, $7),
+	                                       Point<3> ($9, $11, $13), $15);
+		   parsegeom->AddSurface (surf);
+		   surf->SetSurfaceId (0, parsegeom->GetNSurf()-1);
+                   $$ = new Solid (surf);
+                 }
+        | TOK_CONE '(' NUM ',' NUM ',' NUM ';'
+                       NUM ';' 
+                       NUM ',' NUM ',' NUM ';'
+                       NUM ')'
+
+                 {
+		   OneSurfacePrimitive * surf = new Cone (Point<3> ($3, $5, $7),
+	                                     	 Point<3> ($11, $13, $15), $9, $17);
+		   parsegeom->AddSurface (surf);
+		   surf->SetSurfaceId (0, parsegeom->GetNSurf()-1);
+                   $$ = new Solid (surf);
+                 }
+        | TOK_PLAIN '(' NUM ',' NUM ',' NUM ';'
+                           NUM ',' NUM ',' NUM ')'
+                 {
+		   OneSurfacePrimitive * surf = new Plane ( Point<3> ($3, $5, $7),
+	                                        Vec<3> ($9, $11, $13) );
+		   parsegeom->AddSurface (surf);
+		   surf->SetSurfaceId (0, parsegeom->GetNSurf()-1);
+                   $$ = new Solid (surf);
+                 }
+	| TOK_ORTHOBRICK  '(' NUM ',' NUM ',' NUM ';'
+	                      NUM ',' NUM ',' NUM ')'
+		{
+		  Primitive * nprim = new OrthoBrick (Point<3> ($3, $5, $7),
+		                                      Point<3> ($9, $11, $13));
+                  for (int j = 0; j < nprim->GetNSurfaces(); j++)
+		    {
+		      parsegeom->AddSurface (&nprim->GetSurface(j));
+		      nprim->SetSurfaceId (j, parsegeom->GetNSurf()-1);
+		      $$ = new Solid (nprim);
+		    }
+		} 
+
+
+	| TOK_POLYHEDRON '(' 
+		{
+		  polyhedron = new Polyhedra ();
+		}
+		polyhedronpoints ';' ';'
+		polyhedronfaces ')'
+		{
+		  int j;
+                  for (j = 0; j < polyhedron->GetNSurfaces(); j++)
+		    {
+		      parsegeom->AddSurface (&polyhedron->GetSurface(j));
+		      polyhedron->SetSurfaceId (j, parsegeom->GetNSurf()-1);
+		      $$ = new Solid (polyhedron);
+		    }	
+		}
+
+
+/*
+	| TOK_REVOLUTION '(' NUM ',' NUM ',' NUM ';'
+	                     NUM ',' NUM ',' NUM ';' 
+		{
+                    revolution = new Revolution (Point<3> ($3, $5, $7),
+						 Point<3> ($9, $11, $13));
+		}
+ 		    revolutionpoints
+			  ')'
+	        {
+		  revolution -> Finish ();
+		  int j;
+                  for (j = 0; j < revolution->GetNSurfaces(); j++)
+		    {
+		      parsegeom->AddSurface (&revolution->GetSurface(j));
+		      revolution->SetSurfaceId (j, parsegeom->GetNSurf()-1);
+		      $$ = new Solid (revolution);
+		    }	
+	        }
+*/
+
+
+	| TOK_TRANSLATE  '(' NUM ',' NUM ',' NUM ';' solid ')'
+		{
+                  Solid * nsol = $9 -> Copy(*parsegeom);
+                  Vec<3> v($3, $5, $7);
+                  Transformation<3> trans(v);
+		  nsol -> Transform (trans);
+                  $$ = nsol;
+		}
+
+	| TOK_MULTITRANSLATE  '(' NUM ',' NUM ',' NUM ';' NUM ';' solid ')'
+		{
+		  int i;
+                  Solid * hsol = $11;
+	          for (i = 1; i <= $9; i++)
+                    {
+                      Solid * nsol = $11 -> Copy(*parsegeom);
+                      Vec<3> v($3, $5, $7);
+		      v *= i;
+                      Transformation<3> trans(v);
+		      nsol -> Transform (trans);
+                      hsol = new Solid (Solid::UNION, hsol, nsol); 
+                    }
+		  $$ = hsol;
+		}
+
+	| TOK_ROTATE  '(' NUM ',' NUM ',' NUM ';' NUM ',' NUM ',' NUM ';' solid ')'
+		{
+                  Solid * nsol = $15 -> Copy(*parsegeom);
+                  Point<3> c($3, $5, $7);
+                  Transformation<3> rot(c, $9, $11, $13);
+		  nsol -> Transform (rot);
+                  $$ = nsol;
+		}
+
+	| TOK_MULTIROTATE  '(' NUM ',' NUM ',' NUM ';'
+			       NUM ',' NUM ',' NUM ';'
+			       NUM ';' solid ')'
+		{
+		  int i;
+                  Solid * hsol = $17;                      
+
+		  Point<3> c($3, $5, $7);
+                  Transformation<3> trans(c, $9, $11, $13);
+		  Transformation<3> multi(Vec<3>(0,0,0));
+		  Transformation<3> ht;
+
+	          for (i = 1; i <= $15; i++)
+                    {
+                      Solid * nsol = $17 -> Copy(*parsegeom);
+		      nsol -> Transform (multi);
+                      hsol = new Solid (Solid::UNION, hsol, nsol); 
+
+		      ht=multi;
+                      multi.Combine (trans, ht);
+                    }
+		  $$ = hsol;
+		}
+
+
+/*
+	| TOK_TUBE '(' NUM ',' NUM ',' NUM ','
+		{
+		middlecurve = new spline3d;
+		splinep1.X() = $3; splinep1.Y() = $5; splinep1.Z() = $7;
+		}
+	 splinesegmentlist ';' NUM ')'
+		{
+		   Surface * surf = new splinetube (*middlecurve, $12);
+		   parsegeom->AddSurface (surf);
+                   $$ = new Solid (surf, parsegeom->GetNSurf());
+		}
+		
+	| TOK_GENCYL '(' NUM ',' NUM ',' NUM ';'
+	                 NUM ',' NUM ',' NUM ';' 
+	                 NUM ',' NUM ',' NUM ';'
+			readbspline
+			')'
+	        {
+		   Surface * surf = new GeneralizedCylinder
+	           (*bspline, Point<3> ($3, $5, $7), Vec<3> ($9, $11, $13),
+	             Vec<3> ($15, $17, $19) );
+		   parsegeom->AddSurface (surf);
+                   $$ = new Solid (surf, parsegeom->GetNSurf());
+	        }
+*/	             
+	;
+
+
+polyhedronpoints:
+	  polyhedronpoints ';' polyhedronpoint
+	| polyhedronpoint
+	;
+polyhedronfaces:
+	  polyhedronfaces ';' polyhedronface
+	| polyhedronface
+	;
+polyhedronpoint:
+	  NUM ',' NUM ',' NUM
+	{
+		polyhedron->AddPoint (Point<3> ($1, $3, $5));
+ 		cout << " " << $1 << " " << $3 << " " << $5 << endl;
+	}
+	;
+polyhedronface:
+	  NUM ',' NUM ',' NUM
+ 	{ 
+		polyhedron->AddFace (int($1)-1, int($3)-1, int($5)-1);
+		cout << $1 << " " << $3 << " " << $5 << endl; 
+	}
+	| NUM ',' NUM ',' NUM ',' NUM
+ 	{ 	
+		cout << "face, 1 = " << $1 << " " << $3 << " "  << $5 << " " << $7 << endl; 
+		polyhedron->AddFace (int($1)-1, int($3)-1, int($5)-1);
+		polyhedron->AddFace (int($1)-1, int($5)-1, int($7)-1);
+		cout << $1 << $3 << $5 << $7 << endl; 
+	}
+	;
+
+revolutionpoints:
+	  revolutionpoints ';' revolutionpoint
+	| revolutionpoint
+	;
+revolutionpoint:
+	  NUM ',' NUM 
+	{
+//		revolution->AddPoint (Point<2> ($1, $3));
+ 		cout << " " << $1 << " " << $3 << endl;
+	}
+	;
+
+
+
+
+	
+splinesegmentlist:
+	  splinesegment
+        | splinesegment ',' splinesegmentlist
+	;
+splinesegment:
+	  NUM ',' NUM ',' NUM ','
+	  NUM ',' NUM ',' NUM
+	 	{
+	        middlecurve->AddSegment (splinep1, Point<3> ($1, $3, $5), Point<3> ($7, $9, $11));
+		splinep1(0) = $7; splinep1(1) = $9; splinep1(2) = $11;
+		}
+	;
+	
+	
+readbspline:
+          NUM ',' NUM
+                {
+                bspline = new BSplineCurve2d;
+                bspline -> AddPoint (Point<2> ($1, $3));
+		cout << "first point" << endl;
+                }
+          bsplinepointlist
+        ;
+bsplinepointlist: 
+          /* empty */ { } 
+        | ',' NUM ',' NUM
+               {
+               bspline -> AddPoint (Point<2> ($2, $4));
+		cout << "Add Point: " << $2 << "-" << $4 << endl;
+               }
+          bsplinepointlist
+        ;
+
+
+
+
+
+
+
+
+
+recadddef:
+	/* empty */
+        | recadddef adddef ';'
+        ;
+
+adddef:
+	TOK_SINGULAR TOK_EDGE  NUM IDENTSOLID IDENTSOLID
+	{ cout << "singular edge:" << $3 << " between "
+		 << $4 << " and " << $5 << endl; 
+	  parsegeom->singedges.Append 
+	    (new SingularEdge ($3, parsegeom->GetSolid($4), 
+				parsegeom->GetSolid($5)));
+	}
+	|
+	TOK_SINGULAR TOK_POINT  NUM IDENTSOLID IDENTSOLID IDENTSOLID
+	{ cout << "singular point:" << $3 << " between "
+		 << $4 << ", " << $5 << " and " << $6 << endl; 
+	  parsegeom->singpoints.Append 
+	    (new SingularPoint ($3, parsegeom->GetSolid($4), 
+				parsegeom->GetSolid($5),
+				parsegeom->GetSolid($6)));
+	}
+	|
+	TOK_IDENTIFY TOK_CLOSESURFACES IDENTSOLID IDENTSOLID flaglist
+	{ 	
+	  ARRAY<int> si1, si2;
+	  parsegeom->GetSolid($3)->GetSurfaceIndices(si1);
+	  parsegeom->GetSolid($4)->GetSurfaceIndices(si2);
+
+	  parsegeom->AddIdentification (
+		new CloseSurfaceIdentification (
+			parsegeom->GetNIdentifications()+1,
+			*parsegeom, 
+			parsegeom->GetSurface (si1[0]),
+			parsegeom->GetSurface (si2[0]),
+			parseflags));
+	}
+	|
+	TOK_IDENTIFY TOK_CLOSEEDGES IDENTSOLID IDENTSOLID IDENTSOLID
+	{ 	
+	  ARRAY<int> si1, si2, si3;
+	  parsegeom->GetSolid($3)->GetSurfaceIndices(si1);
+	  parsegeom->GetSolid($4)->GetSurfaceIndices(si2);
+	  parsegeom->GetSolid($5)->GetSurfaceIndices(si3);
+
+	  parsegeom->AddIdentification (
+		new CloseEdgesIdentification (
+			parsegeom->GetNIdentifications()+1,
+			*parsegeom, 
+			parsegeom->GetSurface (si1.Get(1)),
+			parsegeom->GetSurface (si2.Get(1)),
+			parsegeom->GetSurface (si3.Get(1))));
+	}
+	|
+	TOK_IDENTIFY TOK_PERIODIC IDENTSOLID IDENTSOLID 
+	{ 
+	  ARRAY<int> si1, si2;
+	  parsegeom->GetSolid($3)->GetSurfaceIndices(si1);
+	  parsegeom->GetSolid($4)->GetSurfaceIndices(si2);
+
+	  parsegeom->AddIdentification (
+		new PeriodicIdentification (
+			parsegeom->GetNIdentifications()+1,
+			*parsegeom,
+			parsegeom->GetSurface (si1.Get(1)),
+			parsegeom->GetSurface (si2.Get(1))));
+	}
+	| 
+	TOK_TLO IDENTSOLID flaglist
+	{
+	  int tlonr = 
+            parsegeom->SetTopLevelObject ((Solid*)parsegeom->GetSolid($2));
+	  TopLevelObject * tlo = parsegeom->GetTopLevelObject (tlonr);
+	  if (parseflags.NumListFlagDefined ("col"))
+	    {
+	      const ARRAY<double> & col = parseflags.GetNumListFlag ("col");
+	      tlo->SetRGB (col[0], col[1], col[2]);
+	    }
+	  if (parseflags.GetDefineFlag ("transparent"))
+	    tlo->SetTransparent (1);
+	}
+	|
+	TOK_TLO IDENTSOLID IDENTSOLID flaglist
+	{
+	  ARRAY<int> si;
+	  parsegeom->GetSolid($3)->GetSurfaceIndices(si);
+	  int tlonr = 
+	    parsegeom->SetTopLevelObject ((Solid*)parsegeom->GetSolid($2),
+					  (Surface*)parsegeom->GetSurface(si.Get(1)));
+	  TopLevelObject * tlo = parsegeom->GetTopLevelObject (tlonr);
+	  if (parseflags.NumListFlagDefined ("col"))
+	    {
+	      const ARRAY<double> & col = parseflags.GetNumListFlag ("col");
+	      tlo->SetRGB (col.Get(1), col.Get(2), col.Get(3));
+	    }
+	  if (parseflags.GetDefineFlag ("transparent"))
+	    tlo->SetTransparent (1);
+	}
+	|
+	TOK_BOUNDINGBOX '(' NUM ',' NUM ',' NUM ';' 
+			    NUM ',' NUM ',' NUM ')'
+	{
+	  parsegeom->SetBoundingBox (Box<3> (Point<3> ($3, $5, $7), 
+                                             Point<3> ($9, $11, $13)));
+	}
+	| 
+	TOK_POINT '(' NUM ',' NUM ',' NUM ')' 
+	{
+	  parsegeom->AddUserPoint (Point<3> ($3, $5, $7));
+	}
+	|
+	TOK_BOUNDARYCONDITION IDENTSOLID IDENTSOLID NUM
+	{
+  	  CSGeometry::BCModification bcm;
+	  ARRAY<int> si;
+
+	  parsegeom->GetSolid($2)->GetSurfaceIndices(si);
+	
+          bcm.tlonr = -1;
+	  int i;	
+	  for (i = 0; i < parsegeom->GetNTopLevelObjects(); i++)
+	    if (strcmp (parsegeom->GetTopLevelObject(i)->GetSolid()->Name(), $3) == 0)
+	      {
+	        bcm.tlonr = i;
+	        break;
+              }
+
+	  bcm.bcnr = int($4);
+	  for (i = 0; i < si.Size(); i++)
+            {
+	      bcm.si = si[i];
+              parsegeom->bcmodifications.Append (bcm);
+            }
+	}
+	;	
+
+
+
+
+flaglist:
+	      { parseflags.DeleteFlags (); }
+	  recflaglist
+	;
+
+recflaglist:
+	  /* empty */
+	| flag recflaglist
+	;
+	
+flag:
+	  '-' anyident
+	                   { parseflags.SetFlag ($2); }
+	| '-' anyident '=' anyident
+	                   { parseflags.SetFlag ($2, $4); }
+	| '-' anyident '=' NUM
+	                   { parseflags.SetFlag ($2, $4); }
+	| '-' anyident '=' numlistbrack
+			   { parseflags.SetFlag ($2, doublearray); }
+	| '-' anyident '=' stringlistbrack
+			   { parseflags.SetFlag ($2, stringarray); }
+	;     	
+
+
+numlistbrack:
+	  '['   			{ doublearray.SetSize (0); }
+	   numlist ']'
+	;
+
+numlist:
+          NUM                        { doublearray.Append ($1); }
+        | numlist ',' NUM            { doublearray.Append ($3); }
+        ;
+
+stringlistbrack:
+	'['
+	{
+	  int i;
+	  for (i = 0; i < stringarray.Size(); i++)
+	     delete stringarray[i];
+	  stringarray.SetSize (0);
+	}
+	  stringlist  ']'
+	;
+
+stringlist:
+	  anyident
+	        {
+			stringarray.Append (new char[strlen($1)+1]);
+			strcpy (stringarray.Last(), $1);
+		}
+					
+	| stringlist ',' anyident
+           	{
+			stringarray.Append (new char[strlen($3)+1]);
+			strcpy (stringarray.Last(), $3);
+		}
+	;
+
+
+
+
+
+anyident:
+	  IDENT            { $$ = $1; }
+	| IDENTSOLID       { $$ = $1; }
+%%
+
+
+int yyerror (char * s)
+{	
+  cerr << s << " in line " << linenum << endl;
+  return 0;
+}
+
diff --git a/contrib/Netgen/libsrc/csg/geoml.hpp b/contrib/Netgen/libsrc/csg/geoml.hpp
new file mode 100644
index 0000000000..e7974ad206
--- /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 0000000000..4622ee42bb
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/identify.cpp
@@ -0,0 +1,1508 @@
+#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
+{
+  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
+{
+  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-3)
+	{
+	  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 * sold, *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 i;
+  int newpi = 0;
+  for (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);
+	   
+  /* 
+  (*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;
+		*/
+	      }
+	}
+    }
+}
+
+
+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.p1 << "-" << seg1.p2 << ", seg2 = " << seg2.p1 << "-" << seg2.p2;
+
+		    if (side == 1)
+		      {
+			if (mesh.GetIdentifications().Get (seg1.p1, seg2.p1) &&
+			    mesh.GetIdentifications().Get (seg1.p2, seg2.p2))
+			  {
+			    foundother = 1;
+			    break;
+			  }
+			
+			if (mesh.GetIdentifications().Get (seg1.p1, seg2.p2) &&
+			    mesh.GetIdentifications().Get (seg1.p2, seg2.p1))
+			  {
+			    foundother = 1;
+			    break;
+			  }
+		      }
+		    else
+		      {
+			if (mesh.GetIdentifications().Get (seg2.p1, seg1.p1) &&
+			    mesh.GetIdentifications().Get (seg2.p2, seg1.p2))
+			  {
+			    foundother = 1;
+			    break;
+			  }
+			
+			if (mesh.GetIdentifications().Get (seg2.p1, seg1.p2) &&
+			    mesh.GetIdentifications().Get (seg2.p2, seg1.p1))
+			  {
+			    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 i1, i2;
+  int found = 0;
+  int i, j, k;
+  int fother;
+
+
+  int facei = segs.Get(1).si;
+  int surfnr = mesh.GetFaceDescriptor(facei).SurfNr();
+
+
+  if (geom.GetSurface(surfnr) == s1 ||
+      geom.GetSurface(surfnr) == s2)
+    {
+      //      (*testout) << "surfs found !" << endl;
+
+      for (i = 1; i <= mesh.GetNSE(); i++)
+	{
+	  const Element2d & sel = mesh.SurfaceElement(i);
+	  INDEX_2 fpair (facei, sel.GetIndex());
+	  fpair.Sort();
+	  if (identfaces.Used (fpair))
+	    {
+	      found = 1;
+	      fother = sel.GetIndex();
+
+	      // copy element
+	      Element2d newel(3);
+	      newel.SetIndex (facei);
+	      for (k = 1; k <= 3; 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;
+      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");
+  // eps_n = 1e-3;
+  eps_n = 1e-6;
+
+  dom_surf_valid = 0;
+}
+
+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
+{
+
+  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);
+	}
+      else
+	{
+	  hsurf.SetSize (geom.GetNSurf());
+	  for (int j = 0; j < hsurf.Size(); j++)
+	    hsurf[j] = j;
+	}
+    }
+
+
+
+  if (!s1->PointOnSurface (sp1.p))
+    return 0;
+  
+  Vec<3> n1 = s1->GetNormalVector (sp1.p);
+  n1.Normalize();
+  if ( fabs(n1 * sp1.v) > eps_n)
+    return 0;
+  
+  if (!s2->PointOnSurface(sp2.p))
+    return 0;
+  
+  Vec<3> n2 = s2->GetNormalVector (sp2.p);
+  n2.Normalize();
+  if ( fabs(n2 * sp2.v) > eps_n)
+    return 0;
+  
+  // must have joint surface 
+  bool joint = 0;
+  //  for (int j = 0; j < geom.GetNSurf(); j++)
+  for (int jj = 0; jj < domain_surfaces.Size(); jj++)
+    {
+      int j = domain_surfaces[jj];
+      if (geom.GetSurface(j) -> PointOnSurface(sp1.p) &&
+	  geom.GetSurface(j) -> PointOnSurface(sp2.p) )
+	{
+	  Vec<3> hn1 = geom.GetSurface(j)->GetNormalVector (sp1.p);
+	  Vec<3> hn2 = geom.GetSurface(j)->GetNormalVector (sp2.p);
+	  
+	  if (hn1 * hn2 > 0)
+	    {
+	      joint = 1;
+	      break;
+	    }
+	}
+    }
+  if (!joint) return 0;
+  
+  Vec<3> v = sp2.p - sp1.p;
+  double vl = v.Length();
+  double cl = fabs (v*n1);
+      
+  if (cl > (1-eps_n*eps_n) * vl && (sp1.v - sp2.v).Length() < 0.1)
+    return 1;
+
+  return 0;
+}
+
+int CloseSurfaceIdentification :: 
+Identifyable (const Point<3> & p1, const Point<3> & p2) const
+{
+  return (s1->PointOnSurface (p1) && s2->PointOnSurface (p2));
+}
+  
+
+
+
+int CloseSurfaceIdentification :: 
+IdentifyableCandidate (const SpecialPoint & sp1) const
+{
+  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 * sold, *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->Print (*testout); 
+	  (*testout) << endl;
+	  (*testout) << "surf2: ";
+	  s2->Print (*testout);
+	  (*testout) << endl;
+
+	  cerr << "GetIdenfifiedPoint: Not possible" << endl;
+	  exit (1);
+	}    
+    }
+  
+  // project to other surface
+  Point<3> hp = p;
+  snew->Project (hp);
+
+  int i;
+  int newpi = 0;
+  for (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);
+  else
+    mesh.GetIdentifications().Add (newpi, pi, nr);
+	   
+  /* 
+  (*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 i, j;
+  int i1, i2;
+
+  int np = mesh.GetNP();
+  BitArray ons2(np);
+  ons2.Clear();
+  for (i2 = 1; i2 <= np; i2++)
+    if (s2->PointOnSurface (mesh.Point(i2)))
+      ons2.Set (i2);
+    
+  for (i1 = 1; i1 <= np; i1++)
+    {
+      const Point<3> p1 = mesh.Point(i1);
+      if (s1->PointOnSurface (p1))
+	{
+	  int candi2 = 0;
+	  double mindist = 1e10;
+
+	  Vec<3> n1;
+	  n1 = s1->GetNormalVector (p1);
+	  n1.Normalize();
+
+	  for (i2 = 1; i2 <= np; i2++)
+	    {
+	      if (i2 == i1)
+		continue;
+	
+	      const Point<3> p2 = mesh.Point(i2);
+	      
+	      if (!ons2.Test(i2))
+		continue;
+		  /*
+	      if (!s2->PointOnSurface (p2))
+		continue;
+		  */
+	      Vec<3> n = p2 - p1;
+	      n.Normalize();
+	      
+	      
+	      bool joint = 0;
+	      for (int jj = 0; jj < domain_surfaces.Size(); jj++)
+		{
+		  int j = domain_surfaces[jj];
+		  if (geom.GetSurface(j) -> PointOnSurface(p1) &&
+		      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 (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, p1 = " 
+			 << mesh[PointIndex(i1)] << ", p2 = " 
+			 << mesh[PointIndex(candi2)] << endl;
+
+	      mesh.GetIdentifications().Add (i1, candi2, nr);
+	    }
+	}
+    }
+}
+
+
+
+void CloseSurfaceIdentification :: IdentifyFaces (class Mesh & mesh)
+{
+  int i, j, k, l;
+  int fi1, fi2, side;
+
+  int s1rep, s2rep;
+  for (i = 0; i < geom.GetNSurf(); i++)
+    {
+      if (geom.GetSurface (i) == s1) 
+	s1rep = geom.GetSurfaceClassRepresentant(i);
+      if (geom.GetSurface (i) == s2) 
+	s2rep = geom.GetSurfaceClassRepresentant(i);
+    }
+
+  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 (s1rep != surfi || s2rep != 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.p1 << "-" << seg1.p2 << ", seg2 = " << seg2.p1 << "-" << seg2.p2;
+
+		    if (side == 1)
+		      {
+			if (mesh.GetIdentifications().Get (seg1.p1, seg2.p1) &&
+			    mesh.GetIdentifications().Get (seg1.p2, seg2.p2))
+			  {
+			    foundother = 1;
+			    break;
+			  }
+			
+			if (mesh.GetIdentifications().Get (seg1.p1, seg2.p2) &&
+			    mesh.GetIdentifications().Get (seg1.p2, seg2.p1))
+			  {
+			    foundother = 1;
+			    break;
+			  }
+		      }
+		    else
+		      {
+			if (mesh.GetIdentifications().Get (seg2.p1, seg1.p1) &&
+			    mesh.GetIdentifications().Get (seg2.p2, seg1.p2))
+			  {
+			    foundother = 1;
+			    break;
+			  }
+			
+			if (mesh.GetIdentifications().Get (seg2.p1, seg1.p2) &&
+			    mesh.GetIdentifications().Get (seg2.p2, seg1.p1))
+			  {
+			    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 CloseSurfaceIdentification :: 
+BuildSurfaceElements (ARRAY<Segment> & segs,
+		      Mesh & mesh, const Surface * surf)
+{
+  int i1, i2;
+  int found = 0, cntquads = 0;
+  int i, j, k;
+
+  // insert quad layer:
+  for (i1 = 1; i1 <= segs.Size(); i1++)
+    for (i2 = 1; i2 < i1; i2++)
+      {
+	const Segment & s1 = segs.Get(i1);
+	const Segment & s2 = segs.Get(i2);
+	if ( (mesh.GetIdentifications().Get (s1.p1, s2.p2) == nr &&
+	      mesh.GetIdentifications().Get (s1.p2, s2.p1) == nr)    || 
+	     (mesh.GetIdentifications().Get (s2.p1, s1.p2) == nr &&
+	      mesh.GetIdentifications().Get (s2.p2, s1.p1) == nr)
+	     )
+	  {
+	    Element2d el(4);
+	    el.PNum(1) = s1.p1;
+	    el.PNum(2) = s1.p2;
+	    el.PNum(3) = s2.p1;
+	    el.PNum(4) = s2.p2;
+
+	    Vec<3> n = Cross (Point<3> (mesh.Point(el.PNum(2)))-
+			      Point<3> (mesh.Point(el.PNum(1))),
+			      Point<3> (mesh.Point(el.PNum(4)))-
+			      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));
+	      }
+			     
+	    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;
+	    cntquads++;
+	  }
+      }
+
+  if (found)
+    {
+      (*mycout) << " insert quad layer of " << cntquads
+		<< " elements at face " << segs.Get(1).si << endl;
+      segs.SetSize(0);
+    }
+  else
+    {
+      BuildSurfaceElements2 (segs, mesh, surf);
+    }
+     
+  /* 
+      int fother;
+      int facei = segs.Get(1).si;
+      int surfnr = mesh.GetFaceDescriptor(facei).SurfNr();
+
+      int foundid = 0;
+      for (i = 1; i <= identfaces.GetNBags(); i++)
+	for (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) << "facei = " << facei << ", surfnr = " << surfnr << endl;
+
+      if (foundid)
+	{
+	  //	  (*testout) << "surfaces found" << endl;
+	  // copy surface
+	  for (i = 1; i <= mesh.GetNSE(); i++)
+	    {
+	      const Element2d & sel = mesh.SurfaceElement(i);
+	      INDEX_2 fpair (facei, sel.GetIndex());
+	      fpair.Sort();
+	      if (identfaces.Used (fpair))
+		{
+		  found = 1;
+		  fother = sel.GetIndex();
+		  
+		  // copy element
+		  Element2d newel(3);
+		  newel.SetIndex (facei);
+		  for (k = 1; k <= 3; k++)
+		    {
+		      newel.PNum(k) = 
+			GetIdentifiedPoint (mesh, sel.PNum(k));
+		      //		      cout << "id-point = " << sel.PNum(k) << ", np = " << newel.PNum(k) << endl;
+		    }	  
+		  
+		  Vec<3> nt = Cross (Vec<3> (mesh.Point (newel.PNum(1)), mesh.Point (newel.PNum(2))),
+				    Vec<3> (mesh.Point (newel.PNum(1)), mesh.Point (newel.PNum(3))));
+		  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;
+    }
+      
+  if (found)
+    segs.SetSize(0);
+  */
+}
+
+
+
+
+
+
+void CloseSurfaceIdentification :: 
+BuildSurfaceElements2 (ARRAY<Segment> & segs,
+		       Mesh & mesh, const Surface * surf)
+{
+  int i1, i2;
+  int found = 0, cntquads = 0;
+  int i, j, k;
+
+  int fother;
+  int facei = segs.Get(1).si;
+  int surfnr = mesh.GetFaceDescriptor(facei).SurfNr();
+  
+  int foundid = 0;
+  for (i = 1; i <= identfaces.GetNBags(); i++)
+    for (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) << "facei = " << facei << ", surfnr = " << surfnr << endl;
+  
+  /*
+    if (geom.GetSurface(surfnr) == s1 ||
+    geom.GetSurface(surfnr) == s2)
+  */
+  if (foundid)
+    {
+      //	  (*testout) << "surfaces found" << endl;
+      // copy surface
+      for (i = 1; i <= mesh.GetNSE(); i++)
+	{
+	  const Element2d & sel = mesh.SurfaceElement(i);
+	  INDEX_2 fpair (facei, sel.GetIndex());
+	  fpair.Sort();
+	  if (identfaces.Used (fpair))
+	    {
+	      found = 1;
+	      fother = sel.GetIndex();
+	      
+	      // copy element
+	      Element2d newel(3);
+	      newel.SetIndex (facei);
+	      for (k = 1; k <= 3; k++)
+		{
+		  newel.PNum(k) = 
+		    GetIdentifiedPoint (mesh, sel.PNum(k));
+		  //		      cout << "id-point = " << sel.PNum(k) << ", np = " << newel.PNum(k) << endl;
+		}	  
+	      
+	      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;
+      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
+{
+  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 i, j;
+  int i1, i2;
+
+  int np = mesh.GetNP();
+  for (i1 = 1; i1 <= np; i1++)
+    for (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);
+	  }
+      }
+}
+
+void CloseEdgesIdentification :: 
+BuildSurfaceElements (ARRAY<Segment> & segs,
+		      Mesh & mesh, const Surface * surf)
+{
+  int i1, i2;
+  int found = 0;
+  int i, j, k;
+
+  if (surf != facet)
+    return;
+
+  for (i1 = 1; i1 <= segs.Size(); i1++)
+    for (i2 = 1; i2 < i1; i2++)
+      {
+	const Segment & s1 = segs.Get(i1);
+	const Segment & s2 = segs.Get(i2);
+	if (mesh.GetIdentifications().Get (s1.p1, s2.p2) &&
+	    mesh.GetIdentifications().Get (s1.p2, s2.p1))
+	  {
+	    Element2d el(4);
+	    el.PNum(1) = s1.p1;
+	    el.PNum(2) = s1.p2;
+	    el.PNum(3) = s2.p2;
+	    el.PNum(4) = s2.p1;
+
+	    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 0000000000..16e371b200
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/identify.hpp
@@ -0,0 +1,180 @@
+
+#ifndef FILE_IDENTIFY
+#define FILE_IDENTIFY
+
+/**************************************************************************/
+/* File:   identify.hh                                                    */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   1. Aug. 99                                                    */
+/**************************************************************************/
+
+/**
+   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:
+  Identification (int anr, const CSGeometry & ageom);
+  virtual ~Identification ();
+  virtual void Print (ostream & ost) const = 0;
+  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;
+  ///
+  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;
+  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;
+  /// 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;
+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;
+  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; }
+};
+
+
+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;
+
+
+  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/lex.yy.cpp b/contrib/Netgen/libsrc/csg/lex.yy.cpp
new file mode 100644
index 0000000000..e5c4e6f614
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/lex.yy.cpp
@@ -0,0 +1,1834 @@
+/* A lexical scanner generated by flex */
+
+/* Scanner skeleton version:
+ * $Header: /cvsroot/gmsh/contrib/Netgen/libsrc/csg/lex.yy.cpp,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+ */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+
+
+
+/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */
+#ifdef c_plusplus
+#ifndef __cplusplus
+#define __cplusplus
+#endif
+#endif
+
+
+#ifdef __cplusplus
+
+#include <stdlib.h>
+#include <iostream.h>
+#include <unistd.h>
+
+/* Use prototypes in function declarations. */
+#define YY_USE_PROTOS
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else	/* ! __cplusplus */
+
+#if __STDC__
+
+#define YY_USE_PROTOS
+#define YY_USE_CONST
+
+#endif	/* __STDC__ */
+#endif	/* ! __cplusplus */
+
+#ifdef __TURBOC__
+ #pragma warn -rch
+ #pragma warn -use
+#include <io.h>
+#include <stdlib.h>
+#define YY_USE_CONST
+#define YY_USE_PROTOS
+#endif
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+
+#ifdef YY_USE_PROTOS
+#define YY_PROTO(proto) proto
+#else
+#define YY_PROTO(proto) ()
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index.  If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition.  This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yy_start = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state.  The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yy_start - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart( yyin )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#define YY_BUF_SIZE 16384
+
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+
+extern int yyleng;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+/* The funky do-while in the following #define is used to turn the definition
+ * int a single C statement (which needs a semi-colon terminator).  This
+ * avoids problems with code like:
+ *
+ * 	if ( condition_holds )
+ *		yyless( 5 );
+ *	else
+ *		do_something_else();
+ *
+ * Prior to using the do-while the compiler would get upset at the
+ * "else" because it interpreted the "if" statement as being all
+ * done when it reached the ';' after the yyless() call.
+ */
+
+/* Return all but the first 'n' matched characters back to the input stream. */
+
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+		*yy_cp = yy_hold_char; \
+		YY_RESTORE_YY_MORE_OFFSET \
+		yy_c_buf_p = yy_cp = yy_bp + n - YY_MORE_ADJ; \
+		YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+		} \
+	while ( 0 )
+
+#define unput(c) yyunput( c, yytext_ptr )
+
+/* The following is because we cannot portably get our hands on size_t
+ * (without autoconf's help, which isn't available because we want
+ * flex-generated scanners to compile on their own).
+ */
+typedef unsigned int yy_size_t;
+
+
+struct yy_buffer_state
+	{
+	istream* yy_input_file;
+
+	char *yy_ch_buf;		/* input buffer */
+	char *yy_buf_pos;		/* current position in input buffer */
+
+	/* Size of input buffer in bytes, not including room for EOB
+	 * characters.
+	 */
+	yy_size_t yy_buf_size;
+
+	/* Number of characters read into yy_ch_buf, not including EOB
+	 * characters.
+	 */
+	int yy_n_chars;
+
+	/* Whether we "own" the buffer - i.e., we know we created it,
+	 * and can realloc() it to grow it, and should free() it to
+	 * delete it.
+	 */
+	int yy_is_our_buffer;
+
+	/* Whether this is an "interactive" input source; if so, and
+	 * if we're using stdio for input, then we want to use getc()
+	 * instead of fread(), to make sure we stop fetching input after
+	 * each newline.
+	 */
+	int yy_is_interactive;
+
+	/* Whether we're considered to be at the beginning of a line.
+	 * If so, '^' rules will be active on the next match, otherwise
+	 * not.
+	 */
+	int yy_at_bol;
+
+	/* Whether to try to fill the input buffer when we reach the
+	 * end of it.
+	 */
+	int yy_fill_buffer;
+
+	int yy_buffer_status;
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+	/* When an EOF's been seen but there's still some text to process
+	 * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+	 * shouldn't try reading from the input source any more.  We might
+	 * still have a bunch of tokens to match, though, because of
+	 * possible backing-up.
+	 *
+	 * When we actually see the EOF, we change the status to "new"
+	 * (via yyrestart()), so that the user can continue scanning by
+	 * just pointing yyin at a new input file.
+	 */
+#define YY_BUFFER_EOF_PENDING 2
+	};
+
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ */
+#define YY_CURRENT_BUFFER yy_current_buffer
+
+
+
+static void *yy_flex_alloc YY_PROTO(( yy_size_t ));
+static void *yy_flex_realloc YY_PROTO(( void *, yy_size_t ));
+static void yy_flex_free YY_PROTO(( void * ));
+
+#define yy_new_buffer yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+	{ \
+	if ( ! yy_current_buffer ) \
+		yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
+	yy_current_buffer->yy_is_interactive = is_interactive; \
+	}
+
+#define yy_set_bol(at_bol) \
+	{ \
+	if ( ! yy_current_buffer ) \
+		yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
+	yy_current_buffer->yy_at_bol = at_bol; \
+	}
+
+#define YY_AT_BOL() (yy_current_buffer->yy_at_bol)
+
+
+#define FLEX_DEBUG
+typedef unsigned char YY_CHAR;
+#define yytext_ptr yytext
+#define YY_INTERACTIVE
+
+#define FLEX_DEBUG
+
+#include <FlexLexer.h>
+
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+	yytext_ptr = yy_bp; \
+	yyleng = (int) (yy_cp - yy_bp); \
+	yy_hold_char = *yy_cp; \
+	*yy_cp = '\0'; \
+	yy_c_buf_p = yy_cp;
+
+#define YY_NUM_RULES 37
+#define YY_END_OF_BUFFER 38
+static yyconst short int yy_accept[222] =
+    {   0,
+        0,    0,    0,    0,    0,    0,   38,   33,   32,   34,
+       33,   33,   33,   30,   31,   31,   31,   31,   31,   31,
+       31,   31,   31,   31,   31,   31,   31,   31,   37,    0,
+       36,    0,    0,   30,   30,   30,    0,   31,   31,   31,
+       31,   31,   31,   31,   31,   31,   31,   31,   31,   31,
+        5,   31,   31,   31,   31,   31,   31,   31,   31,   31,
+       31,   31,    0,   35,    0,    0,   30,   31,    4,   31,
+       31,   31,   31,   31,   31,   31,   31,    6,   31,   31,
+       31,   31,   31,   31,   31,   31,   31,   31,    3,   31,
+       31,    0,   30,   31,   31,   31,   13,   31,   22,   31,
+
+       31,   31,   31,   31,   31,   31,   31,   31,   31,   31,
+       31,   31,   31,   31,   16,   31,   31,   31,   31,   31,
+       31,   31,   31,   31,   14,   15,   23,   31,   31,   31,
+       31,    2,   31,   31,   31,   31,   31,   31,   31,   31,
+       17,   31,   31,   31,   31,   31,   31,   31,    9,   31,
+       11,   31,   31,   31,   31,   31,   31,   31,   31,   31,
+       31,   31,   31,   31,   31,   31,   31,   31,   31,   31,
+       31,   31,   12,   24,   31,   31,   31,   27,   31,   31,
+       21,   31,   31,   31,   31,   31,   31,   31,   31,   31,
+       31,   31,    7,   31,   31,   31,   26,   31,   31,   31,
+
+       18,   19,   20,    1,   31,   29,   31,   10,   31,   31,
+       31,   31,   31,   25,   31,   31,    8,   31,   31,   28,
+        0
+    } ;
+
+static yyconst int yy_ec[256] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    2,    3,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    2,    1,    1,    4,    1,    1,    1,    1,    1,
+        1,    1,    5,    1,    5,    6,    1,    7,    7,    7,
+        8,    7,    7,    7,    7,    7,    7,    1,    1,    1,
+        1,    1,    1,    1,    9,    9,    9,    9,   10,    9,
+        9,    9,    9,    9,    9,    9,    9,    9,    9,    9,
+        9,    9,    9,    9,    9,    9,    9,    9,    9,    9,
+        1,    1,    1,    1,    1,    1,   11,   12,   13,   14,
+
+       15,   16,   17,   18,   19,    9,   20,   21,   22,   23,
+       24,   25,    9,   26,   27,   28,   29,   30,    9,   31,
+       32,    9,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1
+    } ;
+
+static yyconst int yy_meta[33] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    2,    2,    2,    2,
+        2,    2,    2,    2,    2,    2,    2,    2,    2,    2,
+        2,    2,    2,    2,    2,    2,    2,    2,    2,    2,
+        2,    2
+    } ;
+
+static yyconst short int yy_base[226] =
+    {   0,
+        0,    0,    0,    0,    0,    0,   77,  706,  706,  706,
+       30,   29,   31,   34,   38,   40,   43,   61,   45,   47,
+       50,   58,   64,   66,   87,   76,  105,  124,  706,   67,
+      706,   57,   68,    0,   71,   89,   98,   82,  102,  108,
+      112,  110,  114,  120,  126,  129,  133,  144,  141,  147,
+      150,  153,  156,  161,  158,  163,  166,  169,  176,  178,
+      185,  191,   53,  706,  199,  193,  201,  203,  205,  207,
+      209,  212,  214,  216,  221,  218,  230,  232,  235,  237,
+      240,  242,  244,  247,  253,  260,  262,  265,  267,  271,
+      275,  277,  279,  281,  284,  288,  292,  294,  297,  299,
+
+      301,  303,  306,  308,  311,  313,  316,  318,  330,  332,
+      334,  338,  340,  342,  346,  348,  351,  357,  368,  360,
+      370,  372,  378,  380,  384,  388,  394,  396,  398,  400,
+      402,  405,  409,  411,  414,  421,  423,  426,  428,  430,
+      434,  436,  441,  443,  446,  448,  452,  454,  456,  463,
+      468,  470,  472,  476,  478,  480,  485,  491,  482,  493,
+      495,  497,  502,  505,  511,  515,  517,  519,  521,  528,
+      531,  535,  540,  542,  545,  547,  550,  552,  554,  557,
+      559,  561,  564,  566,  572,  575,  577,  579,  584,  586,
+      590,  592,  596,  602,  610,  612,  614,  616,  619,  623,
+
+      628,  630,  632,  634,  638,  640,  642,  646,  648,  653,
+      655,  657,  659,  661,  663,  667,  669,  672,  676,  681,
+      706,  699,  701,   41,  703
+    } ;
+
+static yyconst short int yy_def[226] =
+    {   0,
+      221,    1,  222,  222,  222,  222,  221,  221,  221,  221,
+      223,  221,  221,  221,  224,  224,  224,  224,  224,  224,
+      224,  224,  224,  224,  224,  224,  224,  224,  221,  223,
+      221,  225,  221,   14,  221,  221,  221,  224,  224,  224,
+      224,  224,  224,  224,  224,  224,  224,  224,  224,  224,
+      224,  224,  224,  224,  224,  224,  224,  224,  224,  224,
+      224,  224,  225,  221,  221,  221,  221,  224,  224,  224,
+      224,  224,  224,  224,  224,  224,  224,  224,  224,  224,
+      224,  224,  224,  224,  224,  224,  224,  224,  224,  224,
+      224,  221,  221,  224,  224,  224,  224,  224,  224,  224,
+
+      224,  224,  224,  224,  224,  224,  224,  224,  224,  224,
+      224,  224,  224,  224,  224,  224,  224,  224,  224,  224,
+      224,  224,  224,  224,  224,  224,  224,  224,  224,  224,
+      224,  224,  224,  224,  224,  224,  224,  224,  224,  224,
+      224,  224,  224,  224,  224,  224,  224,  224,  224,  224,
+      224,  224,  224,  224,  224,  224,  224,  224,  224,  224,
+      224,  224,  224,  224,  224,  224,  224,  224,  224,  224,
+      224,  224,  224,  224,  224,  224,  224,  224,  224,  224,
+      224,  224,  224,  224,  224,  224,  224,  224,  224,  224,
+      224,  224,  224,  224,  224,  224,  224,  224,  224,  224,
+
+      224,  224,  224,  224,  224,  224,  224,  224,  224,  224,
+      224,  224,  224,  224,  224,  224,  224,  224,  224,  224,
+        0,  221,  221,  221,  221
+    } ;
+
+static yyconst short int yy_nxt[739] =
+    {   0,
+        8,    9,   10,   11,   12,   13,   14,   14,   15,   15,
+       16,   17,   18,   15,   19,   15,   20,   15,   21,   15,
+       15,   22,   23,   24,   25,   26,   27,   28,   15,   15,
+       15,   15,   31,   32,   33,   34,   34,   35,   35,   36,
+       34,   34,   39,   37,   38,   38,   38,   38,   37,   38,
+       38,   38,   38,   38,   38,   64,   38,   38,   46,   64,
+       40,   47,   41,   48,   38,   38,   42,   38,   38,   31,
+       38,   38,   38,   38,   35,   35,  221,   35,   35,  221,
+       65,   43,   38,   38,   44,   65,   49,   50,   38,   38,
+       55,   51,   45,   38,   38,   35,   35,  221,   37,   56,
+
+      221,   52,   66,   37,   67,   67,  221,   53,   38,   38,
+       54,   38,   38,  221,   38,   38,   38,   38,   38,   38,
+       38,   38,  221,   57,   68,   69,   38,   38,   58,   59,
+       38,   38,   38,   38,  221,   38,   38,   71,   70,   38,
+       38,  221,   72,  221,   60,   74,   73,   38,   38,   61,
+       38,   38,   62,   38,   38,   75,   38,   38,   76,   38,
+       38,   77,   38,   38,   38,   38,   81,   38,   38,   38,
+       38,  221,   38,   38,   78,   38,   38,   79,   80,   82,
+      221,   83,   38,   38,   38,   38,  221,   84,   86,   87,
+       85,   38,   38,   88,  221,   90,  221,   38,   38,   67,
+
+       67,   89,   91,   92,  221,   93,   93,   67,   67,   38,
+       38,   38,   38,   38,   38,   38,   38,   94,   38,   38,
+       38,   38,   38,   38,   38,   38,   97,   38,   38,   95,
+       99,  221,   98,  100,  221,   96,   38,   38,   38,   38,
+      101,   38,   38,   38,   38,  221,   38,   38,   38,   38,
+       38,   38,  103,   38,   38,  104,  221,  102,  105,   38,
+       38,  221,  106,  110,  107,  221,   38,   38,   38,   38,
+      109,   38,   38,   38,   38,  108,  111,   38,   38,  113,
+      112,   38,   38,   93,   93,   93,   93,   38,   38,  115,
+       38,   38,  116,  114,   38,   38,  221,  117,   38,   38,
+
+       38,   38,  118,   38,   38,   38,   38,   38,   38,   38,
+       38,  221,   38,   38,   38,   38,  119,   38,   38,   38,
+       38,  122,   38,   38,   38,   38,  221,  126,  121,  123,
+      120,  124,  221,  125,  221,  128,   38,   38,   38,   38,
+       38,   38,  221,  127,   38,   38,   38,   38,   38,   38,
+      129,  132,   38,   38,   38,   38,  221,   38,   38,  130,
+      221,  136,  131,   38,   38,  133,   38,   38,  134,  137,
+      221,  138,  221,  135,   38,   38,   38,   38,   38,   38,
+      141,  140,  221,  139,   38,   38,   38,   38,  142,  145,
+       38,   38,  221,  146,   38,   38,  221,  143,  221,  144,
+
+       38,   38,   38,   38,   38,   38,   38,   38,   38,   38,
+      147,   38,   38,  221,  149,   38,   38,   38,   38,  221,
+       38,   38,  150,  151,  153,  221,  148,   38,   38,   38,
+       38,  152,   38,   38,   38,   38,   38,   38,  221,  156,
+       38,   38,   38,   38,  158,  155,  154,   38,   38,   38,
+       38,  159,   38,   38,   38,   38,  157,  221,   38,   38,
+       38,   38,   38,   38,  160,  164,  163,  221,  161,   38,
+       38,  162,  221,  166,   38,   38,   38,   38,   38,   38,
+      167,  165,   38,   38,   38,   38,   38,   38,   38,   38,
+      168,   38,   38,  221,  170,  221,  171,   38,   38,   38,
+
+       38,   38,   38,   38,   38,  176,  221,  169,   38,   38,
+      172,   38,   38,  174,  178,  177,  173,   38,   38,  221,
+      175,   38,   38,   38,   38,   38,   38,   38,   38,  180,
+      179,  183,  221,  184,   38,   38,  221,   38,   38,  185,
+      181,   38,   38,  221,  182,  186,   38,   38,   38,   38,
+      187,   38,   38,   38,   38,  188,   38,   38,   38,   38,
+       38,   38,  190,   38,   38,   38,   38,   38,   38,  189,
+       38,  194,   38,   38,  221,  193,  221,  191,   38,   38,
+      192,   38,   38,   38,   38,   38,   38,  198,  221,  195,
+       38,   38,   38,   38,  221,  196,   38,   38,   38,   38,
+
+      221,  197,   38,   38,  221,  201,  199,  221,   38,   38,
+      200,  221,  202,  221,  203,  204,   38,   38,   38,   38,
+       38,   38,   38,   38,  221,   38,   38,  221,  207,   38,
+       38,  221,  205,  208,   38,   38,   38,   38,   38,   38,
+       38,   38,  206,  209,   38,   38,   38,   38,   38,   38,
+      221,  210,   38,   38,   38,   38,  211,  221,  212,   38,
+       38,   38,   38,   38,   38,   38,   38,   38,   38,   38,
+       38,  213,  221,   38,   38,   38,   38,  217,   38,   38,
+      221,  214,   38,   38,  215,  218,  216,   38,   38,  221,
+      221,  221,  221,  221,  221,  219,  221,  221,  220,   29,
+
+       29,   30,   30,   63,   63,    7,  221,  221,  221,  221,
+      221,  221,  221,  221,  221,  221,  221,  221,  221,  221,
+      221,  221,  221,  221,  221,  221,  221,  221,  221,  221,
+      221,  221,  221,  221,  221,  221,  221,  221
+    } ;
+
+static yyconst short int yy_chk[739] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,   11,   11,   12,   12,   12,   13,   13,   14,
+       14,   14,  224,   14,   15,   15,   16,   16,   14,   17,
+       17,   19,   19,   20,   20,   63,   21,   21,   19,   32,
+       16,   20,   16,   21,   22,   22,   17,   18,   18,   30,
+       23,   23,   24,   24,   33,   33,    7,   35,   35,    0,
+       35,   18,   26,   26,   18,   35,   22,   23,   38,   38,
+       26,   24,   18,   25,   25,   36,   36,    0,   36,   26,
+
+        0,   25,   37,   36,   37,   37,    0,   25,   39,   39,
+       25,   27,   27,    0,   40,   40,   42,   42,   41,   41,
+       43,   43,    0,   27,   40,   41,   44,   44,   27,   27,
+       28,   28,   45,   45,    0,   46,   46,   43,   42,   47,
+       47,    0,   44,    0,   28,   46,   45,   49,   49,   28,
+       48,   48,   28,   50,   50,   47,   51,   51,   48,   52,
+       52,   49,   53,   53,   55,   55,   53,   54,   54,   56,
+       56,    0,   57,   57,   50,   58,   58,   51,   52,   54,
+        0,   54,   59,   59,   60,   60,    0,   55,   57,   58,
+       56,   61,   61,   59,    0,   61,    0,   62,   62,   66,
+
+       66,   60,   62,   65,    0,   65,   65,   67,   67,   68,
+       68,   69,   69,   70,   70,   71,   71,   68,   72,   72,
+       73,   73,   74,   74,   76,   76,   72,   75,   75,   70,
+       74,    0,   73,   75,    0,   71,   77,   77,   78,   78,
+       76,   79,   79,   80,   80,    0,   81,   81,   82,   82,
+       83,   83,   79,   84,   84,   80,    0,   77,   81,   85,
+       85,    0,   81,   85,   82,    0,   86,   86,   87,   87,
+       84,   88,   88,   89,   89,   83,   86,   90,   90,   88,
+       87,   91,   91,   92,   92,   93,   93,   94,   94,   91,
+       95,   95,   94,   90,   96,   96,    0,   95,   97,   97,
+
+       98,   98,   96,   99,   99,  100,  100,  101,  101,  102,
+      102,    0,  103,  103,  104,  104,   98,  105,  105,  106,
+      106,  102,  107,  107,  108,  108,    0,  106,  101,  103,
+      100,  104,    0,  105,    0,  108,  109,  109,  110,  110,
+      111,  111,    0,  107,  112,  112,  113,  113,  114,  114,
+      109,  112,  115,  115,  116,  116,    0,  117,  117,  110,
+        0,  117,  111,  118,  118,  113,  120,  120,  114,  117,
+        0,  118,    0,  116,  119,  119,  121,  121,  122,  122,
+      120,  119,    0,  118,  123,  123,  124,  124,  121,  123,
+      125,  125,    0,  124,  126,  126,    0,  122,    0,  122,
+
+      127,  127,  128,  128,  129,  129,  130,  130,  131,  131,
+      128,  132,  132,    0,  130,  133,  133,  134,  134,    0,
+      135,  135,  131,  133,  135,    0,  129,  136,  136,  137,
+      137,  134,  138,  138,  139,  139,  140,  140,    0,  138,
+      141,  141,  142,  142,  140,  137,  136,  143,  143,  144,
+      144,  142,  145,  145,  146,  146,  139,    0,  147,  147,
+      148,  148,  149,  149,  143,  147,  146,    0,  144,  150,
+      150,  145,    0,  150,  151,  151,  152,  152,  153,  153,
+      152,  148,  154,  154,  155,  155,  156,  156,  159,  159,
+      153,  157,  157,    0,  155,    0,  156,  158,  158,  160,
+
+      160,  161,  161,  162,  162,  161,    0,  154,  163,  163,
+      157,  164,  164,  159,  163,  162,  158,  165,  165,    0,
+      160,  166,  166,  167,  167,  168,  168,  169,  169,  165,
+      164,  168,    0,  169,  170,  170,    0,  171,  171,  170,
+      166,  172,  172,    0,  167,  171,  173,  173,  174,  174,
+      172,  175,  175,  176,  176,  175,  177,  177,  178,  178,
+      179,  179,  177,  180,  180,  181,  181,  182,  182,  176,
+      183,  183,  184,  184,    0,  182,    0,  179,  185,  185,
+      180,  186,  186,  187,  187,  188,  188,  187,    0,  184,
+      189,  189,  190,  190,    0,  185,  191,  191,  192,  192,
+
+        0,  186,  193,  193,    0,  190,  188,    0,  194,  194,
+      189,    0,  191,    0,  192,  194,  195,  195,  196,  196,
+      197,  197,  198,  198,    0,  199,  199,    0,  198,  200,
+      200,    0,  195,  199,  201,  201,  202,  202,  203,  203,
+      204,  204,  196,  200,  205,  205,  206,  206,  207,  207,
+        0,  205,  208,  208,  209,  209,  207,    0,  209,  210,
+      210,  211,  211,  212,  212,  213,  213,  214,  214,  215,
+      215,  210,    0,  216,  216,  217,  217,  215,  218,  218,
+        0,  211,  219,  219,  212,  216,  213,  220,  220,    0,
+        0,    0,    0,    0,    0,  218,    0,    0,  219,  222,
+
+      222,  223,  223,  225,  225,  221,  221,  221,  221,  221,
+      221,  221,  221,  221,  221,  221,  221,  221,  221,  221,
+      221,  221,  221,  221,  221,  221,  221,  221,  221,  221,
+      221,  221,  221,  221,  221,  221,  221,  221
+    } ;
+
+static yyconst short int yy_rule_linenum[37] =
+    {   0,
+       33,   34,   35,   36,   37,   38,   39,   40,   41,   42,
+       43,   44,   45,   46,   47,   48,   49,   50,   51,   52,
+       54,   55,   56,   58,   59,   60,   61,   62,   63,   65,
+       66,   75,   76,   77,   78,   79
+    } ;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+#line 1 "geometry.ll"
+#define INITIAL 0
+#line 2 "geometry.ll"
+#include <mystdlib.h>
+#include <myadt.hpp> 
+
+#include <linalg.hpp> 
+#include <csg.hpp>
+
+
+
+
+// extern SYMBOLTABLE<Solid*> solids;
+namespace netgen {
+extern CSGeometry * parsegeom;
+}
+using namespace netgen;
+
+#include "geometry.h"
+
+
+ARRAY<char*> parsestrings;
+int linenum;
+#define incl 1
+
+#define comment 2
+
+#line 594 "lex.yy.cc"
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap YY_PROTO(( void ));
+#else
+extern int yywrap YY_PROTO(( void ));
+#endif
+#endif
+
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy YY_PROTO(( char *, yyconst char *, int ));
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen YY_PROTO(( yyconst char * ));
+#endif
+
+#ifndef YY_NO_INPUT
+#endif
+
+#if YY_STACK_USED
+static int yy_start_stack_ptr = 0;
+static int yy_start_stack_depth = 0;
+static int *yy_start_stack = 0;
+#ifndef YY_NO_PUSH_STATE
+static void yy_push_state YY_PROTO(( int new_state ));
+#endif
+#ifndef YY_NO_POP_STATE
+static void yy_pop_state YY_PROTO(( void ));
+#endif
+#ifndef YY_NO_TOP_STATE
+static int yy_top_state YY_PROTO(( void ));
+#endif
+
+#else
+#define YY_NO_PUSH_STATE 1
+#define YY_NO_POP_STATE 1
+#define YY_NO_TOP_STATE 1
+#endif
+
+#ifdef YY_MALLOC_DECL
+YY_MALLOC_DECL
+#else
+#if __STDC__
+#ifndef __cplusplus
+#include <stdlib.h>
+#endif
+#else
+/* Just try to get by without declaring the routines.  This will fail
+ * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int)
+ * or sizeof(void*) != sizeof(int).
+ */
+#endif
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+
+#ifndef ECHO
+#define ECHO LexerOutput( yytext, yyleng )
+#endif
+
+/* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+	if ( (result = LexerInput( (char *) buf, max_size )) < 0 ) \
+		YY_FATAL_ERROR( "input in flex scanner failed" );
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) LexerError( msg )
+#endif
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL int yyFlexLexer::yylex()
+#endif
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+	YY_USER_ACTION
+
+YY_DECL
+	{
+	register yy_state_type yy_current_state;
+	register char *yy_cp, *yy_bp;
+	register int yy_act;
+
+#line 32 "geometry.ll"
+
+#line 723 "lex.yy.cc"
+
+	if ( yy_init )
+		{
+		yy_init = 0;
+
+#ifdef YY_USER_INIT
+		YY_USER_INIT;
+#endif
+
+		if ( ! yy_start )
+			yy_start = 1;	/* first start state */
+
+		if ( ! yyin )
+			yyin = &cin;
+
+		if ( ! yyout )
+			yyout = &cout;
+
+		if ( ! yy_current_buffer )
+			yy_current_buffer =
+				yy_create_buffer( yyin, YY_BUF_SIZE );
+
+		yy_load_buffer_state();
+		}
+
+	while ( 1 )		/* loops until end-of-file is reached */
+		{
+		yy_cp = yy_c_buf_p;
+
+		/* Support of yytext. */
+		*yy_cp = yy_hold_char;
+
+		/* yy_bp points to the position in yy_ch_buf of the start of
+		 * the current run.
+		 */
+		yy_bp = yy_cp;
+
+		yy_current_state = yy_start;
+yy_match:
+		do
+			{
+			register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+			if ( yy_accept[yy_current_state] )
+				{
+				yy_last_accepting_state = yy_current_state;
+				yy_last_accepting_cpos = yy_cp;
+				}
+			while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+				{
+				yy_current_state = (int) yy_def[yy_current_state];
+				if ( yy_current_state >= 222 )
+					yy_c = yy_meta[(unsigned int) yy_c];
+				}
+			yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+			++yy_cp;
+			}
+		while ( yy_base[yy_current_state] != 706 );
+
+yy_find_action:
+		yy_act = yy_accept[yy_current_state];
+		if ( yy_act == 0 )
+			{ /* have to back up */
+			yy_cp = yy_last_accepting_cpos;
+			yy_current_state = yy_last_accepting_state;
+			yy_act = yy_accept[yy_current_state];
+			}
+
+		YY_DO_BEFORE_ACTION;
+
+
+do_action:	/* This label is used only to access EOF actions. */
+
+		if ( yy_flex_debug )
+			{
+			if ( yy_act == 0 )
+				cerr << "--scanner backing up\n";
+			else if ( yy_act < 37 )
+				cerr << "--accepting rule at line " << yy_rule_linenum[yy_act] <<
+				         "(\"" << yytext << "\")\n";
+			else if ( yy_act == 37 )
+				cerr << "--accepting default rule (\"" << yytext << "\")\n";
+			else if ( yy_act == 38 )
+				cerr << "--(end of buffer or a NUL)\n";
+			else
+				cerr << "--EOF (start condition " << YY_START << ")\n";
+			}
+
+		switch ( yy_act )
+	{ /* beginning of action switch */
+			case 0: /* must back up */
+			/* undo the effects of YY_DO_BEFORE_ACTION */
+			*yy_cp = yy_hold_char;
+			yy_cp = yy_last_accepting_cpos;
+			yy_current_state = yy_last_accepting_state;
+			goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 33 "geometry.ll"
+{ return TOK_RECO; }
+	YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 34 "geometry.ll"
+{ return TOK_SOLID; }
+	YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 35 "geometry.ll"
+{ return TOK_TLO; }
+	YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 36 "geometry.ll"
+{ return TOK_AND; }
+	YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 37 "geometry.ll"
+{ return TOK_OR; }
+	YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 38 "geometry.ll"
+{ return TOK_NOT; }
+	YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 39 "geometry.ll"
+{ return TOK_TRANSLATE; }
+	YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 40 "geometry.ll"
+{ return TOK_MULTITRANSLATE; }
+	YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 41 "geometry.ll"
+{ return TOK_ROTATE; }
+	YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 42 "geometry.ll"
+{ return TOK_MULTIROTATE; }
+	YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 43 "geometry.ll"
+{ return TOK_SPHERE; }
+	YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 44 "geometry.ll"
+{ return TOK_CYLINDER; }
+	YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 45 "geometry.ll"
+{ return TOK_CONE; }
+	YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 46 "geometry.ll"
+{ return TOK_PLAIN; }
+	YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 47 "geometry.ll"
+{ return TOK_PLAIN; }
+	YY_BREAK
+case 16:
+YY_RULE_SETUP
+#line 48 "geometry.ll"
+{ return TOK_TUBE; }
+	YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 49 "geometry.ll"
+{ return TOK_GENCYL; }
+	YY_BREAK
+case 18:
+YY_RULE_SETUP
+#line 50 "geometry.ll"
+{ return TOK_ORTHOBRICK; }
+	YY_BREAK
+case 19:
+YY_RULE_SETUP
+#line 51 "geometry.ll"
+{ return TOK_POLYHEDRON; }
+	YY_BREAK
+case 20:
+YY_RULE_SETUP
+#line 52 "geometry.ll"
+{ return TOK_REVOLUTION; }
+	YY_BREAK
+case 21:
+YY_RULE_SETUP
+#line 54 "geometry.ll"
+{ return TOK_SINGULAR; }
+	YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 55 "geometry.ll"
+{ return TOK_EDGE; }
+	YY_BREAK
+case 23:
+YY_RULE_SETUP
+#line 56 "geometry.ll"
+{ return TOK_POINT; }
+	YY_BREAK
+case 24:
+YY_RULE_SETUP
+#line 58 "geometry.ll"
+{ return TOK_IDENTIFY; }
+	YY_BREAK
+case 25:
+YY_RULE_SETUP
+#line 59 "geometry.ll"
+{ return TOK_CLOSESURFACES; }
+	YY_BREAK
+case 26:
+YY_RULE_SETUP
+#line 60 "geometry.ll"
+{ return TOK_CLOSEEDGES; }
+	YY_BREAK
+case 27:
+YY_RULE_SETUP
+#line 61 "geometry.ll"
+{ return TOK_PERIODIC; }
+	YY_BREAK
+case 28:
+YY_RULE_SETUP
+#line 62 "geometry.ll"
+{ return TOK_BOUNDARYCONDITION; }
+	YY_BREAK
+case 29:
+YY_RULE_SETUP
+#line 63 "geometry.ll"
+{ return TOK_BOUNDINGBOX; }
+	YY_BREAK
+case 30:
+YY_RULE_SETUP
+#line 65 "geometry.ll"
+{ yylval.val = atof (YYText()); return NUM; }
+	YY_BREAK
+case 31:
+YY_RULE_SETUP
+#line 66 "geometry.ll"
+{
+                  yylval.chptr = new char [YYLeng()+1];
+		  parsestrings.Append (yylval.chptr);
+                  strcpy (yylval.chptr, YYText());
+                  if (parsegeom->GetSolid (yylval.chptr))
+                    return IDENTSOLID;
+                  else
+                    return IDENT;
+                }
+	YY_BREAK
+case 32:
+YY_RULE_SETUP
+#line 75 "geometry.ll"
+/* eat up ws */
+	YY_BREAK
+case 33:
+YY_RULE_SETUP
+#line 76 "geometry.ll"
+{ return int(*YYText()); }
+	YY_BREAK
+case 34:
+YY_RULE_SETUP
+#line 77 "geometry.ll"
+{ linenum++; }
+	YY_BREAK
+case 35:
+YY_RULE_SETUP
+#line 78 "geometry.ll"
+{ linenum++; cout << (YYText()+2) ; }  /* line comment */
+	YY_BREAK
+case 36:
+YY_RULE_SETUP
+#line 79 "geometry.ll"
+{ linenum++; }  /* line comment */
+	YY_BREAK
+case 37:
+YY_RULE_SETUP
+#line 82 "geometry.ll"
+ECHO;
+	YY_BREAK
+#line 1013 "lex.yy.cc"
+case YY_STATE_EOF(INITIAL):
+case YY_STATE_EOF(incl):
+case YY_STATE_EOF(comment):
+	yyterminate();
+
+	case YY_END_OF_BUFFER:
+		{
+		/* Amount of text matched not including the EOB char. */
+		int yy_amount_of_matched_text = (int) (yy_cp - yytext_ptr) - 1;
+
+		/* Undo the effects of YY_DO_BEFORE_ACTION. */
+		*yy_cp = yy_hold_char;
+		YY_RESTORE_YY_MORE_OFFSET
+
+		if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_NEW )
+			{
+			/* We're scanning a new file or input source.  It's
+			 * possible that this happened because the user
+			 * just pointed yyin at a new source and called
+			 * yylex().  If so, then we have to assure
+			 * consistency between yy_current_buffer and our
+			 * globals.  Here is the right place to do so, because
+			 * this is the first action (other than possibly a
+			 * back-up) that will match for the new input source.
+			 */
+			yy_n_chars = yy_current_buffer->yy_n_chars;
+			yy_current_buffer->yy_input_file = yyin;
+			yy_current_buffer->yy_buffer_status = YY_BUFFER_NORMAL;
+			}
+
+		/* Note that here we test for yy_c_buf_p "<=" to the position
+		 * of the first EOB in the buffer, since yy_c_buf_p will
+		 * already have been incremented past the NUL character
+		 * (since all states make transitions on EOB to the
+		 * end-of-buffer state).  Contrast this with the test
+		 * in input().
+		 */
+		if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] )
+			{ /* This was really a NUL. */
+			yy_state_type yy_next_state;
+
+			yy_c_buf_p = yytext_ptr + yy_amount_of_matched_text;
+
+			yy_current_state = yy_get_previous_state();
+
+			/* Okay, we're now positioned to make the NUL
+			 * transition.  We couldn't have
+			 * yy_get_previous_state() go ahead and do it
+			 * for us because it doesn't know how to deal
+			 * with the possibility of jamming (and we don't
+			 * want to build jamming into it because then it
+			 * will run more slowly).
+			 */
+
+			yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+			yy_bp = yytext_ptr + YY_MORE_ADJ;
+
+			if ( yy_next_state )
+				{
+				/* Consume the NUL. */
+				yy_cp = ++yy_c_buf_p;
+				yy_current_state = yy_next_state;
+				goto yy_match;
+				}
+
+			else
+				{
+				yy_cp = yy_c_buf_p;
+				goto yy_find_action;
+				}
+			}
+
+		else switch ( yy_get_next_buffer() )
+			{
+			case EOB_ACT_END_OF_FILE:
+				{
+				yy_did_buffer_switch_on_eof = 0;
+
+				if ( yywrap() )
+					{
+					/* Note: because we've taken care in
+					 * yy_get_next_buffer() to have set up
+					 * yytext, we can now set up
+					 * yy_c_buf_p so that if some total
+					 * hoser (like flex itself) wants to
+					 * call the scanner after we return the
+					 * YY_NULL, it'll still work - another
+					 * YY_NULL will get returned.
+					 */
+					yy_c_buf_p = yytext_ptr + YY_MORE_ADJ;
+
+					yy_act = YY_STATE_EOF(YY_START);
+					goto do_action;
+					}
+
+				else
+					{
+					if ( ! yy_did_buffer_switch_on_eof )
+						YY_NEW_FILE;
+					}
+				break;
+				}
+
+			case EOB_ACT_CONTINUE_SCAN:
+				yy_c_buf_p =
+					yytext_ptr + yy_amount_of_matched_text;
+
+				yy_current_state = yy_get_previous_state();
+
+				yy_cp = yy_c_buf_p;
+				yy_bp = yytext_ptr + YY_MORE_ADJ;
+				goto yy_match;
+
+			case EOB_ACT_LAST_MATCH:
+				yy_c_buf_p =
+				&yy_current_buffer->yy_ch_buf[yy_n_chars];
+
+				yy_current_state = yy_get_previous_state();
+
+				yy_cp = yy_c_buf_p;
+				yy_bp = yytext_ptr + YY_MORE_ADJ;
+				goto yy_find_action;
+			}
+		break;
+		}
+
+	default:
+		YY_FATAL_ERROR(
+			"fatal flex scanner internal error--no action found" );
+	} /* end of action switch */
+		} /* end of scanning one token */
+	} /* end of yylex */
+
+yyFlexLexer::yyFlexLexer( istream* arg_yyin, ostream* arg_yyout )
+	{
+	yyin = arg_yyin;
+	yyout = arg_yyout;
+	yy_c_buf_p = 0;
+	yy_init = 1;
+	yy_start = 0;
+	yy_flex_debug = 0;
+	yylineno = 1;	// this will only get updated if %option yylineno
+
+	yy_did_buffer_switch_on_eof = 0;
+
+	yy_looking_for_trail_begin = 0;
+	yy_more_flag = 0;
+	yy_more_len = 0;
+	yy_more_offset = yy_prev_more_offset = 0;
+
+	yy_start_stack_ptr = yy_start_stack_depth = 0;
+	yy_start_stack = 0;
+
+	yy_current_buffer = 0;
+
+#ifdef YY_USES_REJECT
+	yy_state_buf = new yy_state_type[YY_BUF_SIZE + 2];
+#else
+	yy_state_buf = 0;
+#endif
+	}
+
+yyFlexLexer::~yyFlexLexer()
+	{
+	delete yy_state_buf;
+	yy_delete_buffer( yy_current_buffer );
+	}
+
+void yyFlexLexer::switch_streams( istream* new_in, ostream* new_out )
+	{
+	if ( new_in )
+		{
+		yy_delete_buffer( yy_current_buffer );
+		yy_switch_to_buffer( yy_create_buffer( new_in, YY_BUF_SIZE ) );
+		}
+
+	if ( new_out )
+		yyout = new_out;
+	}
+
+#ifdef YY_INTERACTIVE
+int yyFlexLexer::LexerInput( char* buf, int /* max_size */ )
+#else
+int yyFlexLexer::LexerInput( char* buf, int max_size )
+#endif
+	{
+	if ( yyin->eof() || yyin->fail() )
+		return 0;
+
+#ifdef YY_INTERACTIVE
+	yyin->get( buf[0] );
+
+	if ( yyin->eof() )
+		return 0;
+
+	if ( yyin->bad() )
+		return -1;
+
+	return 1;
+
+#else
+	(void) yyin->read( buf, max_size );
+
+	if ( yyin->bad() )
+		return -1;
+	else
+		return yyin->gcount();
+#endif
+	}
+
+void yyFlexLexer::LexerOutput( const char* buf, int size )
+	{
+	(void) yyout->write( buf, size );
+	}
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ *	EOB_ACT_LAST_MATCH -
+ *	EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ *	EOB_ACT_END_OF_FILE - end of file
+ */
+
+int yyFlexLexer::yy_get_next_buffer()
+	{
+	register char *dest = yy_current_buffer->yy_ch_buf;
+	register char *source = yytext_ptr;
+	register int number_to_move, i;
+	int ret_val;
+
+	if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] )
+		YY_FATAL_ERROR(
+		"fatal flex scanner internal error--end of buffer missed" );
+
+	if ( yy_current_buffer->yy_fill_buffer == 0 )
+		{ /* Don't try to fill the buffer, so this is an EOF. */
+		if ( yy_c_buf_p - yytext_ptr - YY_MORE_ADJ == 1 )
+			{
+			/* We matched a single character, the EOB, so
+			 * treat this as a final EOF.
+			 */
+			return EOB_ACT_END_OF_FILE;
+			}
+
+		else
+			{
+			/* We matched some text prior to the EOB, first
+			 * process it.
+			 */
+			return EOB_ACT_LAST_MATCH;
+			}
+		}
+
+	/* Try to read more data. */
+
+	/* First move last chars to start of buffer. */
+	number_to_move = (int) (yy_c_buf_p - yytext_ptr) - 1;
+
+	for ( i = 0; i < number_to_move; ++i )
+		*(dest++) = *(source++);
+
+	if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+		/* don't do the read, it's not guaranteed to return an EOF,
+		 * just force an EOF
+		 */
+		yy_current_buffer->yy_n_chars = yy_n_chars = 0;
+
+	else
+		{
+		int num_to_read =
+			yy_current_buffer->yy_buf_size - number_to_move - 1;
+
+		while ( num_to_read <= 0 )
+			{ /* Not enough room in the buffer - grow it. */
+#ifdef YY_USES_REJECT
+			YY_FATAL_ERROR(
+"input buffer overflow, can't enlarge buffer because scanner uses REJECT" );
+#else
+
+			/* just a shorter name for the current buffer */
+			YY_BUFFER_STATE b = yy_current_buffer;
+
+			int yy_c_buf_p_offset =
+				(int) (yy_c_buf_p - b->yy_ch_buf);
+
+			if ( b->yy_is_our_buffer )
+				{
+				int new_size = b->yy_buf_size * 2;
+
+				if ( new_size <= 0 )
+					b->yy_buf_size += b->yy_buf_size / 8;
+				else
+					b->yy_buf_size *= 2;
+
+				b->yy_ch_buf = (char *)
+					/* Include room in for 2 EOB chars. */
+					yy_flex_realloc( (void *) b->yy_ch_buf,
+							 b->yy_buf_size + 2 );
+				}
+			else
+				/* Can't grow it, we don't own it. */
+				b->yy_ch_buf = 0;
+
+			if ( ! b->yy_ch_buf )
+				YY_FATAL_ERROR(
+				"fatal error - scanner input buffer overflow" );
+
+			yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+			num_to_read = yy_current_buffer->yy_buf_size -
+						number_to_move - 1;
+#endif
+			}
+
+		if ( num_to_read > YY_READ_BUF_SIZE )
+			num_to_read = YY_READ_BUF_SIZE;
+
+		/* Read in more data. */
+		YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]),
+			yy_n_chars, num_to_read );
+
+		yy_current_buffer->yy_n_chars = yy_n_chars;
+		}
+
+	if ( yy_n_chars == 0 )
+		{
+		if ( number_to_move == YY_MORE_ADJ )
+			{
+			ret_val = EOB_ACT_END_OF_FILE;
+			yyrestart( yyin );
+			}
+
+		else
+			{
+			ret_val = EOB_ACT_LAST_MATCH;
+			yy_current_buffer->yy_buffer_status =
+				YY_BUFFER_EOF_PENDING;
+			}
+		}
+
+	else
+		ret_val = EOB_ACT_CONTINUE_SCAN;
+
+	yy_n_chars += number_to_move;
+	yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+	yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+	yytext_ptr = &yy_current_buffer->yy_ch_buf[0];
+
+	return ret_val;
+	}
+
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+yy_state_type yyFlexLexer::yy_get_previous_state()
+	{
+	register yy_state_type yy_current_state;
+	register char *yy_cp;
+
+	yy_current_state = yy_start;
+
+	for ( yy_cp = yytext_ptr + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp )
+		{
+		register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+		if ( yy_accept[yy_current_state] )
+			{
+			yy_last_accepting_state = yy_current_state;
+			yy_last_accepting_cpos = yy_cp;
+			}
+		while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+			{
+			yy_current_state = (int) yy_def[yy_current_state];
+			if ( yy_current_state >= 222 )
+				yy_c = yy_meta[(unsigned int) yy_c];
+			}
+		yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+		}
+
+	return yy_current_state;
+	}
+
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ *	next_state = yy_try_NUL_trans( current_state );
+ */
+
+yy_state_type yyFlexLexer::yy_try_NUL_trans( yy_state_type yy_current_state )
+	{
+	register int yy_is_jam;
+	register char *yy_cp = yy_c_buf_p;
+
+	register YY_CHAR yy_c = 1;
+	if ( yy_accept[yy_current_state] )
+		{
+		yy_last_accepting_state = yy_current_state;
+		yy_last_accepting_cpos = yy_cp;
+		}
+	while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+		{
+		yy_current_state = (int) yy_def[yy_current_state];
+		if ( yy_current_state >= 222 )
+			yy_c = yy_meta[(unsigned int) yy_c];
+		}
+	yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+	yy_is_jam = (yy_current_state == 221);
+
+	return yy_is_jam ? 0 : yy_current_state;
+	}
+
+
+void yyFlexLexer::yyunput( int c, register char* yy_bp )
+	{
+	register char *yy_cp = yy_c_buf_p;
+
+	/* undo effects of setting up yytext */
+	*yy_cp = yy_hold_char;
+
+	if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )
+		{ /* need to shift things up to make room */
+		/* +2 for EOB chars. */
+		register int number_to_move = yy_n_chars + 2;
+		register char *dest = &yy_current_buffer->yy_ch_buf[
+					yy_current_buffer->yy_buf_size + 2];
+		register char *source =
+				&yy_current_buffer->yy_ch_buf[number_to_move];
+
+		while ( source > yy_current_buffer->yy_ch_buf )
+			*--dest = *--source;
+
+		yy_cp += (int) (dest - source);
+		yy_bp += (int) (dest - source);
+		yy_current_buffer->yy_n_chars =
+			yy_n_chars = yy_current_buffer->yy_buf_size;
+
+		if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )
+			YY_FATAL_ERROR( "flex scanner push-back overflow" );
+		}
+
+	*--yy_cp = (char) c;
+
+
+	yytext_ptr = yy_bp;
+	yy_hold_char = *yy_cp;
+	yy_c_buf_p = yy_cp;
+	}
+
+
+int yyFlexLexer::yyinput()
+	{
+	int c;
+
+	*yy_c_buf_p = yy_hold_char;
+
+	if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+		{
+		/* yy_c_buf_p now points to the character we want to return.
+		 * If this occurs *before* the EOB characters, then it's a
+		 * valid NUL; if not, then we've hit the end of the buffer.
+		 */
+		if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] )
+			/* This was really a NUL. */
+			*yy_c_buf_p = '\0';
+
+		else
+			{ /* need more input */
+			int offset = yy_c_buf_p - yytext_ptr;
+			++yy_c_buf_p;
+
+			switch ( yy_get_next_buffer() )
+				{
+				case EOB_ACT_LAST_MATCH:
+					/* This happens because yy_g_n_b()
+					 * sees that we've accumulated a
+					 * token and flags that we need to
+					 * try matching the token before
+					 * proceeding.  But for input(),
+					 * there's no matching to consider.
+					 * So convert the EOB_ACT_LAST_MATCH
+					 * to EOB_ACT_END_OF_FILE.
+					 */
+
+					/* Reset buffer status. */
+					yyrestart( yyin );
+
+					/* fall through */
+
+				case EOB_ACT_END_OF_FILE:
+					{
+					if ( yywrap() )
+						return EOF;
+
+					if ( ! yy_did_buffer_switch_on_eof )
+						YY_NEW_FILE;
+#ifdef __cplusplus
+					return yyinput();
+#else
+					return input();
+#endif
+					}
+
+				case EOB_ACT_CONTINUE_SCAN:
+					yy_c_buf_p = yytext_ptr + offset;
+					break;
+				}
+			}
+		}
+
+	c = *(unsigned char *) yy_c_buf_p;	/* cast for 8-bit char's */
+	*yy_c_buf_p = '\0';	/* preserve yytext */
+	yy_hold_char = *++yy_c_buf_p;
+
+
+	return c;
+	}
+
+
+void yyFlexLexer::yyrestart( istream* input_file )
+	{
+	if ( ! yy_current_buffer )
+		yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE );
+
+	yy_init_buffer( yy_current_buffer, input_file );
+	yy_load_buffer_state();
+	}
+
+
+void yyFlexLexer::yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )
+	{
+	if ( yy_current_buffer == new_buffer )
+		return;
+
+	if ( yy_current_buffer )
+		{
+		/* Flush out information for old buffer. */
+		*yy_c_buf_p = yy_hold_char;
+		yy_current_buffer->yy_buf_pos = yy_c_buf_p;
+		yy_current_buffer->yy_n_chars = yy_n_chars;
+		}
+
+	yy_current_buffer = new_buffer;
+	yy_load_buffer_state();
+
+	/* We don't actually know whether we did this switch during
+	 * EOF (yywrap()) processing, but the only time this flag
+	 * is looked at is after yywrap() is called, so it's safe
+	 * to go ahead and always set it.
+	 */
+	yy_did_buffer_switch_on_eof = 1;
+	}
+
+
+void yyFlexLexer::yy_load_buffer_state()
+	{
+	yy_n_chars = yy_current_buffer->yy_n_chars;
+	yytext_ptr = yy_c_buf_p = yy_current_buffer->yy_buf_pos;
+	yyin = yy_current_buffer->yy_input_file;
+	yy_hold_char = *yy_c_buf_p;
+	}
+
+
+YY_BUFFER_STATE yyFlexLexer::yy_create_buffer( istream* file, int size )
+	{
+	YY_BUFFER_STATE b;
+
+	b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+	b->yy_buf_size = size;
+
+	/* yy_ch_buf has to be 2 characters longer than the size given because
+	 * we need to put in 2 end-of-buffer characters.
+	 */
+	b->yy_ch_buf = (char *) yy_flex_alloc( b->yy_buf_size + 2 );
+	if ( ! b->yy_ch_buf )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+	b->yy_is_our_buffer = 1;
+
+	yy_init_buffer( b, file );
+
+	return b;
+	}
+
+
+void yyFlexLexer::yy_delete_buffer( YY_BUFFER_STATE b )
+	{
+	if ( ! b )
+		return;
+
+	if ( b == yy_current_buffer )
+		yy_current_buffer = (YY_BUFFER_STATE) 0;
+
+	if ( b->yy_is_our_buffer )
+		yy_flex_free( (void *) b->yy_ch_buf );
+
+	yy_flex_free( (void *) b );
+	}
+
+
+#include<unistd.h>
+void yyFlexLexer::yy_init_buffer( YY_BUFFER_STATE b, istream* file )
+
+	{
+	yy_flush_buffer( b );
+
+	b->yy_input_file = file;
+	b->yy_fill_buffer = 1;
+
+	b->yy_is_interactive = 0;
+	}
+
+
+void yyFlexLexer::yy_flush_buffer( YY_BUFFER_STATE b )
+	{
+	if ( ! b )
+		return;
+
+	b->yy_n_chars = 0;
+
+	/* We always need two end-of-buffer characters.  The first causes
+	 * a transition to the end-of-buffer state.  The second causes
+	 * a jam in that state.
+	 */
+	b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+	b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+	b->yy_buf_pos = &b->yy_ch_buf[0];
+
+	b->yy_at_bol = 1;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	if ( b == yy_current_buffer )
+		yy_load_buffer_state();
+	}
+
+
+#ifndef YY_NO_SCAN_BUFFER
+#endif
+
+
+#ifndef YY_NO_SCAN_STRING
+#endif
+
+
+#ifndef YY_NO_SCAN_BYTES
+#endif
+
+
+#ifndef YY_NO_PUSH_STATE
+void yyFlexLexer::yy_push_state( int new_state )
+	{
+	if ( yy_start_stack_ptr >= yy_start_stack_depth )
+		{
+		yy_size_t new_size;
+
+		yy_start_stack_depth += YY_START_STACK_INCR;
+		new_size = yy_start_stack_depth * sizeof( int );
+
+		if ( ! yy_start_stack )
+			yy_start_stack = (int *) yy_flex_alloc( new_size );
+
+		else
+			yy_start_stack = (int *) yy_flex_realloc(
+					(void *) yy_start_stack, new_size );
+
+		if ( ! yy_start_stack )
+			YY_FATAL_ERROR(
+			"out of memory expanding start-condition stack" );
+		}
+
+	yy_start_stack[yy_start_stack_ptr++] = YY_START;
+
+	BEGIN(new_state);
+	}
+#endif
+
+
+#ifndef YY_NO_POP_STATE
+void yyFlexLexer::yy_pop_state()
+	{
+	if ( --yy_start_stack_ptr < 0 )
+		YY_FATAL_ERROR( "start-condition stack underflow" );
+
+	BEGIN(yy_start_stack[yy_start_stack_ptr]);
+	}
+#endif
+
+
+#ifndef YY_NO_TOP_STATE
+int yyFlexLexer::yy_top_state()
+	{
+	return yy_start_stack[yy_start_stack_ptr - 1];
+	}
+#endif
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+
+void yyFlexLexer::LexerError( yyconst char msg[] )
+	{
+	cerr << msg << '\n';
+	exit( YY_EXIT_FAILURE );
+	}
+
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+		yytext[yyleng] = yy_hold_char; \
+		yy_c_buf_p = yytext + n; \
+		yy_hold_char = *yy_c_buf_p; \
+		*yy_c_buf_p = '\0'; \
+		yyleng = n; \
+		} \
+	while ( 0 )
+
+
+/* Internal utility routines. */
+
+#ifndef yytext_ptr
+#ifdef YY_USE_PROTOS
+static void yy_flex_strncpy( char *s1, yyconst char *s2, int n )
+#else
+static void yy_flex_strncpy( s1, s2, n )
+char *s1;
+yyconst char *s2;
+int n;
+#endif
+	{
+	register int i;
+	for ( i = 0; i < n; ++i )
+		s1[i] = s2[i];
+	}
+#endif
+
+#ifdef YY_NEED_STRLEN
+#ifdef YY_USE_PROTOS
+static int yy_flex_strlen( yyconst char *s )
+#else
+static int yy_flex_strlen( s )
+yyconst char *s;
+#endif
+	{
+	register int n;
+	for ( n = 0; s[n]; ++n )
+		;
+
+	return n;
+	}
+#endif
+
+
+#ifdef YY_USE_PROTOS
+static void *yy_flex_alloc( yy_size_t size )
+#else
+static void *yy_flex_alloc( size )
+yy_size_t size;
+#endif
+	{
+	return (void *) malloc( size );
+	}
+
+#ifdef YY_USE_PROTOS
+static void *yy_flex_realloc( void *ptr, yy_size_t size )
+#else
+static void *yy_flex_realloc( ptr, size )
+void *ptr;
+yy_size_t size;
+#endif
+	{
+	/* The cast to (char *) in the following accommodates both
+	 * implementations that use char* generic pointers, and those
+	 * that use void* generic pointers.  It works with the latter
+	 * because both ANSI C and C++ allow castless assignment from
+	 * any pointer type to void*, and deal with argument conversions
+	 * as though doing an assignment.
+	 */
+	return (void *) realloc( (char *) ptr, size );
+	}
+
+#ifdef YY_USE_PROTOS
+static void yy_flex_free( void *ptr )
+#else
+static void yy_flex_free( ptr )
+void *ptr;
+#endif
+	{
+	free( ptr );
+	}
+
+#if YY_MAIN
+int main()
+	{
+	yylex();
+	return 0;
+	}
+#endif
+#line 82 "geometry.ll"
+
+
+extern FlexLexer * lexer;
+
+int yylex ()
+  {
+  return lexer -> yylex();
+  }
+
+extern "C" int yywrap ()
+  {
+  return 1;
+  }
diff --git a/contrib/Netgen/libsrc/csg/manifold.cpp b/contrib/Netgen/libsrc/csg/manifold.cpp
new file mode 100644
index 0000000000..9733389d67
--- /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 0000000000..5deb7236a7
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/manifold.hpp
@@ -0,0 +1,22 @@
+#ifndef FILE_MANIFOLD
+#define FILE_MANIFOLD
+
+/**************************************************************************/
+/* File:   manifold.hh                                                    */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   7. Aug. 96                                                     */
+/**************************************************************************/
+
+/**
+  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 0000000000..0a7d7c74a0
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/meshsurf.cpp
@@ -0,0 +1,178 @@
+#include <mystdlib.h>
+
+#include <csg.hpp>
+#include <meshing.hpp>
+
+
+
+namespace netgen
+{
+  /*
+Meshing2Surfaces :: Meshing2Surfaces (const Surface & asurface)
+  : surface(asurface)
+{
+  ;
+}
+  */
+Meshing2Surfaces :: Meshing2Surfaces (const Surface & asurf,
+				      const Box<3> & abb)
+  : Meshing2(Box3d(abb.PMin(), abb.PMax())), surface(asurf)
+{
+  ;
+}
+
+
+void Meshing2Surfaces :: DefineTransformation (Point3d & p1, 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, Point3d & p) const
+{
+  Point<3> hp = p;
+  geometry.GetSurface(surfind)->Project (hp);
+  p = hp;
+}
+
+void MeshOptimize2dSurfaces :: ProjectPoint2 (INDEX surfind, INDEX surfind2, 
+					      Point3d & p) const
+{
+  Point<3> hp = p;
+  ProjectToEdge ( geometry.GetSurface(surfind), 
+		  geometry.GetSurface(surfind2), hp);
+  p = hp;
+}
+
+
+void MeshOptimize2dSurfaces :: 
+GetNormalVector(INDEX surfind, const Point3d & p, Vec3d & 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)
+{
+  ;
+}
+
+RefinementSurfaces :: ~RefinementSurfaces ()
+{
+  ;
+}
+  
+void RefinementSurfaces :: 
+PointBetween (const Point3d & p1, const Point3d & p2, double secpoint,
+	      int surfi, 
+	      const PointGeomInfo & gi1, 
+	      const PointGeomInfo & gi2,
+	      Point3d & newp, PointGeomInfo & newgi)
+{
+  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 Point3d & p1, const Point3d & p2, double secpoint,
+	      int surfi1, int surfi2, 
+	      const EdgePointGeomInfo & ap1, 
+	      const EdgePointGeomInfo & ap2,
+	      Point3d & newp, EdgePointGeomInfo & newgi)
+{
+  Point<3> hnewp = p1+secpoint*(p2-p1);
+  if (surfi1 != -1 && surfi2 != -1 && surfi1 != surfi2)
+    {
+      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;
+    }
+  else if (surfi1 != -1)
+    {
+      geometry.GetSurface (surfi1) -> Project (hnewp);
+    }
+
+  newp = hnewp;
+}
+
+
+void RefinementSurfaces :: ProjectToSurface (Point<3> & p, int surfi)
+{
+  if (surfi != -1)
+    geometry.GetSurface (surfi) -> Project (p);
+}
+}
diff --git a/contrib/Netgen/libsrc/csg/meshsurf.hpp b/contrib/Netgen/libsrc/csg/meshsurf.hpp
new file mode 100644
index 0000000000..023c2eef70
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/meshsurf.hpp
@@ -0,0 +1,85 @@
+#ifndef FILE_MESHSURF
+#define FILE_MESHSURF
+
+///
+class Meshing2Surfaces : public Meshing2
+{
+  ///
+  const Surface & surface;
+  
+public:
+  ///
+  //  Meshing2Surfaces (const Surface & asurf);
+  ///
+  Meshing2Surfaces (const Surface & asurf, const Box<3> & aboundingbox);
+
+protected:
+  ///
+  virtual void DefineTransformation (Point3d & p1, 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, Point3d & p) const;
+    ///
+    virtual void ProjectPoint2 (INDEX surfind, INDEX surfind2, Point3d & p) const;
+    ///
+    virtual void GetNormalVector(INDEX surfind, const Point3d & p, Vec3d & n) const;
+};
+
+
+
+
+
+class RefinementSurfaces : public Refinement
+{
+  const CSGeometry & geometry;
+
+public:
+  RefinementSurfaces (const CSGeometry & ageometry);
+  virtual ~RefinementSurfaces ();
+  
+  virtual void PointBetween (const Point3d & p1, const Point3d & p2, double secpoint,
+			     int surfi, 
+			     const PointGeomInfo & gi1, 
+			     const PointGeomInfo & gi2,
+			     Point3d & newp, PointGeomInfo & newgi);
+
+  virtual void PointBetween (const Point3d & p1, const Point3d & p2, double secpoint,
+			     int surfi1, int surfi2, 
+			     const EdgePointGeomInfo & ap1, 
+			     const EdgePointGeomInfo & ap2,
+			     Point3d & newp, EdgePointGeomInfo & newgi);
+
+  virtual void ProjectToSurface (Point<3> & p, int surfi);
+};
+
+
+
+#endif
+
diff --git a/contrib/Netgen/libsrc/csg/polyhedra.cpp b/contrib/Netgen/libsrc/csg/polyhedra.cpp
new file mode 100644
index 0000000000..f0ce2f63f5
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/polyhedra.cpp
@@ -0,0 +1,358 @@
+#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)
+{
+  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);
+}
+
+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;
+
+      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;
+
+      double dist2 = MinDistTP2 (p1, p2, p3, box.Center());
+      if (dist2 < sqr (box.Diam()/2))
+	return DOES_INTERSECT;
+    };
+
+  return PointInSolid (box.Center(), 1e-3 * box.Diam());
+}
+
+
+INSOLID_TYPE Polyhedra :: PointInSolid (const Point<3> & p,
+					double eps) const
+{
+  Vec<3> n, v1, v2;
+
+  // random (?) numbers:
+  n(0) = 0.123871;
+  n(1) = 0.15432;
+  n(2) = 0.43989;
+
+  int cnt = 0;
+  Point<3> pmeps (p(0) - eps, p(1) - eps, p(2) - eps);
+
+  for (int i = 0; i < faces.Size(); i++)
+    {
+      const Point<3> & fpmax = faces[i].bbox.PMax();
+      if (fpmax(0) < pmeps(0) ||
+	  fpmax(1) < pmeps(1) ||
+	  fpmax(2) < pmeps(2)) continue;
+
+      const Point<3> & p1 = points[faces[i].pnums[0]];
+      
+      Vec<3> v0 = p - p1;
+      double lam3 = -(faces[i].n * v0) / (faces[i].n * n);
+
+      if (lam3 < -eps) continue;
+      Vec<3> rs = v0 + lam3 * n;
+
+      double lam1 = (faces[i].w1 * rs);
+      double lam2 = (faces[i].w2 * rs);
+
+      if (lam3 < eps)
+	{
+	  if (lam1 >= -eps && lam2 >= -eps && lam1+lam2 <= 1+eps)
+	    return DOES_INTERSECT;
+	}
+      else if (lam1 >= 0 && lam2 >= 0 && lam1+lam2 <= 1)
+	{  // lam3 > 0
+	  cnt++;
+	}
+
+    }
+
+  //  (*testout) << " inside = " << (cnt % 2) << endl;
+  return (cnt % 2) ? IS_INSIDE : IS_OUTSIDE;
+}
+
+
+
+INSOLID_TYPE Polyhedra :: VecInSolid (const Point<3> & p,
+				      const Vec<3> & v,
+				      double eps) const
+{
+  int point_on_n_faces = 0;
+  INSOLID_TYPE res;
+
+  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].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)
+	{
+	  point_on_n_faces++;
+
+	  double scal = vn * faces[i].n;
+	
+	  res = DOES_INTERSECT;
+	  if (scal > eps) res = IS_OUTSIDE;
+	  if (scal < -eps) res = IS_INSIDE;
+	}
+    }
+
+  if (point_on_n_faces == 1)
+    return res;
+
+  
+  Point<3> p2 = p + (1e-3/(v.Length()+1e-16)) * v;
+  res = PointInSolid (p2, eps);
+  //  (*testout) << "p = " << p << " v = " << v << " p2 = " << p2 << endl;
+  //  (*testout) << "polyeder::vecinsolid = " << int(res) << endl;
+  return res;
+}
+
+
+INSOLID_TYPE Polyhedra :: VecInSolid2 (const Point<3> & p,
+				       const Vec<3> & v1,
+				       const Vec<3> & v2,
+				       double eps) const
+{
+  int point_on_n_faces = 0;
+  INSOLID_TYPE res;
+
+  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;
+
+
+
+
+  return Primitive :: VecInSolid2 (p, v1, v2, eps);
+}
+
+
+
+
+void Polyhedra :: GetPrimitiveData (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)
+{
+  return points.Append (p);
+}
+
+int Polyhedra :: AddFace (int pi1, int pi2, int pi3)
+{
+  faces.Append (Face (pi1, pi2, pi3, points));
+  
+  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-6))
+      {
+	if (!inverse)
+	  identicto = i;
+      }
+  //  cout << "is identic = " << identicto << endl;
+
+  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;
+    }
+
+  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;
+}
+}
diff --git a/contrib/Netgen/libsrc/csg/polyhedra.hpp b/contrib/Netgen/libsrc/csg/polyhedra.hpp
new file mode 100644
index 0000000000..529cfff40d
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/polyhedra.hpp
@@ -0,0 +1,78 @@
+#ifndef FILE_POLYHEDRA
+#define FILE_POLYHEDRA
+
+
+/**************************************************************************/
+/* File:   polyhedra.hh                                                   */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   19. Mar. 2000                                                  */
+/**************************************************************************/
+
+/*
+
+  Polyhedral primitive
+  
+*/
+
+class Polyhedra : public Primitive
+{
+  class Face {
+  public:
+    int pnums[3];
+    int planenr;
+
+    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);
+  };
+
+  ARRAY<Point<3> > points;
+  ARRAY<Face> faces;
+  ARRAY<Plane*> planes;
+
+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 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 (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);
+  
+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 0000000000..e235012e43
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/revolution.cpp
@@ -0,0 +1,151 @@
+#include <mystdlib.h>
+
+#include <linalg.hpp>
+#include <csg.hpp>
+
+namespace netgen
+{
+
+#ifdef NONE
+
+Revolution :: Revolution (const Point<3> & ap1, const Point<3> & ap2)
+{
+  p1 = ap1;
+  p2 = ap2;
+  v12 = p2 - p1;
+  v12.Normalize();
+}
+
+Revolution :: ~Revolution ()
+{
+  ;
+}
+
+Primitive * Revolution :: CreateDefault ()
+{
+  return new Revolution( Point<3> (0, 0, 0),
+			 Point<3> (1, 0, 0));
+}
+
+INSOLID_TYPE Revolution :: BoxInSolid (const BoxSphere<3> & box) const
+{
+  int i;
+
+  Vec<3> v = box.Center() - p1;
+  double x = v * v12;
+  double y = sqrt (v.Length2() - x*x);
+  
+  Point<2> lp1, lp2;
+  for (i = 1; i <= polygon.GetNP(); i++)
+    {
+      polygon.GetLine (i, lp1, lp2);
+
+      double dist2 = MinDistLP2 (lp1, lp2, Point<2>(x,y));
+      if (dist2 < sqr (box.Diam()))
+	return DOES_INTERSECT;
+    };
+
+  return PointInSolid (box.Center(), 1e-3 * box.Diam());
+}
+
+
+INSOLID_TYPE Revolution :: PointInSolid (const Point<3> & p,
+					 double eps) const
+{
+  int i, cnt;
+
+  Vec<3> v(p1, p);
+  double x = v * v12;
+  double y = sqrt (v.Length2() - x*x);
+  
+  if (polygon.IsOn (Point<2> (x, y)))
+    return DOES_INTERSECT;
+  if (polygon.IsIn (Point<2> (x, y)))
+    return IS_INSIDE;
+  else
+    return IS_OUTSIDE;
+}
+
+
+
+INSOLID_TYPE Revolution :: VecInSolid (const Point<3> & p,
+				      const Vec<3> & v,
+				      double eps) const
+{
+  Point<3> p2 = p + (1e-3/(v.Length()+1e-16)) * v;
+  return PointInSolid (p2, eps);
+}
+
+
+void Revolution :: GetPrimitiveData (char *& classname, 
+				    ARRAY<double> & coeffs) const
+{
+  classname = "Revolution";
+  coeffs.SetSize(0);
+  coeffs.Append (polygon.GetNP());
+}
+
+void Revolution :: SetPrimitiveData (ARRAY<double> & coeffs)
+{
+  ;
+}
+
+void Revolution :: Reduce (const BoxSphere<3> & box)
+{
+  int i;
+
+  for (i = 1; i <= polygon.GetNP(); i++)
+    surfaceactive.Elem (i) = 0;
+
+
+  Vec<3> v = box.Center() - p1;
+  double x = v * v12;
+  double y = sqrt (v.Length2() - x*x);
+
+  Point<2> lp1, lp2;
+  for (i = 1; i <= polygon.GetNP(); i++)
+    {
+      polygon.GetLine (i, lp1, lp2);
+
+      double dist2 = MinDistLP2 (lp1, lp2, Point<2>(x,y));
+      if (dist2 < sqr (box.Diam()/2))
+	surfaceactive.Elem(i) = 1;
+    };
+}
+
+void Revolution :: UnReduce ()
+{
+  for (int i = 0; i < polygon.GetNP(); i++)
+    surfaceactive[i] = 1;
+}
+
+
+int Revolution :: AddPoint (const Point<2> & p)
+{
+  polygon.AddPoint (p);
+  return polygon.GetNP();
+}
+
+void Revolution :: Finish ()
+{
+  int i;
+  Point<2> lp1, lp2;
+  Point<3> cp1, cp2;
+
+  for (i = 1; i <= polygon.GetNP(); i++)
+    {
+      polygon.GetLine (i, lp1, lp2);
+      cp1 = p1 + lp1.X() * v12;
+      cp2 = p1 + lp2.X() * v12;
+      
+      faces.Append (new Cone (cp1, cp2,
+			      fabs (lp1.Y()), 
+			      fabs (lp2.Y())));
+
+      invsurf.Append (lp1.X() < lp2.X());
+    }
+}
+
+
+#endif
+}
diff --git a/contrib/Netgen/libsrc/csg/revolution.hpp b/contrib/Netgen/libsrc/csg/revolution.hpp
new file mode 100644
index 0000000000..d57102d306
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/revolution.hpp
@@ -0,0 +1,65 @@
+#ifndef FILE_REVOLUTION
+#define FILE_REVOLUTION
+
+
+/**************************************************************************/
+/* File:   revolution.hh                                                  */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   12. Oct. 2000                                                  */
+/**************************************************************************/
+
+/*
+
+  Primitive of revolution
+  
+*/
+
+
+#ifdef NONE
+
+class Revolution : public Primitive
+{
+  Point<3> p1, p2;
+  Vec<3> v12;
+  Polygon2d polygon;
+  ARRAY<Cone*> faces;
+  ARRAY<int> invsurf;
+public:
+  Revolution (const Point<3> & ap1, const Point<3> & ap2);
+  ~Revolution ();
+  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;
+
+  virtual int GetNSurfaces() const 
+    { return faces.Size(); }
+  virtual Surface & GetSurface (int i) 
+    { return *faces.Elem(i); }
+  virtual const Surface & GetSurface (int i = 1) const
+    { return *faces.Get(i); }
+
+  virtual int SurfaceInverted (int i = 1) const
+  { return invsurf.Get(i); }
+
+  virtual void GetPrimitiveData (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<2> & p);
+  void Finish ();
+protected:
+  //  int FaceBoxIntersection (int fnr, const BoxSphere<3> & box) const;
+  //  void CalcData();
+};
+
+
+#endif
+
+#endif
diff --git a/contrib/Netgen/libsrc/csg/singularref.cpp b/contrib/Netgen/libsrc/csg/singularref.cpp
new file mode 100644
index 0000000000..add7bfdf65
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/singularref.cpp
@@ -0,0 +1,117 @@
+#include <mystdlib.h>
+#include <myadt.hpp>
+
+#include <linalg.hpp>
+#include <csg.hpp>
+#include <meshing.hpp>
+
+namespace netgen
+{
+
+SingularEdge :: SingularEdge (double abeta, 
+			      const Solid * asol1, 
+			      const Solid * asol2)
+{
+  beta = abeta;
+
+  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;
+}
+
+void SingularEdge :: FindPointsOnEdge (class Mesh & mesh)
+{
+  (*testout) << "find points on edge" << endl;
+  int j;
+  points.SetSize(0);
+  segms.SetSize(0);
+  for (SegmentIndex si = 0; si < mesh.GetNSeg(); si++)
+    {
+      INDEX_2 i2 (mesh[si].p1, mesh[si].p2);
+      
+      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 (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 = 1;
+	  mesh[si].singedge_right = 1;
+	}	    
+    }
+
+  /*  
+  (*testout) << "Singular 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);
+  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)
+{
+  beta = abeta;
+  sol1 = asol1;
+  sol2 = asol2;
+  sol3 = asol3;
+}
+
+
+void SingularPoint :: FindPoints (class Mesh & mesh)
+{
+  points.SetSize(0);
+  for (PointIndex pi = PointIndex::BASE; 
+       pi < mesh.GetNP()+PointIndex::BASE; pi++)
+    {
+      const Point<3> p = mesh[pi];
+      if (sol1->IsIn (p) && sol2->IsIn(p) && sol3->IsIn(p) &&
+	  !sol1->IsStrictIn (p) && !sol2->IsStrictIn(p) && !sol3->IsStrictIn(p))
+	{
+	  points.Append (p);
+	  PrintMessage (5, "Point (", p(0), ", ", p(1), ", ", p(2), ") is singular");
+	  mesh[pi].SetSingular();
+	}
+    }  
+}
+
+
+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 0000000000..d52dcf8f8c
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/singularref.hpp
@@ -0,0 +1,68 @@
+#ifndef FILE_SINGULARREF
+#define FILE_SINGULARREF
+
+/**************************************************************************/
+/* File:   singularref.hh                                                 */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   25. Sep. 99                                                    */
+/**************************************************************************/
+
+/**
+   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;
+  // ARRAY<Point<3> > points;
+  // ARRAY<INDEX_2> segms;
+public:
+  SingularFace (int adomnr, const Solid * asol)
+    : domnr(adomnr), sol(asol) { ; }
+  const Solid * GetSolid() const { return sol; }
+  int GetDomainNr () const { return domnr; }
+};
+
+
+///
+class SingularEdge 
+{
+public:
+  double beta;
+  const Solid *sol1, *sol2;
+  ARRAY<Point<3> > points;
+  ARRAY<INDEX_2> segms;
+public:
+  SingularEdge (double abeta, const Solid * asol1, const Solid * asol2);
+  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;
+ 
+public:
+  SingularPoint (double abeta, const Solid * asol1, const Solid * asol2,
+		 const Solid * asol3);
+  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 0000000000..08d20f78a4
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/solid.cpp
@@ -0,0 +1,1217 @@
+#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;
+    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;
+	}
+      } 
+  }
+
+
+
+
+  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) ? 1 : 0;
+	}
+      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);
+      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;
+    int i;
+    for (i = 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;
+    int i;
+    for (i = 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) const
+  {
+    int in, strin;
+    RecTangentialSolid (p, tansol, in, strin);
+  }
+
+  void Solid :: RecTangentialSolid (const Point<3> & p, Solid *& tansol,
+				    int & in, int & strin) 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, 1e-6);
+
+	  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, in1, strin1);
+	  s2 -> RecTangentialSolid (p, tansol2, in2, strin2);
+
+	  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 -> RecTangentialSolid (p, tansol1, in1, strin1);
+	  s2 -> RecTangentialSolid (p, tansol2, in2, strin2);
+
+	  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 -> RecTangentialSolid (p, tansol1, hin, hstrin);
+
+	  if (tansol1)
+	    tansol = new Solid (SUB, tansol1);
+	  in = !hstrin;
+	  strin = !hin;
+	  break;
+	}
+      case ROOT:
+	{
+	  s1 -> RecTangentialSolid (p, tansol, in, strin);
+	  break;
+	}
+      }
+  }
+
+
+
+
+  void Solid :: TangentialSolid2 (const Point<3> & p, 
+				  const Vec<3> & t,
+				  Solid *& tansol) const
+  {
+    int in, strin;
+    RecTangentialSolid2 (p, t, tansol, in, strin);
+  }
+
+  void Solid :: RecTangentialSolid2 (const Point<3> & p, const Vec<3> & t,
+				     Solid *& tansol,
+				     int & in, int & strin) 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, 1e-6);
+	  if (ist == DOES_INTERSECT)
+	    ist = prim->VecInSolid (p, t, 1e-6);
+
+	  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, in1, strin1);
+	  s2 -> RecTangentialSolid2 (p, t, tansol2, in2, strin2);
+
+	  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, in1, strin1);
+	  s2 -> RecTangentialSolid2 (p, t, tansol2, in2, strin2);
+
+	  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, hin, hstrin);
+
+	  if (tansol1)
+	    tansol = new Solid (SUB, tansol1);
+	  in = !hstrin;
+	  strin = !hin;
+	  break;
+	}
+      case ROOT:
+	{
+	  s1 -> RecTangentialSolid2 (p, t, tansol, in, strin);
+	  break;
+	}
+      }
+  }
+
+
+
+
+  int Solid :: Edge (const Point<3> & p, const Vec<3> & v) const
+  {
+    int in, strin, faces;
+    RecEdge (p, v, in, strin, faces);
+    return faces >= 2;
+  }
+
+  int Solid :: OnFace (const Point<3> & p, const Vec<3> & v) const
+  {
+    int in, strin, faces;
+    RecEdge (p, v, in, strin, faces);
+    return faces >= 1;
+  }
+
+
+  void Solid :: RecEdge (const Point<3> & p, const Vec<3> & v,
+			 int & in, int & strin, int & faces) const
+  {
+    switch (op)
+      {
+      case TERM: case TERM_REF:
+	{
+	  INSOLID_TYPE ist = prim->VecInSolid (p, v, 1e-6);
+	  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) < 1e-6 && 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);
+	  s2 -> RecEdge (p, v, in2, strin2, faces2);
+
+	  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);
+	  s2 -> RecEdge (p, v, in2, strin2, faces2);
+
+	  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);
+	  in = !strin1;
+	  strin = !in1;
+	  break;
+	}
+      case ROOT:
+	{
+	  s1 -> RecEdge (p, v, in, strin, faces);
+	  break;
+	}
+      }
+  }
+
+
+  void Solid :: CalcSurfaceInverse ()
+  {
+    CalcSurfaceInverseRec (0);
+  }
+
+  void Solid :: CalcSurfaceInverseRec (int inv)
+  {
+    switch (op)
+      {
+      case TERM: case TERM_REF:
+	{
+	  int priminv;
+	  for (int i = 0; i < prim->GetNSurfaces(); i++)
+	    {
+	      priminv = prim->SurfaceInverted(i);
+	      if (inv) priminv = 1 - 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;
+	}
+      }
+    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 :: 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;
+	}
+      }
+  }
+
+
+
+
+
+  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 0000000000..796729125a
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/solid.hpp
@@ -0,0 +1,195 @@
+#ifndef FILE_SOLID
+#define FILE_SOLID
+
+/**************************************************************************/
+/* File:   solid.hh                                                       */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   1. Dez. 95                                                     */
+/**************************************************************************/
+
+/*
+
+  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;
+
+  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 = 1e-6) const;
+  bool VectorIn2Rec (const Point<3> & p, const Vec<3> & v1, const Vec<3> & v2,
+		     double eps = 1e-6) const;
+
+
+  ///
+  void TangentialSolid (const Point<3> & p, Solid *& tansol) const;
+  ///
+  void TangentialSolid2 (const Point<3> & p, const Vec<3> & t,
+			 Solid *& tansol) const;
+  ///
+  int Edge (const Point<3> & p, const Vec<3> & v) const;
+  ///
+  int OnFace (const Point<3> & p, const Vec<3> & v) 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, 
+			   int & in, int & strin) const;
+  ///
+  void RecTangentialSolid2 (const Point<3> & p, const Vec<3> & vec, 
+			    Solid *& tansol, int & in, int & strin) const;
+  ///
+  void RecEdge (const Point<3> & p, const Vec<3> & v,
+                int & in, int & strin, int & faces) const;
+  ///
+  void CalcSurfaceInverseRec (int inv);
+  ///
+  Solid * RecGetReducedSolid (const BoxSphere<3> & box, INSOLID_TYPE & in) const;
+  ///
+  void RecGetSurfaceIndices (ARRAY<int> & surfind) const;
+  void RecGetSurfaceIndices (IndexSet & iset) 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 0000000000..c9b2477bb0
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/specpoin.cpp
@@ -0,0 +1,1231 @@
+#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 
+*/
+
+
+namespace netgen
+{
+  void ProjectToEdge (const Surface * f1, const Surface * f2, Point<3> & hp);
+
+
+
+  SpecialPoint :: SpecialPoint (const SpecialPoint & sp)
+  {
+    p = sp.p;
+    v = sp.v;
+    s1 = sp.s1;
+    s2 = sp.s2;
+    layer = sp.layer;
+    unconditional = sp.unconditional;
+  }
+  
+  SpecialPoint & SpecialPoint :: operator= (const SpecialPoint & sp)
+  {
+    p = sp.p;
+    v = sp.v;
+    s1 = sp.s1;
+    s2 = sp.s2;
+    layer = sp.layer;
+    unconditional = sp.unconditional;
+    return *this;
+  }
+
+
+  void SpecialPoint :: Print (ostream & str)
+  {
+    str << "p = " << p << "   v = " << v 
+	<< " s1/s2 = " << s1 << "/" << s2
+	<< " layer = " << layer
+	<< endl;
+  }
+
+
+  static ARRAY<int> numprim_hist;
+
+  SpecialPointCalculation :: SpecialPointCalculation ()
+  {
+    ;  
+  }
+
+  void SpecialPointCalculation :: 
+  CalcSpecialPoints (const CSGeometry & ageometry, 
+		     ARRAY<MeshPoint> & apoints)
+  {
+    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;
+
+	CalcSpecialPointsRec (tlo->GetSolid(), tlo->GetLayer(),
+			      box, 1, 1, 1);
+      }
+  
+    // 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)
+  {
+    if (multithread.terminate)
+      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, sureexp;          // 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();
+
+    numprim_hist[numprim]++;
+
+    Point<3> p = box.Center();
+
+
+    possiblecrossp = (numprim >= 3) && calccp;
+    surecrossp = 0;
+
+    if (possiblecrossp && (locsurf.Size() <= 5 || 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)), p );
+	      
+		deg = CrossPointDegenerated 
+		  (geometry->GetSurface(locsurf.Get(k1)), 
+		   geometry->GetSurface(locsurf.Get(k2)), 
+		   geometry->GetSurface(locsurf.Get(k3)), box );
+	      
+		if (!nc && !deg) decision = 0;
+		if (nc) surecrossp = 1;
+	      }
+
+	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)), p ) )
+		      {
+			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);
+
+			if (pp(0) > box.PMin()(0) - 1e-5 && 
+			    pp(0) < box.PMax()(0) + 1e-5 &&
+			    pp(1) > box.PMin()(1) - 1e-5 && 
+			    pp(1) < box.PMax()(1) + 1e-5 &&
+			    pp(2) > box.PMin()(2) - 1e-5 && 
+			    pp(2) < box.PMax()(2) + 1e-5 &&
+			    sol -> IsIn (pp) && !sol->IsStrictIn (pp) &&
+			    !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);
+			    boxp.CalcDiamCenter();
+			    ARRAY<int> locsurf2;
+
+			    geometry -> GetIndependentSurfaceIndices (sol, boxp, locsurf2);
+			  
+			    bool found1 = 0, found2 = 0, found3 = 0;
+			    for (int i = 0; i < locsurf2.Size(); i++)
+			      {
+				if (locsurf2[i] == locsurf.Get(k1)) found1 = 1;
+				if (locsurf2[i] == locsurf.Get(k2)) found2 = 1;
+				if (locsurf2[i] == locsurf.Get(k3)) found3 = 1;
+			      }
+
+			    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;
+
+
+    if (numprim == 2)
+      {
+	const Surface * surf1 = geometry->GetSurface(locsurf[0]);
+	const Surface * surf2 = geometry->GetSurface(locsurf[1]);
+	
+	const Plane * plane1 = dynamic_cast<const Plane*> (surf1);
+	const Plane * plane2 = dynamic_cast<const Plane*> (surf2);
+	const QuadraticSurface * quadric1 = dynamic_cast<const QuadraticSurface*> (surf1);
+	const QuadraticSurface * quadric2 = dynamic_cast<const QuadraticSurface*> (surf2);
+
+	if (plane1 && plane2)
+	  possibleexp = 0;
+	else
+	  {
+	    ARRAY<Point<3> > pts;
+	    if (plane1 && quadric2)
+	      {
+		ComputeExtremalPoints (plane1, quadric2, pts);
+		possibleexp = 0;
+	      }		
+	    else if (plane2 && quadric1)
+	      {
+		ComputeExtremalPoints (plane2, quadric1, pts);
+		possibleexp = 0;
+	      }		
+
+	    for (int j = 0; j < pts.Size(); j++)
+	      if (Dist (pts[j], box.Center()) < box.Diam()/2 &&
+		  sol -> IsIn (pts[j]) && !sol->IsStrictIn (pts[j]) )
+		{
+		  if (AddPoint (pts[j], layer))
+		    (*testout) << "Extremal point found: " << pts[j] << endl;
+		}  
+	  }
+      }
+
+    
+    if (possibleexp && (locsurf.Size() <= 5 || 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;
+		}
+	    }
+
+	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) && !sol->IsStrictIn (pp) )
+			    {
+			      if (AddPoint (pp, layer))
+				(*testout) << "Extremal point found: " << pp << endl;
+			    }  
+			}            
+		    }
+		}
+	  }
+	if (decision)
+	  possibleexp = 0;
+      }
+ 
+
+
+    if (possiblecrossp || possibleexp)
+      {
+	BoxSphere<3> sbox;
+	for (int i = 0; i < 8; i++)
+	  {
+	    box.GetSubBox (i, sbox);
+	    sbox.Increase (1e-4 * sbox.Diam());
+
+	    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 Point<3> & p)
+  {
+    Vec<3> grad, rs, x;
+    Mat<3> jacobi, inv;
+
+    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);
+
+	return (beta * gamma * eta < 0.1);
+      }
+    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;
+
+	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);
+
+	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);
+
+	jacobi.Solve (rs, x);
+
+	if (Abs2 (x) < 1e-24 && i > 1)
+	  {
+	    i = 1;
+	  }
+      
+	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 :: 
+  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;
+		    */
+		  }
+	      }
+	  }
+      }
+  }
+
+
+
+
+
+
+  /*
+    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 0;
+
+    points->Append (MeshPoint(p, layer));
+    PrintMessageCR (3, "Found points ", points->Size());
+    return 1;
+  }
+
+
+
+
+
+
+
+  void SpecialPointCalculation :: 
+  AnalyzeSpecialPoints (const CSGeometry & ageometry,
+			ARRAY<MeshPoint> & apoints, 
+			ARRAY<SpecialPoint> & specpoints)
+  {
+    ARRAY<int> surfind;
+    ARRAY<int> surfind2;
+
+    ARRAY<Vec<3> > normalvecs;
+    Vec<3> t, nsurf;
+    Point<3> p;
+
+    ARRAY<int> specpoint2point;
+    specpoints.SetSize (0);
+
+    geometry = &ageometry;
+ 
+    (*testout) << "AnalyzeSpecialPoints\n";
+
+
+    Box<3> bbox;
+    if (apoints.Size())
+      bbox.Set (apoints[0]);
+    else
+      { bbox.Set (Point<3> (0,0,0)); bbox.Add (Point<3> (1,1,1)); }
+    for (int i = 1; i < apoints.Size(); i++)
+      bbox.Add (apoints[i]);
+    bbox.Increase (0.1 * Dist (bbox.PMin(), bbox.PMax()));
+
+    Point3dTree searchtree (bbox.PMin(), bbox.PMax());
+    ARRAY<int> locsearch;
+
+    for (int si = 0; si < ageometry.GetNTopLevelObjects(); si++)
+      {
+	//      (*testout) << "main solid " << si << "\n";
+	
+	const Solid * sol = ageometry.GetTopLevelObject(si)->GetSolid();
+	const Surface * surf = ageometry.GetTopLevelObject(si)->GetSurface();
+
+	for (int i = 0; i < apoints.Size(); i++)
+	  {
+	    p = apoints[i];
+	    if (ageometry.GetTopLevelObject(si)->GetLayer() !=
+		apoints[i].GetLayer())
+	      continue;
+
+	    //	  (*testout) << "Point " << apoints[i] << "\n";
+
+	    Solid * locsol;
+	    sol -> TangentialSolid (p, locsol);
+	    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);
+	    box.CalcDiamCenter();
+	    ageometry.GetIndependentSurfaceIndices (locsol, box, 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 = j+1; k < normalvecs.Size(); k++)
+		for (int l = 1; l <= 2; l++)
+		  {
+		    t = Cross (normalvecs[j], normalvecs[k]);
+		    if (Abs2 (t) < 1e-8)
+		      {
+			cerr << "AnalyzePoint: Surfaces degenerated" << "\n";
+			break;
+		      }
+		    t.Normalize();
+		    if (l == 2) t *= -1;
+
+		    // try tangential direction t
+
+		    // (*testout) << "check tangential " << t << "\n";
+
+		    if (surf && fabs (nsurf * t) > 1e-6)
+		      continue;
+
+		    if (!surf)
+		      {
+			ageometry.GetIndependentSurfaceIndices 
+			  (locsol, p, t, surfind2);
+		  
+			bool found1 = 0, found2 = 0;
+			for (int ii = 0; ii < surfind2.Size(); ii++)
+			  {
+			    if (surfind2[ii] == surfind[j])
+			      found1 = 1;
+			    if (surfind2[ii] == surfind[k])
+			      found2 = 1;
+			  }
+			if (!found1 || !found2)
+			  continue;
+		      }
+
+
+		    bool isedge;
+
+		    // isedge = locsol -> Edge (apoints.Get(i), t);
+		  
+		    // edge must be on tangential surface
+		    isedge = 
+		      locsol->VectorIn (p, t) &&
+		      !locsol->VectorStrictIn (p, t);
+		  
+		    // (*testout) << "isedge,1 = " << isedge << "\n";
+
+		    // there must exist at least two different faces on edge
+		    if (isedge)
+		      {
+			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;
+
+			    /*
+			      (*testout) << "nv = " << normalvecs[m] << ", s = " << s << "\n";
+			      (*testout) << "t2a = " << t2a << ", t2b = " << t2b << "\n";
+			      (*testout) << "via = "
+			      << locsol->VectorIn (p, t2a) << "/"
+			      << locsol->VectorStrictIn (p, t2a);
+			      (*testout) << "vib = "
+			      << locsol->VectorIn (p, t2b) << "/"
+			      << locsol->VectorStrictIn (p, t2b) << "\n";
+			    */
+
+			    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)
+		      {
+			int spi = -1;
+
+			searchtree.GetIntersecting (apoints[i]-Vec3d(1e-4,1e-4,1e-4), 
+						    apoints[i]+Vec3d(1e-4,1e-4,1e-4), 
+						    locsearch);
+
+			for (int m = 0; m < locsearch.Size(); m++)
+			  if (Dist2 (specpoints[locsearch[m]].p, apoints[i]) < 1e-8
+			      && 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);
+			  }
+
+			specpoints[spi].p = apoints[i];
+			specpoints[spi].v = t;
+			if (surfind.Size() >= 3)
+			  specpoints[spi].unconditional = 1;
+			specpoints[spi].s1 = surfind[j];
+			specpoints[spi].s2 = surfind[k];
+			specpoints[spi].layer = apoints[i].GetLayer();
+			for (int up = 0; up < geometry->GetNUserPoints(); up++)
+			  if (Dist (geometry->GetUserPoint(up), apoints[i]) < 1e-10)
+			    specpoints[spi].unconditional = 1;
+		      }
+          
+		  }
+	    delete locsol;
+	  }
+      }
+
+    // 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 0000000000..8fd26f3207
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/specpoin.hpp
@@ -0,0 +1,150 @@
+#ifndef FILE_SPECPOIN
+#define FILE_SPECPOIN
+
+
+/**************************************************************************/
+/* File:   specpoin.hpp                                                   */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Okt. 95                                                    */
+/**************************************************************************/
+
+/*
+
+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;        
+
+  ///
+  SpecialPoint () : p(0,0,0), v(0,0,0), layer(0), unconditional(0), s1(0), s2(0)
+  { ; }
+
+  ///
+  SpecialPoint (const SpecialPoint & sp2);
+
+  ///
+  SpecialPoint & operator= (const SpecialPoint & sp2);
+  
+  ///
+  void Print (ostream & str);
+
+
+  int GetLayer() const { return layer; }
+
+  ///
+  bool HasSurfaces (int as1, int as2) const
+  {
+    return (s1 == as1 && s2 == as2 || s1 == as2 && s2 == as1);
+  }
+};
+
+
+
+///
+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;
+
+public: 
+
+  ///
+  SpecialPointCalculation (); 
+  
+  ///
+  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 Point<3> & p);  
+  ///
+  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);
+};
+
+#endif
+
+
diff --git a/contrib/Netgen/libsrc/csg/specpoin_new.cpp b/contrib/Netgen/libsrc/csg/specpoin_new.cpp
new file mode 100644
index 0000000000..4ac2c130fa
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/specpoin_new.cpp
@@ -0,0 +1,1367 @@
+#include <mystdlib.h>
+#include <meshing.hpp>
+#include <csg.hpp>
+
+
+/*
+   Special Point calculation uses the global Flags:
+
+   size .. 500       cube = [-size, size]^3
+   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 
+*/
+
+
+namespace netgen
+{
+void ProjectToEdge (const Surface * f1, const Surface * f2, Point<3> & hp);
+
+
+
+SpecialPoint :: SpecialPoint (const SpecialPoint & sp)
+{
+  p = sp.p;
+  v = sp.v;
+  s1 = sp.s1;
+  s2 = sp.s2;
+  layer = sp.layer;
+  unconditional = sp.unconditional;
+}
+  
+SpecialPoint & SpecialPoint :: operator= (const SpecialPoint & sp)
+{
+  p = sp.p;
+  v = sp.v;
+  s1 = sp.s1;
+  s2 = sp.s2;
+  layer = sp.layer;
+  unconditional = sp.unconditional;
+  return *this;
+}
+
+
+void SpecialPoint :: Print (ostream & str)
+{
+  str << "p = " << p << "   v = " << v 
+      << " s1/s2 = " << s1 << "/" << s2
+      << " layer = " << layer
+      << endl;
+}
+
+
+
+
+SpecialPointCalculation :: SpecialPointCalculation ()
+{
+  ;  
+}
+
+void SpecialPointCalculation :: 
+CalcSpecialPoints (const CSGeometry & ageometry, 
+		   ARRAY<MeshPoint> & apoints)
+{
+  int i;
+
+  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());
+
+  for (i = 0; i < geometry->GetNTopLevelObjects(); i++)
+    {
+      (*testout) << "tlo " << i << ":" << endl;
+      const TopLevelObject * tlo = geometry->GetTopLevelObject(i);
+      tlo->GetSolid()->Print (*testout); 
+      (*testout) << endl;
+      CalcSpecialPointsRec (tlo->GetSolid(), tlo->GetLayer(),
+			    box, 1, 1, 1);
+    }
+  
+  PrintDot ('\n');
+
+
+  // add user point:
+  for (i = 0; i < geometry->GetNUserPoints(); i++)
+    AddPoint (geometry->GetUserPoint(i), 1);
+  
+  PrintMessage (3, apoints.Size(), " special points");
+
+  for (i = 0; i < boxesinlevel.Size(); i++)
+    (*testout) << "level " << i << " has " 
+	       << boxesinlevel[i] << " boxes" << endl;
+}
+
+
+
+// int debug;
+void SpecialPointCalculation :: 
+CalcSpecialPointsRec (const Solid * sol, int layer,
+		      const BoxSphere<3> & box, 
+		      int level, bool calccp, bool calcep)
+{
+  if (multithread.terminate)
+    return;
+
+  int i;
+  BoxSphere<3> sbox;
+  Solid * redsol;
+
+  int numprim;
+
+  bool decision;
+  bool possiblecrossp, possibleexp;  // possible cross or extremalpoint
+  bool surecrossp, sureexp;          // sure ...
+  
+  static ARRAY<int> locsurf;  // attention: array is static
+
+
+  Point<3> p;
+  int k1, k2, k3;
+  int extremdir;
+  double hd;
+
+  if (!sol) return;
+
+  if (level >= 100)
+    {
+      cerr << "Problems in CalcSpecialPoints" << endl;
+      cerr << "Point: " << box.Center() << endl;
+      exit (1);
+    }
+
+  static int cntbox = 0;
+  cntbox++;
+  if (cntbox % 10000 == 0) 
+    PrintDot ();
+
+  if (level <= boxesinlevel.Size())
+    boxesinlevel.Elem(level)++;
+  else
+    boxesinlevel.Append (1);
+
+  /*
+  numprim = sol -> NumPrimitives();
+  sol -> GetSurfaceIndices (locsurf);
+  */
+
+  // debug = 0;
+  // box.IsIn (Point<3> (4.9, 1.279, 2.8));
+
+
+  geometry -> GetIndependentSurfaceIndices (sol, box, locsurf);
+  numprim = locsurf.Size();
+
+  /*  
+  if (debug)
+    {
+      (*testout) << "box = " << box.PMin() << "-" << box.PMax()
+		 << " np = " << numprim << " : ";
+      for (i = 1; i <= locsurf.Size(); i++)
+	(*testout) << " " << locsurf.Get(i);
+      (*testout) << " diam = " << box.Diam();
+      (*testout) << " numprim = " << numprim;
+      (*testout) << endl;
+    }
+  */
+
+
+  p = box.Center();
+  
+  /*
+  (*testout) << "box = " << box.PMin() << " - " << box.PMax()
+	     << ", lev = " << level 
+	     << ", nprim = " << sol->NumPrimitives() 
+	     << ", lsurf = " << locsurf.Size() << endl;
+
+  for (i = 1; i <= locsurf.Size(); i++)
+    geometry->GetSurface (locsurf.Get(i)) -> Print (*testout);
+  sol -> Print (*testout);
+  */
+
+  /*
+    for (i = 1; i <= locsurf.Size(); i++)
+    (*testout) << locsurf.Get(i) << " ";
+    (*testout) << "C = " << box.Center() << " diam = " << box.Diam() << endl;
+    */
+
+  possiblecrossp = (numprim >= 3) && calccp;
+  surecrossp = 0;
+
+  if (possiblecrossp && (locsurf.Size() <= 10))
+    {
+      decision = 1;
+      surecrossp = 0;
+
+      for (k1 = 1; k1 <= locsurf.Size() - 2; k1++)
+	for (k2 = k1 + 1; k2 <= locsurf.Size() - 1; k2++)
+	  for (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)), p );
+	      
+	      deg = CrossPointDegenerated 
+		(geometry->GetSurface(locsurf.Get(k1)), 
+		 geometry->GetSurface(locsurf.Get(k2)), 
+		 geometry->GetSurface(locsurf.Get(k3)), box );
+	      
+	      if (!nc && !deg) decision = 0;
+	      if (nc) surecrossp = 1;
+	    }
+
+      if (decision && surecrossp)
+	{
+	  for (k1 = 1; k1 <= locsurf.Size() - 2; k1++)
+	    for (k2 = k1 + 1; k2 <= locsurf.Size() - 1; k2++)
+	      for (k3 = k2 + 1; k3 <= locsurf.Size(); k3++)
+		{
+		  if (CrossPointNewtonConvergence 
+		      (geometry->GetSurface(locsurf.Get(k1)), 
+		       geometry->GetSurface(locsurf.Get(k2)), 
+		       geometry->GetSurface(locsurf.Get(k3)), p ) )
+		    {
+		      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);
+
+		      if (pp(0) > box.PMin()(0) - 1e-5 && 
+			  pp(0) < box.PMax()(0) + 1e-5 &&
+			  pp(1) > box.PMin()(1) - 1e-5 && 
+			  pp(1) < box.PMax()(1) + 1e-5 &&
+			  pp(2) > box.PMin()(2) - 1e-5 && 
+			  pp(2) < box.PMax()(2) + 1e-5 &&
+			  sol -> IsIn (pp) && !sol->IsStrictIn (pp) &&
+			  !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);
+			  boxp.CalcDiamCenter();
+			  ARRAY<int> locsurf2;
+
+			  geometry -> GetIndependentSurfaceIndices (sol, boxp, locsurf2);
+			  
+			  /*
+			  ReducePrimitiveIterator rpi(boxp);
+			  UnReducePrimitiveIterator urpi;
+			  
+			  ((Solid*)sol) -> IterateSolid (rpi);
+			  sol -> GetIndependentSurfaceIndices (locsurf2);
+			  ((Solid*)sol) -> IterateSolid (urpi);
+			  */
+			  bool found1 = 0, found2 = 0, found3 = 0;
+			  for (i = 1; i <= locsurf2.Size(); i++)
+			    {
+			      if (locsurf2.Get(i) == locsurf.Get(k1))
+				found1 = 1;
+			      if (locsurf2.Get(i) == locsurf.Get(k2))
+				found2 = 1;
+			      if (locsurf2.Get(i) == locsurf.Get(k3))
+				found3 = 1;
+			    }
+			  
+			  if (found1 && found2 && found3)
+			    if (AddPoint (pp, layer))
+			      {
+				(*testout) << "Crosspoint found: " << pp 
+					   << " diam = " << box.Diam() << endl;
+				(*testout) << "surfs: " 
+					   << locsurf.Get(k1) << "," 
+					   << locsurf.Get(k2) << "," 
+					   << locsurf.Get(k3) << endl;
+			      }
+			}
+		    }
+		}
+	}
+      
+      if (decision)
+	possiblecrossp = 0;
+    }
+
+
+
+
+  possibleexp = (numprim >= 2) && calcep;
+    
+  if (possibleexp && (locsurf.Size() <= 10))
+    {
+      decision = 1;
+      sureexp = 0;
+
+      for (k1 = 1; k1 <= locsurf.Size() - 1; k1++)
+	for (k2 = k1+1; k2 <= locsurf.Size(); k2++)
+	  {
+	    bool nc, deg;
+	    
+	    nc = EdgeNewtonConvergence 
+	      (geometry->GetSurface(locsurf.Get(k1)),
+	       geometry->GetSurface(locsurf.Get(k2)),
+	       p);
+	    
+	    deg = EdgeDegenerated 
+	      (geometry->GetSurface(locsurf.Get(k1)),
+	       geometry->GetSurface(locsurf.Get(k2)),
+	       box);
+	    
+	    if (!nc && !deg) decision = 0;
+	    if (nc) sureexp = 1;
+
+	    /*
+	    if (debug)
+	      {
+		(*testout) << "p = " << p << " s1,2 = " << locsurf.Get(k1) << ", " << locsurf.Get(k2) 
+			   << " nc = " << nc << " deg = " << deg << endl;
+	      }
+	    */
+	  }
+
+      if (decision && sureexp)
+	{
+	  for (k1 = 1; k1 <= locsurf.Size() - 1; k1++)
+	    for (k2 = k1+1; k2 <= locsurf.Size(); k2++)
+	      {
+		if (
+		    EdgeNewtonConvergence 
+		    (geometry->GetSurface(locsurf.Get(k1)),
+		     geometry->GetSurface(locsurf.Get(k2)),
+		     p) )
+		  {
+		    EdgeNewton 
+		      (geometry->GetSurface(locsurf.Get(k1)),
+		       geometry->GetSurface(locsurf.Get(k2)),
+		       p);
+		    
+		    Point<3> pp;
+		    if (IsEdgeExtremalPoint 
+		      (geometry->GetSurface(locsurf.Get(k1)),
+		       geometry->GetSurface(locsurf.Get(k2)),
+		       p, pp, box.Diam()/2))
+			{
+			  (*testout) << "extremalpoint (nearly) found:" 
+				     << pp
+				     << endl;
+			  if (Dist (pp, box.Center()) < box.Diam()/2 &&
+			      sol -> IsIn (pp) && !sol->IsStrictIn (pp) )
+			    {
+			      //                AddExtremalPoint (locsurf.Get(k1), locsurf.Get(k2), p);
+			      if (AddPoint (pp, layer))
+				(*testout) << "Extremal point found: " << pp << endl;
+			    }  
+			}            
+		  }
+	      }
+	}
+      if (decision)
+	possibleexp = 0;
+    }
+ 
+
+
+  if (possiblecrossp || possibleexp)
+    {
+      for (i = 0; i < 8; i++)
+	{
+	  box.GetSubBox (i, sbox);
+	  sbox.Increase (1e-4 * sbox.Diam());
+
+	  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 Point<3> & p)
+{
+  int i;
+  Vec<3> grad;
+  Vec<3> rs, x;
+  Mat<3> jacobi, inv;
+  double alpha, beta, gamma, eta;
+  double sum;
+  int j;
+
+
+  rs(0) = f1->CalcFunctionValue (p);
+  rs(1) = f2->CalcFunctionValue (p);
+  rs(2) = f3->CalcFunctionValue (p);
+
+  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);
+
+  alpha = 1;
+  if (fabs (Det (jacobi)) > 1e-8)
+    {
+      CalcInverse (jacobi, inv);
+      x = inv * rs;
+
+      gamma = f1 -> HesseNorm() + f2 -> HesseNorm() + f3 -> HesseNorm();
+      beta = 0;
+      for (i = 0; i < 3; i++)
+	{
+	  sum = 0;
+	  for (j = 0; j < 3; j++)
+	    sum += fabs (inv(i,j));
+	  beta = max2 (beta, sum);
+	}
+      eta = Abs (x);
+
+      alpha = beta * gamma * eta;
+    }
+
+  return (alpha < 0.1);
+}
+
+
+
+
+bool SpecialPointCalculation :: 
+CrossPointDegenerated (const Surface * f1,
+		       const Surface * f2, 
+		       const Surface * f3, 
+		       const BoxSphere<3> & box) const
+{
+  Mat<3> mat;
+  Vec<3> grad, g1, g2, g3;
+  double normprod;
+
+  if (box.Diam() > relydegtest) return 0;
+
+  f1->CalcGradient (box.Center(), g1);
+  normprod = Abs (g1);
+
+  f2->CalcGradient (box.Center(), g2);
+  normprod *= Abs (g2);
+ 
+  f3->CalcGradient (box.Center(), g3);
+  normprod *= Abs (g3);
+
+  for (int i = 0; i < 3; i++)
+    {
+      mat(i,0) = g1(i);
+      mat(i,1) = g2(i);
+      mat(i,2) = g3(i);
+    }
+
+  if (fabs (Det (mat)) < cpeps1 * normprod)
+    return 1;
+  else 
+    return 0;
+}
+ 
+
+
+
+
+void SpecialPointCalculation :: CrossPointNewton (const Surface * f1, 
+						  const Surface * f2, 
+						  const Surface * f3, Point<3> & p)
+{
+  int i;
+  Vec<3> g1, g2, g3;
+  Vec<3> rs, sol;
+  Mat<3> mat;
+
+  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);
+	  /*
+      Transpose (g1, g2, g3);
+      SolveLinearSystem (g1, g2, g3, rs, sol);
+	  */
+      if (sol.Length() < 1e-12 && i > 1) i = 1;
+
+      p -= sol;
+    }
+}
+
+
+
+
+/******* Tests for Point on edges **********************/
+
+
+
+
+bool SpecialPointCalculation :: 
+EdgeNewtonConvergence (const Surface * f1, const Surface * f2, 
+		       const Point<3> & p)
+{
+  int i;
+  Vec<3> g1, g2, sol;
+  Vec<2> vrs;
+  double alpha, beta, gamma, eta;
+  double sum;
+  Mat<2,3> mat;
+  Mat<3,2> inv;
+  int j;
+
+  vrs(0) = f1->CalcFunctionValue (p);
+  vrs(1) = f2->CalcFunctionValue (p);
+
+  f1->CalcGradient (p, g1);
+  f2->CalcGradient (p, g2);
+
+  for (i = 0; i < 3; i++)
+    {
+      mat(0,i) = g1(i);
+      mat(1,i) = g2(i);
+    }
+
+  alpha = 1;
+
+  if ( fabs(g1 * g2) < (1 - 1e-8) * Abs (g1) * Abs (g2))
+    {
+      CalcInverse (mat, inv);
+      sol = inv * vrs;
+
+      // SolveLinearSystemLS (g1, g2, vrs, sol);
+
+      gamma = f1 -> HesseNorm() + f2 -> HesseNorm();
+
+      /*
+      Vec<3> inv1, inv2;
+      PseudoInverse (g1, g2, inv1, inv2);
+      */
+      
+      beta = 0;
+      for (i = 0; i < 3; i++)
+	for (j = 0; j < 2; j++)
+	  beta += inv(i,j) * inv(i,j);
+      beta = sqrt (beta);
+
+      //      beta = inv1.Length() + inv2.Length();
+      eta = Abs (sol);
+      alpha = beta * gamma * eta;
+    }
+  return (alpha < 0.1);
+}
+
+
+
+
+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();
+  int i;
+  Vec<3> grad, g1, g2, sol;
+  Vec<2> vrs;
+  Mat<2,3> mat;
+
+  i = 20;
+  while (i > 0)
+    {
+      if (Dist (p, box.Center()) > box.Diam())
+	return 0;
+
+      i--;
+      vrs(0) = f1->CalcFunctionValue (p);
+      vrs(1) = f2->CalcFunctionValue (p);
+
+      f1->CalcGradient (p, g1);
+      f2->CalcGradient (p, g2);
+
+      if ( fabs (g1 * g2) > (1 - 1e-10) * Abs (g1) * Abs (g2))
+	return 1;
+
+      for (int j = 0; j < 3; j++)
+	{
+	  mat(0,j) = g1(j);
+	  mat(1,j) = g2(j);
+	}
+      mat.Solve (vrs, sol);
+      //      SolveLinearSystemLS (g1, g2, vrs, sol);
+
+      if (Abs (sol) < 1e-12 && i > 1) i = 1;
+      p -= sol;
+    }
+
+  return 0;
+}
+
+
+
+
+
+
+void SpecialPointCalculation :: EdgeNewton (const Surface * f1, 
+					    const Surface * f2, Point<3> & p)
+{
+  int i;
+  Vec<3> grad, g1, g2, sol;
+  Vec<2> vrs;
+  Mat<2,3> mat;
+
+  i = 10;
+  while (i > 0)
+    {
+      i--;
+      vrs(0) = f1->CalcFunctionValue (p);
+      vrs(1) = f2->CalcFunctionValue (p);
+
+      f1->CalcGradient (p, g1);
+      f2->CalcGradient (p, g2);
+
+      for (int j = 0; j < 3; j++)
+	{
+	  mat(0,j) = g1(j);
+	  mat(1,j) = g2(j);
+	}
+      mat.Solve (vrs, sol);
+      //      SolveLinearSystemLS (g1, g2, vrs, sol);
+
+      if (Abs (sol) < 1e-12 && 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;
+  int j;
+
+  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 (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)
+{
+  int i;
+
+  Vec<3> g1, g2, v, curv;
+  Vec<3> rs, x, y1, y2, y;
+  Mat<3> h1, h2;
+  Mat<3> jacobi;
+
+
+  if (dir < 1 || dir > 3)
+    {
+      cerr << "Error: Illegal extremdir" << endl;
+      return;
+    }
+
+  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);
+
+      jacobi.Solve (rs, x);
+      /*
+      CalcInverse (jacobi, inv);
+      inv.Mult (rs, x);
+      */
+      //    (*testout) << "err = " << x.L2Norm() << endl;
+
+      if (Abs (x) < 1e-12 && i > 1)
+	{
+	  //      (*testout) << "convergent in " << (10 - i) << " steps " << endl;
+
+	  i = 1;
+	}
+      
+      p -= x;
+    }
+
+  if (Abs (x) > 1e-10)
+    {
+      (*testout) << "Error: extremum Newton not convergent" << endl;
+      (*testout) << "dir = " << dir << endl;
+      (*testout) << "p = " << p << endl;
+      (*testout) << "x = " << x << 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);
+
+  /*
+  CalcInverse (a, inv);
+  inv.Mult (rs, x);          // x .. Lagrangeparameter
+  */
+  //  (*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 0;
+
+  points->Append (MeshPoint(p, layer));
+  return 1;
+}
+
+
+
+
+
+
+
+/*
+void SpecialPointCalculation :: 
+AnalyzeSpecialPoints (const CSGeometry & ageometry,
+		      ARRAY<Point<3> > & apoints, 
+		      ARRAY<SpecialPoint> & specpoints)
+{
+  int si, i, j, k, l, m, spi;
+  Solid * locsol;
+  ARRAY<int> surfind;
+  ARRAY<Vec<3>> normalvecs;
+  const Solid * sol;
+  Vec<3> t;
+  Point<3> p;
+
+  ARRAY<int> specpoint2point;
+  specpoints.SetSize (0);
+ 
+  (*testout) << "AnalyzeSpecialPoints" << endl;
+
+  for (si = 1; si <= ageometry.GetNTopLevelObjects(); si++)
+    {
+      (*testout) << "main solid " << si << endl;
+
+      sol = ageometry.GetTopLevelObject(si)->GetSolid();
+      for (i = 1; i <= apoints.Size(); i++)
+	{
+	  p = apoints.Get(i);
+
+	  sol -> TangentialSolid (p, locsol);
+	  if (!locsol) continue;
+	  
+	  (*testout) << "Point " << apoints.Get(i) << endl;
+
+	  locsol -> GetSurfaceIndices (surfind);
+	  for (j = surfind.Size(); j >= 1; j--)
+	    if (fabs (ageometry.GetSurface(surfind.Get(j))->
+		      CalcFunctionValue (p)) > 1e-6)
+	      surfind.DeleteElement (j);
+
+
+
+	  (*testout) << "Surfaces: ";
+	  for (j = 1; j <= surfind.Size(); j++)
+	    (*testout) << surfind.Get(j) << " ";
+	  (*testout) << endl;
+
+	  normalvecs.SetSize(surfind.Size());
+	  for (j = 1; j <= surfind.Size(); j++)
+	    ageometry.GetSurface(surfind.Get(j)) -> 
+	      GetNormalVector(apoints.Get(i), normalvecs.Elem(j));
+
+	  for (j = 1; j <= normalvecs.Size() - 1; j ++)
+	    for (k = j+1; k <= normalvecs.Size(); k++)
+	      for (l = 1; l <= 2; l++)
+		{
+		  t = Cross (normalvecs.Get(j), normalvecs.Get(k));
+		  if (t.Length2() < 1e-8)
+		    {
+		      cerr << "AnalyzePoint: Surfaces degenerated" << endl;
+		      break;
+		    }
+		  t /= t.Length();
+		  if (l == 2) t *= -1;
+
+		  if (locsol -> Edge (apoints.Get(i), t))
+		    {
+		      spi = 0;
+		      for (m = 1; m <= specpoints.Size(); m++)
+			if (Dist2 (specpoints.Get(m).p, apoints.Get(i)) < 1e-8
+			    && (specpoints.Get(m).v - t).Length2() < 1e-8)
+			  {
+			    spi = m;
+			    break;
+			  }
+		      if (!spi)
+			{
+			  spi = specpoints.Append (SpecialPoint());
+			  specpoint2point.Append (i);
+			  specpoints.Last().unconditional = 0;
+			}
+		      specpoints.Elem(spi).p = apoints.Get(i);
+		      specpoints.Elem(spi).v = t;
+		      if (surfind.Size() >= 3)
+			specpoints.Elem(spi).unconditional = 1;
+		      specpoints.Elem(spi).s1 = surfind.Get(j);
+		      specpoints.Elem(spi).s2 = surfind.Get(k);
+		      (*testout) << "spi = " << spi 
+				 << " uncond = " << specpoints.Get(spi).unconditional
+				 << " t = " << t << endl;
+		    }
+          
+		}
+	  delete locsol;
+	}
+    }
+
+  // if special point is unconditional on some solid,
+  // it must be unconditional everywhere:
+
+  BitArray uncond (apoints.Size());
+  uncond.Clear();
+
+  for (i = 1; i <= specpoints.Size(); i++)
+    if (specpoints.Get(i).unconditional)
+      uncond.Set (specpoint2point.Get(i));
+
+  for (i = 1; i <= specpoints.Size(); i++)
+    specpoints.Elem(i).unconditional = 
+      uncond.Test (specpoint2point.Get(i)) ? 1 : 0;
+}
+*/
+
+
+
+void SpecialPointCalculation :: 
+AnalyzeSpecialPoints (const CSGeometry & ageometry,
+		      ARRAY<MeshPoint> & apoints, 
+		      ARRAY<SpecialPoint> & specpoints)
+{
+  int si, i, j, k, l, m, spi;
+
+  Solid * locsol;
+  ARRAY<int> surfind;
+  ARRAY<int> surfind2;
+
+  ARRAY<Vec<3> > normalvecs;
+  const Solid * sol;
+  const Surface * surf;
+
+  Vec<3> t, nsurf;
+  Point<3> p;
+
+  ARRAY<int> specpoint2point;
+  specpoints.SetSize (0);
+
+  geometry = &ageometry;
+ 
+  (*testout) << "AnalyzeSpecialPoints\n";
+
+
+  Box<3> bbox;
+  if (apoints.Size())
+    bbox.Set (apoints[0]);
+  for (int i = 1; i < apoints.Size(); i++)
+    bbox.Add (apoints[i]);
+  bbox.Increase (0.1 * Dist (bbox.PMin(), bbox.PMax()));
+
+  Point3dTree searchtree (bbox.PMin(), bbox.PMax());
+  ARRAY<int> locsearch;
+
+  for (si = 0; si < ageometry.GetNTopLevelObjects(); si++)
+    {
+      (*testout) << "main solid " << si << "\n";
+
+      sol = ageometry.GetTopLevelObject(si)->GetSolid();
+      surf = ageometry.GetTopLevelObject(si)->GetSurface();
+
+      for (i = 0; i < apoints.Size(); i++)
+	{
+	  p = apoints[i];
+	  if (ageometry.GetTopLevelObject(si)->GetLayer() !=
+	      apoints[i].GetLayer())
+	    continue;
+
+	  (*testout) << "Point " << apoints[i] << "\n";
+
+	  sol -> TangentialSolid (p, locsol);
+	  if (!locsol) continue;
+	  
+	  // get all surface indices, 
+	  if (surf)
+	    {
+	      locsol -> GetSurfaceIndices (surfind);
+	      bool hassurf = 0;
+	      for (m = 0; m < surfind.Size(); m++)
+		if (ageometry.GetSurface(surfind[m]) == surf)
+		  hassurf = 1;
+
+	      if (!hassurf)
+		continue;
+
+	      surf->GetNormalVector (p, nsurf);
+	    }
+
+	  // get independent surfaces of tangential solid
+
+	  BoxSphere<3> box(p,p);
+	  box.Increase (1e-6);
+	  box.CalcDiamCenter();
+	  ageometry.GetIndependentSurfaceIndices (locsol, box, surfind);
+
+
+	  (*testout) << "surfind.size = " << surfind.Size() << endl;
+
+	  /*
+	  locsol -> GetSurfaceIndices (surfind);
+	  for (j = surfind.Size(); j >= 1; j--)
+	    if (fabs (ageometry.GetSurface(surfind.Get(j))->
+		      CalcFunctionValue (p)) > 1e-6)
+	      surfind.DeleteElement (j);
+	  */
+
+	  /*
+	  (*testout) << "Surfaces: ";
+	  for (j = 0; j < surfind.Size(); j++)
+	    (*testout) << surfind[j] << " ";
+	  (*testout) << "\n";
+	  */
+
+
+	  normalvecs.SetSize(surfind.Size());
+	  for (j = 0; j < surfind.Size(); j++)
+	    ageometry.GetSurface(surfind[j]) ->
+	      GetNormalVector(apoints[i], normalvecs[j]);
+
+	  for (j = 0; j < normalvecs.Size(); j++)
+	    for (k = j+1; k < normalvecs.Size(); k++)
+	      for (l = 1; l <= 2; l++)
+		{
+		  t = Cross (normalvecs[j], normalvecs[k]);
+		  if (Abs2 (t) < 1e-8)
+		    {
+		      cerr << "AnalyzePoint: Surfaces degenerated" << "\n";
+		      break;
+		    }
+		  t.Normalize();
+		  if (l == 2) t *= -1;
+
+		  // try tangential direction t
+
+		  // (*testout) << "check tangential " << t << "\n";
+
+		  if (surf && fabs (nsurf * t) > 1e-6)
+		    continue;
+
+		  if (!surf)
+		    {
+		      ageometry.GetIndependentSurfaceIndices 
+			(locsol, p, t, surfind2);
+		  
+		      bool found1 = 0, found2 = 0;
+		      for (int ii = 0; ii < surfind2.Size(); ii++)
+			{
+			  if (surfind2[ii] == surfind[j])
+			    found1 = 1;
+			  if (surfind2[ii] == surfind[k])
+			    found2 = 1;
+			}
+		      if (!found1 || !found2)
+			continue;
+		    }
+
+
+		  bool isedge;
+
+		  // isedge = locsol -> Edge (apoints.Get(i), t);
+		  
+		  // edge must be on tangential surface
+		  isedge = 
+		    locsol->VectorIn (p, t) &&
+		    !locsol->VectorStrictIn (p, t);
+		  
+		  // (*testout) << "isedge,1 = " << isedge << "\n";
+
+		  // there must exist at least two different faces on edge
+		  if (isedge)
+		    {
+		      int cnts = 0;
+		      for (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;
+
+			  /*
+			  (*testout) << "nv = " << normalvecs[m] << ", s = " << s << "\n";
+			  (*testout) << "t2a = " << t2a << ", t2b = " << t2b << "\n";
+			  (*testout) << "via = "
+				     << locsol->VectorIn (p, t2a) << "/"
+				     << locsol->VectorStrictIn (p, t2a);
+			  (*testout) << "vib = "
+				     << locsol->VectorIn (p, t2b) << "/"
+				     << locsol->VectorStrictIn (p, t2b) << "\n";
+			  */
+
+			  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)
+		    {
+		      spi = -1;
+
+		      searchtree.GetIntersecting (apoints[i]-Vec3d(1e-4,1e-4,1e-4), 
+						  apoints[i]+Vec3d(1e-4,1e-4,1e-4), 
+						  locsearch);
+		      
+		      for (m = 0; m < locsearch.Size(); m++)
+			if (Dist2 (specpoints[locsearch[m]].p, apoints[i]) < 1e-8
+			    && Abs2(specpoints[locsearch[m]].v - t) < 1e-8)
+			  {
+			    spi = locsearch[m];
+			    break;
+			  }
+
+		      /*
+		      for (m = 0; m < specpoints.Size(); m++)
+			if (Dist2 (specpoints[m].p, apoints[i]) < 1e-8
+			    && Abs2(specpoints[m].v - t) < 1e-8)
+			  {
+			    spi = m;
+			    break;
+			  }
+		      */
+		      if (spi == -1)
+			{
+			  spi = specpoints.Append (SpecialPoint()) - 1;
+			  specpoint2point.Append (i);
+			  specpoints.Last().unconditional = 0;
+			  searchtree.Insert (apoints[i], spi);
+			}
+		      specpoints[spi].p = apoints[i];
+		      specpoints[spi].v = t;
+		      if (surfind.Size() >= 3)
+			specpoints[spi].unconditional = 1;
+		      specpoints[spi].s1 = surfind[j];
+		      specpoints[spi].s2 = surfind[k];
+		      specpoints[spi].layer = apoints[i].GetLayer();
+		      for (int up = 0; up < geometry->GetNUserPoints(); up++)
+			if (Dist (geometry->GetUserPoint(up), apoints[i]) < 1e-10)
+			  specpoints[spi].unconditional = 1;
+			
+		      /*
+		      (*testout) << "spi = " << spi 
+				 << " uncond = " << specpoints[spi].unconditional
+				 << " t = " << t << "\n";
+		      */
+		    }
+          
+		}
+	  delete locsol;
+	}
+    }
+
+  // if special point is unconditional on some solid,
+  // it must be unconditional everywhere:
+
+  BitArray uncond (apoints.Size());
+  uncond.Clear();
+
+  for (i = 0; i < specpoints.Size(); i++)
+    if (specpoints[i].unconditional)
+      uncond.Set (specpoint2point[i]);
+  
+  for (i = 0; i < specpoints.Size(); i++)
+    specpoints[i].unconditional = 
+      uncond.Test (specpoint2point[i]) ? 1 : 0;
+}
+}
diff --git a/contrib/Netgen/libsrc/csg/specpoin_old.cpp b/contrib/Netgen/libsrc/csg/specpoin_old.cpp
new file mode 100644
index 0000000000..0f30ef7298
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/specpoin_old.cpp
@@ -0,0 +1,1370 @@
+#include <mystdlib.h>
+#include <meshing.hpp>
+#include <csg.hpp>
+
+
+/*
+
+   Special Point calculation uses the global Flags:
+
+
+   size .. 500       cube = [-size, size]^3
+   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 
+*/
+
+
+namespace netgen
+{
+void ProjectToEdge (const Surface * f1, const Surface * f2, Point<3> & hp);
+
+
+
+  /*
+SpecialPoint :: SpecialPoint ()
+  : p(), v(), layer(0)
+{
+  ;
+}
+  */
+
+
+SpecialPoint :: SpecialPoint (const SpecialPoint & sp)
+{
+  p = sp.p;
+  v = sp.v;
+  s1 = sp.s1;
+  s2 = sp.s2;
+  layer = sp.layer;
+  unconditional = sp.unconditional;
+}
+  
+SpecialPoint & SpecialPoint :: operator= (const SpecialPoint & sp)
+{
+  p = sp.p;
+  v = sp.v;
+  s1 = sp.s1;
+  s2 = sp.s2;
+  layer = sp.layer;
+  unconditional = sp.unconditional;
+  return *this;
+}
+
+/*
+bool SpecialPoint :: HasSurfaces (int as1, int as2) const
+{
+  return (s1 == as1 && s2 == as2 || s1 == as2 && s2 == as1);
+}
+*/
+void SpecialPoint :: Print (ostream & str)
+{
+  str << "p = " << p << "   v = " << v 
+      << " s1/s2 = " << s1 << "/" << s2
+      << " layer = " << layer
+      << endl;
+}
+
+
+
+
+SpecialPointCalculation :: SpecialPointCalculation ()
+{
+  ;  
+}
+
+void SpecialPointCalculation :: 
+CalcSpecialPoints (const CSGeometry & ageometry, 
+		   ARRAY<MeshPoint> & apoints)
+{
+  int i;
+
+  geometry = &ageometry;
+  points = &apoints;
+
+  size = geometry->MaxSize();  // globflags.GetNumFlag ("maxsize", 500);
+  (*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());
+
+  for (i = 0; i < geometry->GetNTopLevelObjects(); i++)
+    {
+      (*testout) << "tlo " << i << ":" << endl;
+      const TopLevelObject * tlo = geometry->GetTopLevelObject(i);
+      tlo->GetSolid()->Print (*testout); 
+      (*testout) << endl;
+      CalcSpecialPointsRec (tlo->GetSolid(), tlo->GetLayer(),
+			    box, 1, 1, 1);
+    }
+  
+  PrintDot ('\n');
+
+
+  // add user point:
+  int found = 0;
+  for (i = 0; i < geometry->GetNUserPoints(); i++)
+    AddPoint (geometry->GetUserPoint(i), 1);
+  
+  PrintMessage (3, apoints.Size(), " special points");
+
+  for (i = 0; i < boxesinlevel.Size(); i++)
+    (*testout) << "level " << i << " has " 
+	       << boxesinlevel[i] << " boxes" << endl;
+}
+
+
+
+int debug;
+void SpecialPointCalculation :: 
+CalcSpecialPointsRec (const Solid * sol, int layer,
+		      const BoxSphere<3> & box, 
+		      int level, bool calccp, bool calcep)
+{
+  if (multithread.terminate)
+    return;
+
+  int i;
+  BoxSphere<3> sbox;
+  Solid * redsol;
+
+  int numprim;
+
+  bool decision;
+  bool possiblecrossp, possibleexp;  // possible cross or extremalpoint
+  bool surecrossp, sureexp;          // sure ...
+  
+  static ARRAY<int> locsurf;  // attention: array is static
+
+
+  Point<3> p;
+  int k1, k2, k3;
+  int extremdir;
+  double hd;
+
+  if (!sol) return;
+
+
+  if (level >= 100)
+    {
+      cerr << "Problems in CalcSpecialPoints" << endl;
+      cerr << "Point: " << box.Center() << endl;
+      exit (1);
+    }
+
+  static int cntbox = 0;
+  cntbox++;
+  if (cntbox % 10000 == 0) 
+    PrintDot ();
+
+  if (level <= boxesinlevel.Size())
+    boxesinlevel.Elem(level)++;
+  else
+    boxesinlevel.Append (1);
+
+  /*
+  numprim = sol -> NumPrimitives();
+  sol -> GetSurfaceIndices (locsurf);
+  */
+
+  debug = 0;
+  // box.IsIn (Point<3> (4.9, 1.279, 2.8));
+
+
+  geometry -> GetIndependentSurfaceIndices (sol, box, locsurf);
+  numprim = locsurf.Size();
+  
+  if (debug)
+    {
+      (*testout) << "box = " << box.PMin() << "-" << box.PMax()
+		 << " np = " << numprim << " : ";
+      for (i = 1; i <= locsurf.Size(); i++)
+	(*testout) << " " << locsurf.Get(i);
+      (*testout) << " diam = " << box.Diam();
+      (*testout) << " numprim = " << numprim;
+      (*testout) << endl;
+    }
+
+  p = box.Center();
+  
+  /*
+  (*testout) << "box = " << box.PMin() << " - " << box.PMax()
+	     << ", lev = " << level 
+	     << ", nprim = " << sol->NumPrimitives() 
+	     << ", lsurf = " << locsurf.Size() << endl;
+
+  for (i = 1; i <= locsurf.Size(); i++)
+    geometry->GetSurface (locsurf.Get(i)) -> Print (*testout);
+  sol -> Print (*testout);
+  */
+
+  /*
+    for (i = 1; i <= locsurf.Size(); i++)
+    (*testout) << locsurf.Get(i) << " ";
+    (*testout) << "C = " << box.Center() << " diam = " << box.Diam() << endl;
+    */
+
+  possiblecrossp = (numprim >= 3) && calccp;
+  surecrossp = 0;
+
+  if (possiblecrossp && (locsurf.Size() <= 10))
+    {
+      decision = 1;
+      surecrossp = 0;
+
+      for (k1 = 1; k1 <= locsurf.Size() - 2; k1++)
+	for (k2 = k1 + 1; k2 <= locsurf.Size() - 1; k2++)
+	  for (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)), p );
+	      
+	      deg = CrossPointDegenerated 
+		(geometry->GetSurface(locsurf.Get(k1)), 
+		 geometry->GetSurface(locsurf.Get(k2)), 
+		 geometry->GetSurface(locsurf.Get(k3)), box );
+	      
+	      if (!nc && !deg) decision = 0;
+	      if (nc) surecrossp = 1;
+	    }
+
+      if (decision && surecrossp)
+	{
+	  for (k1 = 1; k1 <= locsurf.Size() - 2; k1++)
+	    for (k2 = k1 + 1; k2 <= locsurf.Size() - 1; k2++)
+	      for (k3 = k2 + 1; k3 <= locsurf.Size(); k3++)
+		{
+		  if (CrossPointNewtonConvergence 
+		      (geometry->GetSurface(locsurf.Get(k1)), 
+		       geometry->GetSurface(locsurf.Get(k2)), 
+		       geometry->GetSurface(locsurf.Get(k3)), p ) )
+		    {
+		      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);
+
+		      if (pp(0) > box.PMin()(0) - 1e-5 && 
+			  pp(0) < box.PMax()(0) + 1e-5 &&
+			  pp(1) > box.PMin()(1) - 1e-5 && 
+			  pp(1) < box.PMax()(1) + 1e-5 &&
+			  pp(2) > box.PMin()(2) - 1e-5 && 
+			  pp(2) < box.PMax()(2) + 1e-5 &&
+			  sol -> IsIn (pp) && !sol->IsStrictIn (pp) &&
+			  !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);
+			  boxp.CalcDiamCenter();
+			  ARRAY<int> locsurf2;
+
+			  geometry -> GetIndependentSurfaceIndices (sol, boxp, locsurf2);
+			  
+			  /*
+			  ReducePrimitiveIterator rpi(boxp);
+			  UnReducePrimitiveIterator urpi;
+			  
+			  ((Solid*)sol) -> IterateSolid (rpi);
+			  sol -> GetIndependentSurfaceIndices (locsurf2);
+			  ((Solid*)sol) -> IterateSolid (urpi);
+			  */
+			  bool found1 = 0, found2 = 0, found3 = 0;
+			  for (i = 1; i <= locsurf2.Size(); i++)
+			    {
+			      if (locsurf2.Get(i) == locsurf.Get(k1))
+				found1 = 1;
+			      if (locsurf2.Get(i) == locsurf.Get(k2))
+				found2 = 1;
+			      if (locsurf2.Get(i) == locsurf.Get(k3))
+				found3 = 1;
+			    }
+			  
+			  if (found1 && found2 && found3)
+			    if (AddPoint (pp, layer))
+			      {
+				(*testout) << "Crosspoint found: " << pp 
+					   << " diam = " << box.Diam() << endl;
+				(*testout) << "surfs: " 
+					   << locsurf.Get(k1) << "," 
+					   << locsurf.Get(k2) << "," 
+					   << locsurf.Get(k3) << endl;
+			      }
+			}
+		    }
+		}
+	}
+      
+      if (decision)
+	possiblecrossp = 0;
+    }
+
+
+
+
+  possibleexp = (numprim >= 2) && calcep;
+    
+  if (possibleexp && (locsurf.Size() <= 10))
+    {
+      decision = 1;
+      sureexp = 0;
+
+      for (k1 = 1; k1 <= locsurf.Size() - 1; k1++)
+	for (k2 = k1+1; k2 <= locsurf.Size(); k2++)
+	  {
+	    bool nc, deg;
+	    
+	    nc = EdgeNewtonConvergence 
+	      (geometry->GetSurface(locsurf.Get(k1)),
+	       geometry->GetSurface(locsurf.Get(k2)),
+	       p);
+	    
+	    deg = EdgeDegenerated 
+	      (geometry->GetSurface(locsurf.Get(k1)),
+	       geometry->GetSurface(locsurf.Get(k2)),
+	       box);
+	    
+	    if (!nc && !deg) decision = 0;
+	    if (nc) sureexp = 1;
+
+	    if (debug)
+	      {
+		(*testout) << "p = " << p << " s1,2 = " << locsurf.Get(k1) << ", " << locsurf.Get(k2) 
+			   << " nc = " << nc << " deg = " << deg << endl;
+	      }
+	  }
+
+      if (decision && sureexp)
+	{
+	  for (k1 = 1; k1 <= locsurf.Size() - 1; k1++)
+	    for (k2 = k1+1; k2 <= locsurf.Size(); k2++)
+	      {
+		if (
+		    EdgeNewtonConvergence 
+		    (geometry->GetSurface(locsurf.Get(k1)),
+		     geometry->GetSurface(locsurf.Get(k2)),
+		     p) )
+		  {
+		    EdgeNewton 
+		      (geometry->GetSurface(locsurf.Get(k1)),
+		       geometry->GetSurface(locsurf.Get(k2)),
+		       p);
+		    
+		    Point<3> pp;
+		    if (IsEdgeExtremalPoint 
+		      (geometry->GetSurface(locsurf.Get(k1)),
+		       geometry->GetSurface(locsurf.Get(k2)),
+		       p, pp, box.Diam()/2))
+			{
+			  (*testout) << "extremalpoint (nearly) found:" 
+				     << pp
+				     << endl;
+			  if (Dist (pp, box.Center()) < box.Diam()/2 &&
+			      sol -> IsIn (pp) && !sol->IsStrictIn (pp) )
+			    {
+			      //                AddExtremalPoint (locsurf.Get(k1), locsurf.Get(k2), p);
+			      if (AddPoint (pp, layer))
+				(*testout) << "Extremal point found: " << pp << endl;
+			    }  
+			}            
+		  }
+	      }
+	}
+      if (decision)
+	possibleexp = 0;
+    }
+ 
+
+
+  if (possiblecrossp || possibleexp)
+    {
+      for (i = 0; i < 8; i++)
+	{
+	  box.GetSubBox (i, sbox);
+	  sbox.Increase (1e-4 * sbox.Diam());
+
+	  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 Point<3> & p)
+{
+  int i;
+  Vec<3> grad;
+  Vec<3> rs, x;
+  Mat<3> jacobi, inv;
+  double alpha, beta, gamma, eta;
+  double sum;
+  int j;
+
+
+  rs(0) = f1->CalcFunctionValue (p);
+  rs(1) = f2->CalcFunctionValue (p);
+  rs(2) = f3->CalcFunctionValue (p);
+
+  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);
+
+  alpha = 1;
+  if (fabs (Det (jacobi)) > 1e-8)
+    {
+      CalcInverse (jacobi, inv);
+      x = inv * rs;
+
+      gamma = f1 -> HesseNorm() + f2 -> HesseNorm() + f3 -> HesseNorm();
+      beta = 0;
+      for (i = 0; i < 3; i++)
+	{
+	  sum = 0;
+	  for (j = 0; j < 3; j++)
+	    sum += fabs (inv(i,j));
+	  beta = max2 (beta, sum);
+	}
+      eta = Abs (x);
+
+      alpha = beta * gamma * eta;
+    }
+
+  return (alpha < 0.1);
+}
+
+
+
+
+bool SpecialPointCalculation :: 
+CrossPointDegenerated (const Surface * f1,
+		       const Surface * f2, 
+		       const Surface * f3, 
+		       const BoxSphere<3> & box) const
+{
+  Mat<3> mat;
+  Vec<3> grad, g1, g2, g3;
+  double normprod;
+
+  if (box.Diam() > relydegtest) return 0;
+
+  f1->CalcGradient (box.Center(), g1);
+  normprod = Abs (g1);
+
+  f2->CalcGradient (box.Center(), g2);
+  normprod *= Abs (g2);
+ 
+  f3->CalcGradient (box.Center(), g3);
+  normprod *= Abs (g3);
+
+  for (int i = 0; i < 3; i++)
+    {
+      mat(i,0) = g1(i);
+      mat(i,1) = g2(i);
+      mat(i,2) = g3(i);
+    }
+
+  if (fabs (Det (mat)) < cpeps1 * normprod)
+    return 1;
+  else 
+    return 0;
+}
+ 
+
+
+
+
+void SpecialPointCalculation :: CrossPointNewton (const Surface * f1, 
+						  const Surface * f2, 
+						  const Surface * f3, Point<3> & p)
+{
+  int i;
+  Vec<3> g1, g2, g3;
+  Vec<3> rs, sol;
+  Mat<3> mat;
+
+  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);
+	  /*
+      Transpose (g1, g2, g3);
+      SolveLinearSystem (g1, g2, g3, rs, sol);
+	  */
+      if (sol.Length() < 1e-12 && i > 1) i = 1;
+
+      p -= sol;
+    }
+}
+
+
+
+
+/******* Tests for Point on edges **********************/
+
+
+
+
+bool SpecialPointCalculation :: 
+EdgeNewtonConvergence (const Surface * f1, const Surface * f2, 
+		       const Point<3> & p)
+{
+  int i;
+  Vec<3> g1, g2, sol;
+  Vec<2> vrs;
+  double alpha, beta, gamma, eta;
+  double sum;
+  Mat<2,3> mat;
+  Mat<3,2> inv;
+  int j;
+
+  vrs(0) = f1->CalcFunctionValue (p);
+  vrs(1) = f2->CalcFunctionValue (p);
+
+  f1->CalcGradient (p, g1);
+  f2->CalcGradient (p, g2);
+
+  for (i = 0; i < 3; i++)
+    {
+      mat(0,i) = g1(i);
+      mat(1,i) = g2(i);
+    }
+
+  alpha = 1;
+
+  if ( fabs(g1 * g2) < (1 - 1e-8) * Abs (g1) * Abs (g2))
+    {
+      CalcInverse (mat, inv);
+      sol = inv * vrs;
+
+      // SolveLinearSystemLS (g1, g2, vrs, sol);
+
+      gamma = f1 -> HesseNorm() + f2 -> HesseNorm();
+
+      /*
+      Vec<3> inv1, inv2;
+      PseudoInverse (g1, g2, inv1, inv2);
+      */
+      
+      beta = 0;
+      for (i = 0; i < 3; i++)
+	for (j = 0; j < 2; j++)
+	  beta += inv(i,j) * inv(i,j);
+      beta = sqrt (beta);
+
+      //      beta = inv1.Length() + inv2.Length();
+      eta = Abs (sol);
+      alpha = beta * gamma * eta;
+    }
+  return (alpha < 0.1);
+}
+
+
+
+
+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();
+  int i;
+  Vec<3> grad, g1, g2, sol;
+  Vec<2> vrs;
+  Mat<2,3> mat;
+
+  i = 20;
+  while (i > 0)
+    {
+      if (Dist (p, box.Center()) > box.Diam())
+	return 0;
+
+      i--;
+      vrs(0) = f1->CalcFunctionValue (p);
+      vrs(1) = f2->CalcFunctionValue (p);
+
+      f1->CalcGradient (p, g1);
+      f2->CalcGradient (p, g2);
+
+      if ( fabs (g1 * g2) > (1 - 1e-10) * Abs (g1) * Abs (g2))
+	return 1;
+
+      for (int j = 0; j < 3; j++)
+	{
+	  mat(0,j) = g1(j);
+	  mat(1,j) = g2(j);
+	}
+      mat.Solve (vrs, sol);
+      //      SolveLinearSystemLS (g1, g2, vrs, sol);
+
+      if (Abs (sol) < 1e-12 && i > 1) i = 1;
+      p -= sol;
+    }
+
+  return 0;
+  /*
+  return 0;
+
+  static DenseMatrix jacobi(3);
+  Vec<3> grad, g1, g2, g3;
+  double normprod;
+  
+  if (box.Diam() > relydegtest) return 0;
+  
+  f1->CalcGradient (box.Center(), g1);
+  normprod = g1.Length();
+  
+  f2->CalcGradient (box.Center(), g2);
+  normprod *= g2.Length();
+  
+  if (fabs (g1 * g2) < 1e-8 * normprod)
+    return 1;
+  else 
+    return 0;
+  */
+}
+
+
+
+
+
+
+void SpecialPointCalculation :: EdgeNewton (const Surface * f1, 
+					    const Surface * f2, Point<3> & p)
+{
+  int i;
+  Vec<3> grad, g1, g2, sol;
+  Vec<2> vrs;
+  Mat<2,3> mat;
+
+  i = 10;
+  while (i > 0)
+    {
+      i--;
+      vrs(0) = f1->CalcFunctionValue (p);
+      vrs(1) = f2->CalcFunctionValue (p);
+
+      f1->CalcGradient (p, g1);
+      f2->CalcGradient (p, g2);
+
+      for (int j = 0; j < 3; j++)
+	{
+	  mat(0,j) = g1(j);
+	  mat(1,j) = g2(j);
+	}
+      mat.Solve (vrs, sol);
+      //      SolveLinearSystemLS (g1, g2, vrs, sol);
+
+      if (Abs (sol) < 1e-12 && 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;
+  int j;
+
+  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 (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)
+{
+  int i;
+
+  Vec<3> g1, g2, v, curv;
+  Vec<3> rs, x, y1, y2, y;
+  Mat<3> h1, h2;
+  Mat<3> jacobi;
+
+
+  if (dir < 1 || dir > 3)
+    {
+      cerr << "Error: Illegal extremdir" << endl;
+      return;
+    }
+
+  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);
+
+      jacobi.Solve (rs, x);
+      /*
+      CalcInverse (jacobi, inv);
+      inv.Mult (rs, x);
+      */
+      //    (*testout) << "err = " << x.L2Norm() << endl;
+
+      if (Abs (x) < 1e-12 && i > 1)
+	{
+	  //      (*testout) << "convergent in " << (10 - i) << " steps " << endl;
+
+	  i = 1;
+	}
+      
+      p -= x;
+    }
+
+  if (Abs (x) > 1e-10)
+    {
+      (*testout) << "Error: extremum Newton not convergent" << endl;
+      (*testout) << "dir = " << dir << endl;
+      (*testout) << "p = " << p << endl;
+      (*testout) << "x = " << x << 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);
+
+  /*
+  CalcInverse (a, inv);
+  inv.Mult (rs, x);          // x .. Lagrangeparameter
+  */
+  //  (*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 0;
+
+  points->Append (MeshPoint(p, layer));
+  return 1;
+}
+
+
+
+
+
+
+
+/*
+void SpecialPointCalculation :: 
+AnalyzeSpecialPoints (const CSGeometry & ageometry,
+		      ARRAY<Point<3> > & apoints, 
+		      ARRAY<SpecialPoint> & specpoints)
+{
+  int si, i, j, k, l, m, spi;
+  Solid * locsol;
+  ARRAY<int> surfind;
+  ARRAY<Vec<3>> normalvecs;
+  const Solid * sol;
+  Vec<3> t;
+  Point<3> p;
+
+  ARRAY<int> specpoint2point;
+  specpoints.SetSize (0);
+ 
+  (*testout) << "AnalyzeSpecialPoints" << endl;
+
+  for (si = 1; si <= ageometry.GetNTopLevelObjects(); si++)
+    {
+      (*testout) << "main solid " << si << endl;
+
+      sol = ageometry.GetTopLevelObject(si)->GetSolid();
+      for (i = 1; i <= apoints.Size(); i++)
+	{
+	  p = apoints.Get(i);
+
+	  sol -> TangentialSolid (p, locsol);
+	  if (!locsol) continue;
+	  
+	  (*testout) << "Point " << apoints.Get(i) << endl;
+
+	  locsol -> GetSurfaceIndices (surfind);
+	  for (j = surfind.Size(); j >= 1; j--)
+	    if (fabs (ageometry.GetSurface(surfind.Get(j))->
+		      CalcFunctionValue (p)) > 1e-6)
+	      surfind.DeleteElement (j);
+
+
+
+	  (*testout) << "Surfaces: ";
+	  for (j = 1; j <= surfind.Size(); j++)
+	    (*testout) << surfind.Get(j) << " ";
+	  (*testout) << endl;
+
+	  normalvecs.SetSize(surfind.Size());
+	  for (j = 1; j <= surfind.Size(); j++)
+	    ageometry.GetSurface(surfind.Get(j)) -> 
+	      GetNormalVector(apoints.Get(i), normalvecs.Elem(j));
+
+	  for (j = 1; j <= normalvecs.Size() - 1; j ++)
+	    for (k = j+1; k <= normalvecs.Size(); k++)
+	      for (l = 1; l <= 2; l++)
+		{
+		  t = Cross (normalvecs.Get(j), normalvecs.Get(k));
+		  if (t.Length2() < 1e-8)
+		    {
+		      cerr << "AnalyzePoint: Surfaces degenerated" << endl;
+		      break;
+		    }
+		  t /= t.Length();
+		  if (l == 2) t *= -1;
+
+		  if (locsol -> Edge (apoints.Get(i), t))
+		    {
+		      spi = 0;
+		      for (m = 1; m <= specpoints.Size(); m++)
+			if (Dist2 (specpoints.Get(m).p, apoints.Get(i)) < 1e-8
+			    && (specpoints.Get(m).v - t).Length2() < 1e-8)
+			  {
+			    spi = m;
+			    break;
+			  }
+		      if (!spi)
+			{
+			  spi = specpoints.Append (SpecialPoint());
+			  specpoint2point.Append (i);
+			  specpoints.Last().unconditional = 0;
+			}
+		      specpoints.Elem(spi).p = apoints.Get(i);
+		      specpoints.Elem(spi).v = t;
+		      if (surfind.Size() >= 3)
+			specpoints.Elem(spi).unconditional = 1;
+		      specpoints.Elem(spi).s1 = surfind.Get(j);
+		      specpoints.Elem(spi).s2 = surfind.Get(k);
+		      (*testout) << "spi = " << spi 
+				 << " uncond = " << specpoints.Get(spi).unconditional
+				 << " t = " << t << endl;
+		    }
+          
+		}
+	  delete locsol;
+	}
+    }
+
+  // if special point is unconditional on some solid,
+  // it must be unconditional everywhere:
+
+  BitArray uncond (apoints.Size());
+  uncond.Clear();
+
+  for (i = 1; i <= specpoints.Size(); i++)
+    if (specpoints.Get(i).unconditional)
+      uncond.Set (specpoint2point.Get(i));
+
+  for (i = 1; i <= specpoints.Size(); i++)
+    specpoints.Elem(i).unconditional = 
+      uncond.Test (specpoint2point.Get(i)) ? 1 : 0;
+}
+*/
+
+
+
+void SpecialPointCalculation :: 
+AnalyzeSpecialPoints (const CSGeometry & ageometry,
+		      ARRAY<MeshPoint> & apoints, 
+		      ARRAY<SpecialPoint> & specpoints)
+{
+  int si, i, j, k, l, m, spi;
+
+  Solid * locsol;
+  ARRAY<int> surfind;
+  ARRAY<int> surfind2;
+
+  ARRAY<Vec<3> > normalvecs;
+  const Solid * sol;
+  const Surface * surf;
+
+  Vec<3> t, nsurf;
+  Point<3> p;
+
+  ARRAY<int> specpoint2point;
+  specpoints.SetSize (0);
+
+  geometry = &ageometry;
+ 
+  (*testout) << "AnalyzeSpecialPoints\n";
+
+  for (si = 0; si < ageometry.GetNTopLevelObjects(); si++)
+    {
+      (*testout) << "main solid " << si << "\n";
+
+      sol = ageometry.GetTopLevelObject(si)->GetSolid();
+      surf = ageometry.GetTopLevelObject(si)->GetSurface();
+
+      for (i = 0; i < apoints.Size(); i++)
+	{
+	  p = apoints[i];
+	  if (ageometry.GetTopLevelObject(si)->GetLayer() !=
+	      apoints[i].GetLayer())
+	    continue;
+
+	  // (*testout) << "Point " << apoints[i] << "\n";
+
+	  sol -> TangentialSolid (p, locsol);
+	  if (!locsol) continue;
+	  
+	  // get all surface indices, 
+	  if (surf)
+	    {
+	      locsol -> GetSurfaceIndices (surfind);
+	      bool hassurf = 0;
+	      for (m = 0; m < surfind.Size(); m++)
+		if (ageometry.GetSurface(surfind[m]) == surf)
+		  hassurf = 1;
+
+	      if (!hassurf)
+		continue;
+
+	      surf->GetNormalVector (p, nsurf);
+	    }
+
+	  // get independent surfaces of tangential solid
+
+	  BoxSphere<3> box(p,p);
+	  box.Increase (1e-6);
+	  box.CalcDiamCenter();
+	  ageometry.GetIndependentSurfaceIndices (locsol, box, surfind);
+
+
+	  /*
+	  locsol -> GetSurfaceIndices (surfind);
+	  for (j = surfind.Size(); j >= 1; j--)
+	    if (fabs (ageometry.GetSurface(surfind.Get(j))->
+		      CalcFunctionValue (p)) > 1e-6)
+	      surfind.DeleteElement (j);
+	  */
+
+	  /*
+	  (*testout) << "Surfaces: ";
+	  for (j = 0; j < surfind.Size(); j++)
+	    (*testout) << surfind[j] << " ";
+	  (*testout) << "\n";
+	  */
+
+
+	  normalvecs.SetSize(surfind.Size());
+	  for (j = 0; j < surfind.Size(); j++)
+	    ageometry.GetSurface(surfind[j]) ->
+	      GetNormalVector(apoints[i], normalvecs[j]);
+
+	  for (j = 0; j < normalvecs.Size(); j++)
+	    for (k = j+1; k < normalvecs.Size(); k++)
+	      for (l = 1; l <= 2; l++)
+		{
+		  t = Cross (normalvecs[j], normalvecs[k]);
+		  if (Abs2 (t) < 1e-8)
+		    {
+		      cerr << "AnalyzePoint: Surfaces degenerated" << "\n";
+		      break;
+		    }
+		  t.Normalize();
+		  if (l == 2) t *= -1;
+
+		  // try tangential direction t
+
+		  // (*testout) << "check tangential " << t << "\n";
+
+		  if (surf && fabs (nsurf * t) > 1e-6)
+		    continue;
+
+		  if (!surf)
+		    {
+		      ageometry.GetIndependentSurfaceIndices 
+			(locsol, p, t, surfind2);
+		  
+		      bool found1 = 0, found2 = 0;
+		      for (int ii = 0; ii < surfind2.Size(); ii++)
+			{
+			  if (surfind2[ii] == surfind[j])
+			    found1 = 1;
+			  if (surfind2[ii] == surfind[k])
+			    found2 = 1;
+			}
+		      if (!found1 || !found2)
+			continue;
+		    }
+
+
+		  bool isedge;
+
+		  // isedge = locsol -> Edge (apoints.Get(i), t);
+		  
+		  // edge must be on tangential surface
+		  isedge = 
+		    locsol->VectorIn (p, t) &&
+		    !locsol->VectorStrictIn (p, t);
+		  
+		  // (*testout) << "isedge,1 = " << isedge << "\n";
+
+		  // there must exist at least two different faces on edge
+		  if (isedge)
+		    {
+		      int cnts = 0;
+		      for (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;
+
+			  /*
+			  (*testout) << "nv = " << normalvecs[m] << ", s = " << s << "\n";
+			  (*testout) << "t2a = " << t2a << ", t2b = " << t2b << "\n";
+			  (*testout) << "via = "
+				     << locsol->VectorIn (p, t2a) << "/"
+				     << locsol->VectorStrictIn (p, t2a);
+			  (*testout) << "vib = "
+				     << locsol->VectorIn (p, t2b) << "/"
+				     << locsol->VectorStrictIn (p, t2b) << "\n";
+			  */
+
+			  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)
+		    {
+		      spi = -1;
+		      for (m = 0; m < specpoints.Size(); m++)
+			if (Dist2 (specpoints[m].p, apoints[i]) < 1e-8
+			    && Abs2(specpoints[m].v - t) < 1e-8)
+			  {
+			    spi = m;
+			    break;
+			  }
+		      if (spi == -1)
+			{
+			  spi = specpoints.Append (SpecialPoint()) - 1;
+			  specpoint2point.Append (i);
+			  specpoints.Last().unconditional = 0;
+			}
+		      specpoints[spi].p = apoints[i];
+		      specpoints[spi].v = t;
+		      if (surfind.Size() >= 3)
+			specpoints[spi].unconditional = 1;
+		      specpoints[spi].s1 = surfind[j];
+		      specpoints[spi].s2 = surfind[k];
+		      specpoints[spi].layer = apoints[i].GetLayer();
+		      for (int up = 0; up < geometry->GetNUserPoints(); up++)
+			if (Dist (geometry->GetUserPoint(up), apoints[i]) < 1e-10)
+			  specpoints[spi].unconditional = 1;
+			
+		      /*
+		      (*testout) << "spi = " << spi 
+				 << " uncond = " << specpoints[spi].unconditional
+				 << " t = " << t << "\n";
+		      */
+		    }
+          
+		}
+	  delete locsol;
+	}
+    }
+
+  // if special point is unconditional on some solid,
+  // it must be unconditional everywhere:
+
+  BitArray uncond (apoints.Size());
+  uncond.Clear();
+
+  for (i = 0; i < specpoints.Size(); i++)
+    if (specpoints[i].unconditional)
+      uncond.Set (specpoint2point[i]);
+  
+  for (i = 0; i < specpoints.Size(); i++)
+    specpoints[i].unconditional = 
+      uncond.Test (specpoint2point[i]) ? 1 : 0;
+}
+}
diff --git a/contrib/Netgen/libsrc/csg/spline3d.cpp b/contrib/Netgen/libsrc/csg/spline3d.cpp
new file mode 100644
index 0000000000..455493ad6f
--- /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;
+  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 0000000000..753788459f
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/spline3d.hpp
@@ -0,0 +1,92 @@
+///
+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 0000000000..8b8d58127a
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/surface.cpp
@@ -0,0 +1,392 @@
+#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;
+}
+
+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;
+    }
+}
+
+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 (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();
+
+  cout << "cannot create primitive " << classname << endl;
+  return NULL;
+}
+
+
+Primitive * Primitive :: Copy () const
+{
+  cout << "Primitive called for baseclass" << endl;
+  return NULL;
+}
+
+
+void Primitive :: Transform (Transformation<3> & trans)
+{
+  cout << "transform called for baseclass" << endl;
+}
+
+
+INSOLID_TYPE Primitive :: 
+VecInSolid2 (const Point<3> & p,
+	     const Vec<3> & v1,
+	     const Vec<3> & v2,
+	     double eps) const
+{
+  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;
+}
+
+
+
+
+
+
+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
+{
+  Vec<3> hv;
+  double hv1;
+  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
+{
+  Vec<3> hv;
+  double hv1, hv2;
+
+  GetSurface(0).CalcGradient (p, hv);
+
+  hv1 = v1 * hv;
+  if (hv1 <= -eps)
+    return IS_INSIDE;
+  if (hv1 >= eps)
+    return IS_OUTSIDE;
+
+  hv2 = v2 * hv;
+  if (hv2 <= 0)
+    return IS_INSIDE;
+  else
+    return IS_OUTSIDE;
+}
+  
+
+
+
+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);
+
+      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 0000000000..db0b4a74d2
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/surface.hpp
@@ -0,0 +1,301 @@
+#ifndef FILE_SURFACE
+#define FILE_SURFACE
+
+/**************************************************************************/
+/* File:   surface.hh                                                     */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   1. Dez. 95                                                     */
+/**************************************************************************/
+
+
+
+
+// class DenseMatrix;
+// class Box3dSphere;
+class TriangleApproximation;
+
+/**
+  Basis class for implicit surface geometry.
+  This class is used for generation of surface meshes
+  in NETGEN as well as for mesh refinement in FEPP.
+  */
+
+
+
+
+class Surface
+{
+protected:
+  /// invert normal vector
+  bool inverse;
+  /// maximal h in surface
+  double maxh;
+  /// name of surface
+  char * name;
+  /// boundary condition nr
+  int bcprop;
+  ///
+  
+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 point 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;
+  //@}
+
+
+  /// Move Point p to closes point in surface
+  virtual void Project (Point<3> & p) const;
+
+
+  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 { };
+
+#ifdef MYGRAPH  
+  ///
+  virtual void Plot (const class ROT3D & /* rot */) const { };
+#endif
+  };
+
+
+typedef enum { IS_OUTSIDE = 0, IS_INSIDE = 1, DOES_INTERSECT = 2}
+INSOLID_TYPE;
+
+
+
+
+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 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;
+
+  
+  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 (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 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 0000000000..403716155b
--- /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 0000000000..7b2db16b09
--- /dev/null
+++ b/contrib/Netgen/libsrc/csg/triapprox.hpp
@@ -0,0 +1,57 @@
+#ifndef FILE_TRIAPPROX
+#define FILE_TRIAPPROX
+
+/**************************************************************************/
+/* File:   triapprox.hh                                                   */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   2. Mar. 98                                                    */
+/**************************************************************************/
+
+/**
+   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/general/Makefile b/contrib/Netgen/libsrc/general/Makefile
new file mode 100644
index 0000000000..65a387a844
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for general purpose data types
+#
+src = array.cpp bitarray.cpp hashtabl.cpp symbolta.cpp table.cpp flags.cpp \
+	spbita2d.cpp seti.cpp optmem.cpp sort.cpp mystring.cpp parthreads.cpp \
+	moveablemem.cpp dynamicmem.cpp ngexception.cpp
+#	
+lib = gen
+libpath = libsrc/general
+#
+include ../makefile.inc
+
diff --git a/contrib/Netgen/libsrc/general/array.cpp b/contrib/Netgen/libsrc/general/array.cpp
new file mode 100644
index 0000000000..e3bc3e6fdf
--- /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 0000000000..c953cedeff
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/array.hpp
@@ -0,0 +1,478 @@
+#ifndef FILE_ARRAY
+#define FILE_ARRAY
+
+/**************************************************************************/
+/* File:   array.hpp                                                      */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Jun. 95                                                    */
+/**************************************************************************/
+
+
+/**
+   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
+  inline FlatArray (int asize, T * adata) 
+    : size(asize), data(adata) { ; }
+
+  /// the size
+  inline int Size() const { return size; }
+
+
+  /// access array. 
+  inline T & operator[] (int i) 
+  { 
+#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. 
+  inline const 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]; 
+  }
+
+  ///
+  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]; 
+  }
+  
+  ///
+  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]; 
+  }
+
+  ///
+  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 last element. check by macro CHECK_RANGE
+  T & Last ()
+  {
+    return data[size-1];
+  }
+
+  /// access last element. check by macro CHECK_RANGE
+  const 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;
+  }
+};
+
+
+
+
+// print array
+template <class T, int BASE>
+inline ostream & operator<< (ostream & s, const FlatArray<T,BASE> & a)
+{
+  for (int i = BASE; i < a.Size()+BASE; 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:
+  /// 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 = this->size;
+    ownmem = 1;
+    for (int i = BASE; i < this->size+BASE; i++)
+      (*this)[i] = a2[i];
+  }
+
+
+
+  /// if responsible, deletes memory
+  ~ARRAY()
+  {
+    if (ownmem)
+      delete [] this->data;
+  }
+
+  /// Change logical size. If necessary, do reallocation. Keeps contents.
+  void SetSize(int nsize)
+  {
+    if (nsize > allocsize) 
+      ReSize (nsize);
+    this->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 (this->size == allocsize) 
+      ReSize (this->size+1);
+    this->data[this->size] = el;
+    this->size++;
+    return this->size;
+  }
+
+
+  /// Delete element i (0-based). Move last element to position i.
+  void Delete (int i)
+  {
+#ifdef CHECK_ARRAY_RANGE
+    RangeCheck (i+1);
+#endif
+
+    this->data[i] = this->data[this->size-1];
+    this->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
+
+    this->data[i-1] = this->data[this->size-1];
+    this->size--;
+  }
+
+  /// Delete last element. 
+  void DeleteLast ()
+  {
+    this->size--;
+  }
+
+  /// Deallocate memory
+  void DeleteAll ()
+  {
+    if (ownmem)
+      delete [] this->data;
+    this->data = 0;
+    this->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 < this->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 (this->data)
+      {
+	T * p = new T[nsize];
+	
+	int mins = (nsize < this->size) ? nsize : this->size; 
+	memcpy (p, this->data, mins * sizeof(T));
+
+	if (ownmem)
+	  delete [] this->data;
+	ownmem = 1;
+	this->data = p;
+      }
+    else
+      {
+	this->data = new T[nsize];
+	ownmem = 1;
+      }
+    
+    allocsize = nsize;
+  }
+};
+
+
+
+template <class T, int S> 
+class ArrayMem : public ARRAY<T>
+{
+  // T mem[S];
+  // 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])))
+  {
+    this->SetSize (asize);
+  }
+
+  ArrayMem & operator= (const T & val)  
+  {
+    ARRAY<T>::operator= (val);
+    return *this;
+  }
+};
+
+
+
+
+
+
+
+
+
+
+
+///
+template <class T> class MoveableArray 
+{
+  int size;
+  int allocsize;
+  MoveableMem<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]; }
+
+  ///
+  const T & operator[] (int i) const
+  { return ((const T*)data)[i]; }
+
+  ///
+  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;
+  }
+
+  void SetName (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;
+}
+
+
+
+
+
+
+
+
+#endif
+
diff --git a/contrib/Netgen/libsrc/general/autoptr.hpp b/contrib/Netgen/libsrc/general/autoptr.hpp
new file mode 100644
index 0000000000..b90841408b
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/autoptr.hpp
@@ -0,0 +1,31 @@
+#ifndef FILE_AUTOPTR
+#define FILE_AUTOPTR
+
+/**************************************************************************/
+/* File:   autoptr.hpp                                                    */
+/* Author: STL, Joachim Schoeberl                                         */
+/* Date:   29. Dec. 02                                                    */
+/**************************************************************************/
+
+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 0000000000..0d03fd5c45
--- /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 0000000000..1b7042fa5a
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/bitarray.hpp
@@ -0,0 +1,207 @@
+#ifndef FILE_BitArray
+#define FILE_BitArray
+
+/**************************************************************************/
+/* File:   bitarray.hpp                                                   */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Jun. 95                                                    */
+/**************************************************************************/
+
+#include <limits.h>
+
+/**
+   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);
+  ///
+  inline INDEX Size () const;
+
+  ///
+  void Set ();
+  ///
+  inline void Set (INDEX i);
+  ///
+  void Clear ();
+  ///
+  inline void Clear (INDEX i);
+  ///
+  inline int Test (INDEX i) const;
+  ///
+  void Invert ();
+  ///
+  void And (const BitArray & ba2);
+  ///
+  void Or (const BitArray & ba2);
+private:
+  ///
+  inline unsigned char Mask (INDEX i) const;
+  ///
+  inline INDEX Addr (INDEX i) const;
+
+  ///
+  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 << 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 0000000000..66b22cb4a7
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/dynamicmem.cpp
@@ -0,0 +1,117 @@
+#include <iostream>
+#include <iomanip>
+
+#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];
+  ptr = (char*)malloc (s);
+}
+
+void BaseDynamicMem :: ReAlloc (size_t s)
+{
+  if (size == s) return;
+
+  char * old = ptr;
+  // ptr = new char[s];
+  ptr = (char*)malloc(s);
+  memmove (ptr, old, (s < size) ? s : size);
+  // delete old;
+  free (old);
+  size = s;
+}
+
+void BaseDynamicMem :: Free ()
+{
+  // delete ptr;
+  free (ptr);
+  ptr = 0;
+}
+
+void BaseDynamicMem :: Swap (BaseDynamicMem & m2)
+{
+  int 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;
+  int mem = 0;
+  int cnt = 0;
+  while (p)
+    {
+      mem += p->size;
+      cnt++;
+
+      cout << setw(10) << p->size << " Bytes";
+      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;
+}
+
+}
diff --git a/contrib/Netgen/libsrc/general/dynamicmem.hpp b/contrib/Netgen/libsrc/general/dynamicmem.hpp
new file mode 100644
index 0000000000..501020988e
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/dynamicmem.hpp
@@ -0,0 +1,94 @@
+#ifndef FILE_DYNAMICMEM
+#define FILE_DYNAMICMEM
+
+/**************************************************************************/
+/* File:   dynamicmem.hpp                                                 */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   12. Feb. 2003                                                  */
+/**************************************************************************/
+
+
+
+
+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 ();
+};
+
+
+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 0000000000..9cce0cef2f
--- /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;
+  }
+  
+  int 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*> hstra(0);
+	return hstra;
+      }
+  }
+
+  const ARRAY<double> & 
+  Flags ::GetNumListFlag (const char * name) const
+  {
+    if (numlistflags.Used (name))
+      return *numlistflags.Get(name);
+    else
+      {
+	static ARRAY<double> hnuma(0);
+	return hnuma;
+      }
+  }
+
+
+  int Flags :: StringFlagDefined (const char * name) const
+  {
+    return strflags.Used (name);
+  }
+
+  int Flags :: NumFlagDefined (const char * name) const
+  {
+    return numflags.Used (name);
+  }
+
+  int Flags :: StringListFlagDefined (const char * name) const
+  {
+    return strlistflags.Used (name);
+  }
+
+  int 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 0000000000..7c62f7815b
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/flags.hpp
@@ -0,0 +1,83 @@
+#ifndef FILE_FLAGS
+#define FILE_FLAGS
+
+
+/**************************************************************************/
+/* File:   flags.hh                                                       */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   10. Oct. 96                                                   */
+/**************************************************************************/
+
+/** 
+   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:
+  ///
+  Flags ();
+  ///
+  ~Flags ();
+  
+  /// Deletes all flags
+  void DeleteFlags ();
+  /// Sets string flag, overwrite if exists
+  void SetFlag (const char * name, const char * val);
+  /// Sets numerical flag, overwrite if exists
+  void SetFlag (const char * name, double val);
+  /// Sets boolean flag
+  void SetFlag (const char * name);
+  /// Sets string arary falg
+  void SetFlag (const char * name, const ARRAY<char*> & val);
+  /// Sets double array flag
+  void SetFlag (const char * name, const ARRAY<double> & val);
+  
+  /// Save flags to file
+  void SaveFlags (const char * filename) const;
+  /// write flags to stream
+  void PrintFlags (ostream & ost) const;
+  /// Load flags from file
+  void LoadFlags (const char * filename);
+  /// set flag of form -name=hello -val=0.5 -defined
+  void SetCommandLineFlag (const char * st);
+
+  /// Returns string flag, default value if not exists
+  const char * GetStringFlag (const char * name, const char * def) const;
+  /// Returns numerical flag, default value if not exists
+  double GetNumFlag (const char * name, double def) const;
+  /// Returns address of numerical flag, null if not exists
+  const double * GetNumFlagPtr (const char * name) const;
+  /// Returns address of numerical flag, null if not exists
+  double * GetNumFlagPtr (const char * name);
+  /// Returns boolean flag
+  int GetDefineFlag (const char * name) const;
+  /// Returns string list flag, empty array if not exist
+  const ARRAY<char*> & GetStringListFlag (const char * name) const;
+  /// Returns num list flag, empty array if not exist
+  const ARRAY<double> & GetNumListFlag (const char * name) const;
+
+
+  /// Test, if string flag is defined
+  int StringFlagDefined (const char * name) const;
+  /// Test, if num flag is defined
+  int NumFlagDefined (const char * name) const;
+  /// Test, if string list flag is defined
+  int StringListFlagDefined (const char * name) const;
+  /// Test, if num list flag is defined
+  int 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 0000000000..0485ab6045
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/hashtabl.cpp
@@ -0,0 +1,294 @@
+/**************************************************************************/
+/* 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
+  {
+    int i;
+    for (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_2_CLOSED_HASHTABLE ::
+  BASE_INDEX_2_CLOSED_HASHTABLE (int size)
+    : hash(size)
+  {
+    hash.SetName ("i2-hashtable, hash");
+
+    invalid = -1;
+    int i;
+    for (i = 1; i <= size; i++)
+      hash.Elem(i).I1() = invalid;
+  }
+
+  void BASE_INDEX_2_CLOSED_HASHTABLE ::
+  BaseSetSize (int size)
+  {
+    int i;
+    hash.SetSize(size);
+    for (i = 1; i <= size; i++)
+      hash.Elem(i).I1() = invalid;
+  }
+
+
+  int BASE_INDEX_2_CLOSED_HASHTABLE ::
+  Position2 (const INDEX_2 & ind) const
+  {
+    int i;
+
+    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;
+
+    i = HashValue(ind);
+    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;
+	  }
+      }
+  }
+
+  int BASE_INDEX_2_CLOSED_HASHTABLE :: UsedElements () const
+  {
+    int i, n = hash.Size();
+    int cnt = 0;
+    for (i = 1; i <= n; i++)
+      if (hash.Get(i).I1() != invalid)
+	cnt++;
+    return cnt;
+  }
+
+
+
+
+
+
+
+
+
+
+
+  BASE_INDEX_3_CLOSED_HASHTABLE ::
+  BASE_INDEX_3_CLOSED_HASHTABLE (int size)
+    : hash(size)
+  {
+    hash.SetName ("i3-hashtable, hash");
+
+    invalid = -1;
+    int i;
+    for (i = 1; i <= size; i++)
+      hash.Elem(i).I1() = invalid;
+  }
+
+  void BASE_INDEX_3_CLOSED_HASHTABLE ::
+  BaseSetSize (int size)
+  {
+    int i;
+    hash.SetSize(size);
+    for (i = 1; i <= size; i++)
+      hash.Elem(i).I1() = invalid;
+  }
+
+
+  int BASE_INDEX_3_CLOSED_HASHTABLE ::
+  Position2 (const INDEX_3 & ind) const
+  {
+    int i;
+
+    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_3_CLOSED_HASHTABLE ::
+  PositionCreate2 (const INDEX_3 & ind, int & apos) 
+  {
+    int i;
+
+    i = HashValue(ind);
+    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;
+	  }
+      }
+  }
+
+  int BASE_INDEX_3_CLOSED_HASHTABLE :: UsedElements () const
+  {
+    int i, n = hash.Size();
+    int cnt = 0;
+    for (i = 1; i <= n; i++)
+      if (hash.Get(i).I1() != invalid)
+	cnt++;
+    return cnt;
+  }
+
+
+}
+
diff --git a/contrib/Netgen/libsrc/general/hashtabl.hpp b/contrib/Netgen/libsrc/general/hashtabl.hpp
new file mode 100644
index 0000000000..b3e0e93f7e
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/hashtabl.hpp
@@ -0,0 +1,1000 @@
+#ifndef FILE_HASHTABL
+#define FILE_HASHTABL
+
+/**************************************************************************/
+/* File:   hashtabl.hh                                                    */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Jun. 95                                                    */
+/**************************************************************************/
+
+/**
+   Abstract data type HASHTABLE.
+   Hash is done by one INDEX
+*/
+class BASE_INDEX_HASHTABLE
+{
+protected:
+  /// keys are stored in this table
+  TABLE<INDEX> 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> 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);
+  }
+
+
+
+  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 ();
+};
+
+
+
+
+
+
+
+
+/// 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;
+      }
+    /*
+    int pos = HashValue (ind);
+    if (hash.Get(pos) == ind) return pos;
+    return Position2 (ind);
+    */
+  }
+
+  // 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);
+  }
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+class BASE_INDEX_3_CLOSED_HASHTABLE
+{
+protected:
+  ///
+  MoveableArray<INDEX_3> hash;
+  ///
+  int invalid;
+public:
+  ///
+  BASE_INDEX_3_CLOSED_HASHTABLE (int size);
+
+  int Size() const { return hash.Size(); }
+  bool UsedPos (int pos) const { return ! (hash.Get(pos).I1() == invalid); }
+  int UsedElements () const;
+
+  ///
+  int HashValue (const INDEX_3 & ind) const
+  {
+    return (ind.I1() + 15 * ind.I2() + 41 * ind.I3()) % hash.Size() + 1;
+  }
+  
+  int Position (const INDEX_3 & 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;
+      }
+    /*
+    int pos = HashValue (ind);
+    if (hash.Get(pos) == ind) return pos;
+    return Position2 (ind);
+    */
+  }
+
+  // returns 1, if new postion is created
+  int PositionCreate (const INDEX_3 & 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_3 & ind) const;
+  int 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> cont;
+
+public:
+  ///
+  inline INDEX_3_CLOSED_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 void SetData (int pos, const INDEX_3 & ahash, const T & acont);
+  ///
+  inline void GetData (int pos, INDEX_3 & ahash, T & acont) const;
+  ///
+  inline void SetData (int pos, const T & acont);
+  ///
+  inline void GetData (int pos, T & acont) const;
+  ///
+  inline const T & GetData (int pos) const;
+  ///
+  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<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.Get(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/moveablemem.cpp b/contrib/Netgen/libsrc/general/moveablemem.cpp
new file mode 100644
index 0000000000..cdfac0e7b8
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/moveablemem.cpp
@@ -0,0 +1,249 @@
+#include <iostream>
+#include <iomanip>
+
+#include <myadt.hpp>
+using namespace std;
+namespace netgen
+{
+  
+  NgMutex mem_mutex;
+  
+  size_t BaseMoveableMem::totalsize = 0; // 500000000;
+  size_t BaseMoveableMem::used = 0;
+  char * BaseMoveableMem::largeblock = 0;
+  
+  BaseMoveableMem * BaseMoveableMem::first = 0;
+  BaseMoveableMem * BaseMoveableMem::last = 0;
+  
+
+  BaseMoveableMem :: BaseMoveableMem (size_t s)
+  {
+    // cout << "Construct object begin" << endl;
+    //   Print ();
+
+  prev = last;
+  next = 0;
+
+  if (last) last->next = this;
+  last = this;
+  if (!first) first = this;
+
+  size = 0;
+
+  if (prev)
+    pos = prev->pos + prev->size;
+  else
+    pos = 0;
+
+  ptr = 0;
+  name = NULL;
+
+  if (s) Alloc(s);
+}
+ 
+BaseMoveableMem :: ~BaseMoveableMem () throw()
+{
+  Free();
+
+  if (next) next->prev = prev;
+  else last = prev;
+  if (prev) prev->next = next;
+  else first = next;
+
+  if(name != NULL)
+    {
+      delete [] name;
+      name = NULL;
+    }
+}
+
+void BaseMoveableMem :: SetName (const char * aname)
+{
+  if(name != NULL)
+    {
+      delete [] name;
+      name = NULL;
+    }
+  if (aname)
+    {
+      name = new char[strlen(aname)+1];
+      strcpy (name, aname);
+    }
+}
+
+
+void BaseMoveableMem :: Alloc (size_t s)
+{
+  if (totalsize == 0)
+    {
+      size = s;
+      ptr = (char*) malloc(s);
+      return;
+    }
+
+
+  used += s - size;
+
+  int r = s % 8;
+  if (r) s += 8-r;
+  if (prev)
+    pos = prev->pos + prev->size;
+  else
+    pos = 0;
+  size = s;
+  
+  if (next)
+    {
+      NgLock lock(mem_mutex);
+      lock.Lock();
+      try
+	{
+	  next->MoveTo (pos+size);
+	}
+      catch (NgException e)
+      {
+	lock.UnLock();
+	throw NgException ("MoveableMem overflow");
+      }
+      lock.UnLock();
+    }
+
+  if (size)
+    {
+      if (!largeblock)
+	{
+	  cout << "moveable memory: allocate large block of "
+	       << totalsize / 1048576  << " MB" << endl; 
+	  // largeblock = new char[totalsize];
+	  largeblock = (char*)malloc (totalsize);
+	}
+      ptr = largeblock+pos;
+
+      if (pos + size > totalsize)
+	throw NgException ("MoveableMem overflow");
+    }
+  else
+    ptr = 0;
+}
+
+void BaseMoveableMem :: ReAlloc (size_t s)
+{
+  if (totalsize == 0)
+    {
+      if (size == s) return;
+      
+      char * old = ptr;
+      ptr = (char*)malloc(s);
+      memmove (ptr, old, (s < size) ? s : size);
+      free (old);
+      size = s;
+      return;
+    }
+
+  Alloc (s);
+}
+
+void BaseMoveableMem :: MoveTo (size_t newpos)
+{
+  //  cout << "move block, oldpos = " << pos << "; newpos = " << newpos
+  // << ", size = " << size << endl;
+  static int move = 0;
+
+  if (newpos + size > totalsize)
+    throw NgException ("MoveableMem overflow");
+  if (newpos > pos)
+    {
+      if (next) next->MoveTo (newpos+size);
+      memmove (largeblock+newpos, largeblock+pos, size);
+      move += size;
+    }
+  else if (newpos < pos)
+    {
+      // cout << "move down: " << size << endl;
+      memmove (largeblock+newpos, largeblock+pos, size);
+      if (next) next->MoveTo (newpos+size);
+      move += size;
+    }
+  pos = newpos;
+  ptr = largeblock+pos;
+  //  cout << "total move: " << move << endl;
+}
+
+void BaseMoveableMem :: Free () throw()
+{
+  if (totalsize == 0)
+    {
+      free (ptr);
+      ptr = 0;
+      return;
+    }
+
+  /*
+    cout << "free block, pos = " << pos << "size = " << size << endl;
+    cout << "before: " << endl;
+    Print();
+  */
+  used -= size;
+  if (next) 
+    {
+      NgLock lock(mem_mutex);
+      lock.Lock();
+      next->MoveTo (pos);
+      lock.UnLock();
+    }
+  
+  size = 0;
+  ptr = 0;
+  // pos = 0;
+}
+
+void BaseMoveableMem :: Swap (BaseMoveableMem & m2) throw()
+{
+  int hi;
+  // BaseMoveableMem * hp;
+  char * cp;
+  hi = size; size  = m2.size; m2.size = hi;
+  hi = pos; pos = m2.pos; m2.pos = hi;
+  /*
+  hp = prev; prev = m2.prev; m2.prev = hp;
+  hp = next; next = m2.next; m2.next = hp;
+  */
+  cp = ptr; ptr = m2.ptr; m2.ptr = cp;
+  cp = name; name = m2.name; m2.name = cp;
+}
+
+
+void BaseMoveableMem :: Print ()
+{
+  cout << "****************** Moveable Mem Report ****************" << endl;
+  BaseMoveableMem * p = first;
+  int mem = 0;
+  int cnt = 0;
+  while (p)
+    {
+      mem += p->size;
+      cnt++;
+
+      cout << setw(10) << p->size << " Bytes";
+      cout << ", pos = " << p->pos;
+      //      cout << ", addr = " << p->ptr;
+      if (p->name)
+	cout << " in block " << p->name;
+      cout << endl;
+
+      p = p->next;
+    }
+
+  if (mem > 100000000)
+    cout << "memory in moveable arena: " << mem/1048576 << " MB" << endl;
+  else if (mem > 100000)
+    cout << "memory in moveable arena: " << mem/1024 << " kB" << endl;
+  else
+    cout << "memory in moveable arena: " << mem << " Bytes" << endl;
+  cout << "number of blocks:         " << cnt << endl;
+  
+  cout << " used  = " << used << endl;
+  //  cout << "******************************************************" << endl;
+}
+
+}
diff --git a/contrib/Netgen/libsrc/general/moveablemem.hpp b/contrib/Netgen/libsrc/general/moveablemem.hpp
new file mode 100644
index 0000000000..92a67ad6da
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/moveablemem.hpp
@@ -0,0 +1,97 @@
+#ifndef FILE_MOVEABLEMEM
+#define FILE_MOVEABLEMEM
+
+/**************************************************************************/
+/* File:   moveablemem.hpp                                                */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   12. Feb. 2003                                                  */
+/**************************************************************************/
+
+
+extern NgMutex mem_mutex;
+
+class BaseMoveableMem
+{
+public:
+  static size_t totalsize;
+  static size_t used;
+
+private:
+  static char * largeblock;
+  static BaseMoveableMem *first, *last;
+
+  BaseMoveableMem *prev, *next;
+  size_t size, pos;
+  char * ptr;
+  char * name;
+
+protected:
+  BaseMoveableMem (size_t s = 0);
+  ~BaseMoveableMem () throw();
+  void Alloc (size_t s);
+  void ReAlloc (size_t s);
+  void MoveTo (size_t newpos);
+  void Free () throw(); 
+  char * Ptr() { return ptr; }
+  const char * Ptr() const { return ptr; }
+  void Swap (BaseMoveableMem & m2) throw(); 
+public:
+  void SetName (const char * aname);
+  static void Print ();
+};
+
+
+
+
+template <typename T>
+class MoveableMem : public BaseMoveableMem
+{
+public:
+  MoveableMem (size_t s = 0)
+    : BaseMoveableMem (sizeof(T) * s) 
+  {
+    ;
+  }
+  void Alloc (size_t s)
+  {
+    BaseMoveableMem::Alloc (sizeof(T) * s);
+  }
+  void ReAlloc (size_t s)
+  {
+    BaseMoveableMem::ReAlloc (sizeof(T) * s);
+  }
+  void Free ()
+  {
+    BaseMoveableMem::Free ();
+  }
+
+  const T * Ptr() const
+  {
+    return reinterpret_cast<const T*> (BaseMoveableMem::Ptr());
+  }
+
+  T * Ptr()
+  {
+    return reinterpret_cast<T*> (BaseMoveableMem::Ptr());
+  }
+
+  operator const T* () const
+  {
+    return reinterpret_cast<const T*> (BaseMoveableMem::Ptr());
+  }
+
+  operator T* () 
+  {
+    return reinterpret_cast<T*> (BaseMoveableMem::Ptr());
+  }
+
+  void Swap (MoveableMem<T> & m2)
+  {
+    BaseMoveableMem::Swap (m2);
+  }
+protected:
+  MoveableMem (const MoveableMem & m) { ; }
+  MoveableMem & operator= (const MoveableMem & m) { ; }
+};
+
+#endif
diff --git a/contrib/Netgen/libsrc/general/myadt.hpp b/contrib/Netgen/libsrc/general/myadt.hpp
new file mode 100644
index 0000000000..f74976d51f
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/myadt.hpp
@@ -0,0 +1,43 @@
+#ifndef FILE_MYADT
+#define FILE_MYADT
+
+/**************************************************************************/
+/* File:   myadt.hpp                                                      */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Jun. 95                                                    */
+/**************************************************************************/
+
+/* 
+   include for all abstract data types
+*/
+
+
+
+#include <mystdlib.h>
+#include <mydefs.hpp>
+
+
+namespace netgen
+{
+#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"
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/general/mystring.cpp b/contrib/Netgen/libsrc/general/mystring.cpp
new file mode 100644
index 0000000000..6b0f0cee74
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/mystring.cpp
@@ -0,0 +1,386 @@
+
+//**************************************************************
+//
+// 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>
+
+/*
+#include <iostream.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <fstream.h>
+#include "mystring.hh"
+
+ */
+
+namespace netgen
+{
+
+void DefaultStringErrHandler()
+{
+  cerr << "Fehler : Bereichs�berschreitung bei Stringoperation\n" << flush;
+}
+
+void (*MyStr::ErrHandler)() = DefaultStringErrHandler;
+
+  /*     
+MyStr::MyStr()
+{
+  length = 0;
+  str = shortstr;
+  str[0] = 0;
+}
+  */
+
+MyStr::MyStr(const char *s)
+{
+  length = 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 = 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 = 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 = 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 = 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 = 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 std::string & st)
+{
+  length = 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;
+  }
+  int 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;
+  }
+}
+
+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 0000000000..cd6639552c
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/mystring.hpp
@@ -0,0 +1,209 @@
+
+//**************************************************************
+//
+// 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
+
+class Point3d;
+class Vec3d;
+
+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:
+  MyStr();
+  MyStr(const char *);
+  MyStr(char);
+  MyStr(const MyStr &);
+  MyStr(int);
+  MyStr(long);
+  MyStr(double);
+  MyStr(const Point3d& p);
+  MyStr(const Vec3d& p);
+  MyStr(const std::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();
+
+  //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/ngexception.cpp b/contrib/Netgen/libsrc/general/ngexception.cpp
new file mode 100644
index 0000000000..6746dd2521
--- /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 0000000000..56c561aa8c
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/ngexception.hpp
@@ -0,0 +1,30 @@
+#ifndef FILE_NGEXCEPTION
+#define FILE_NGEXCEPTION
+
+/**************************************************************************/
+/* File:   ngexception.hpp                                                */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   16. Jan. 2002                                                  */
+/**************************************************************************/
+
+
+/// Base class for all ng exceptions
+class NgException 
+{
+  /// verbal description of exception
+  string what;
+public:
+  ///
+  NgException (const string & s);
+  ///
+  virtual ~NgException ();
+
+  /// append string to description
+  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 0000000000..c8f961b12a
--- /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 (unsigned i = 0; i < bablocks.Size(); i++)
+      delete [] bablocks[i];
+  }
+
+  void BlockAllocator :: Alloc2 ()
+  {
+    //  return new char[size];
+    //    if (!freelist)
+      {
+	// 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 0000000000..d9e5e2137e
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/optmem.hpp
@@ -0,0 +1,52 @@
+#ifndef FILE_OPTMEM
+#define FILE_OPTMEM
+
+/**************************************************************************/
+/* File:   optmem.hh                                                      */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   04. Apr. 97                                                    */
+/**************************************************************************/
+
+/** 
+    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;
+    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 0000000000..81e9d0b6cc
--- /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 0000000000..99dd017633
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/parthreads.hpp
@@ -0,0 +1,126 @@
+#ifndef FILE_PARTHREADS
+#define FILE_PARTHREADS
+
+/**************************************************************************/
+/* File:   parthreads.hh                                                  */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   22. Nov. 2000                                                  */
+/**************************************************************************/
+
+/*
+  Parallel thread, Mutex, 
+*/
+
+#ifdef NO_PARALLEL_THREADS
+
+class NgMutex { };
+
+class NgLock
+{
+public:	
+  NgLock (NgMutex & mut, bool lock = 0) { ; }
+  void Lock () { ; }
+  void UnLock () { ; }
+};
+
+
+#else
+
+#ifdef WIN32
+
+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;
+  }
+};
+
+#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 = 0)
+    : 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 = 1;
+  }
+  void UnLock ()
+  { 
+    pthread_mutex_unlock (&mut); 
+    locked = 0;
+  }
+  /*
+  int TryLock ()
+  { 
+    return pthread_mutex_trylock (&mut); 
+  }
+  */
+};
+
+#endif
+
+#endif
+
+#endif
diff --git a/contrib/Netgen/libsrc/general/seti.cpp b/contrib/Netgen/libsrc/general/seti.cpp
new file mode 100644
index 0000000000..702337f076
--- /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 0000000000..4ca0b8eb4e
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/seti.hpp
@@ -0,0 +1,45 @@
+#ifndef FILE_SETI
+#define FILE_SETI
+
+
+/**************************************************************************/
+/* File:   seti.hh                                                        */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   20. Mar. 98                                                    */
+/**************************************************************************/
+
+/**
+  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> & Array() { return set; }
+};
+
+#endif
+
diff --git a/contrib/Netgen/libsrc/general/sort.cpp b/contrib/Netgen/libsrc/general/sort.cpp
new file mode 100644
index 0000000000..264a132a74
--- /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 QickSortRec (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) QickSortRec (values, order, left, j);
+  if (i < right) QickSortRec (values, order, i, right);
+}
+
+void QickSort (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;
+
+  QickSortRec (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 0000000000..99c3291000
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/sort.hpp
@@ -0,0 +1,19 @@
+#ifndef FILE_SORT
+#define FILE_SORT
+
+/**************************************************************************/
+/* File:   sort.hh                                                        */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   07. Jan. 00                                                    */
+/**************************************************************************/
+
+
+// order(i) is sorted index of element i
+extern void Sort (const ARRAY<double> & values,
+		  ARRAY<int> & order);
+
+extern void QickSort (const ARRAY<double> & values,
+		      ARRAY<int> & order);
+
+		  
+#endif
diff --git a/contrib/Netgen/libsrc/general/spbita2d.cpp b/contrib/Netgen/libsrc/general/spbita2d.cpp
new file mode 100644
index 0000000000..e1e80e2cad
--- /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 0000000000..db656653b1
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/spbita2d.hpp
@@ -0,0 +1,56 @@
+#ifndef FILE_SPBITA2D
+#define FILE_SPBITA2D
+
+/**************************************************************************/
+/* File:   spbita2d.hh                                                    */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Jun. 95                                                    */
+/**************************************************************************/
+
+/** 
+   Implementation of sparse 2 dimensional bitarray
+*/
+
+
+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 0000000000..db8dfad266
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/stack.hpp
@@ -0,0 +1,112 @@
+#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"
+
+
+///
+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/stack.icc b/contrib/Netgen/libsrc/general/stack.icc
new file mode 100644
index 0000000000..c0182a564b
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/stack.icc
@@ -0,0 +1,67 @@
+/*****************************************************************************/
+/*  File: stack.hh                                                           */
+/*  Author: Wolfram Muehlhuber                                               */
+/*  Date: September 98                                                       */
+/*****************************************************************************/
+
+/*
+  
+  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;
+}
+
+
diff --git a/contrib/Netgen/libsrc/general/symbolta.cpp b/contrib/Netgen/libsrc/general/symbolta.cpp
new file mode 100644
index 0000000000..b02daa65dc
--- /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 0000000000..6b8916e8ea
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/symbolta.hpp
@@ -0,0 +1,158 @@
+#ifndef FILE_SYMBOLTA
+#define FILE_SYMBOLTA
+
+
+/**************************************************************************/
+/* File:   symbolta.hh                                                    */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Jun. 95                                                    */
+/**************************************************************************/
+
+/**
+   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 int 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 int SYMBOLTABLE<T> :: Used (const char * name) const
+{
+  return (Index(name)) ? 1 : 0;
+}
+
+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 0000000000..ed84789bf9
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/table.cpp
@@ -0,0 +1,167 @@
+/**************************************************************************/
+/* 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 :: 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 :: 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;
+  }
+
+}
diff --git a/contrib/Netgen/libsrc/general/table.hpp b/contrib/Netgen/libsrc/general/table.hpp
new file mode 100644
index 0000000000..b986049b00
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/table.hpp
@@ -0,0 +1,212 @@
+#ifndef FILE_TABLE
+#define FILE_TABLE
+
+/**************************************************************************/
+/* File:   table.hpp                                                      */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Jun. 95                                                    */
+/**************************************************************************/
+
+/// 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);
+
+  /// 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 IncSize2 (int i, int elsize);
+
+  //  void DecSize (int i);
+
+  ///
+  void AllocateElementsOneBlock (int elemsize);
+
+  int AllocatedElements () const;
+  int UsedElements () const;
+};
+
+
+
+
+
+
+
+/** 
+   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 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);
+  }
+
+
+  /// 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 mem
+  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, TABLE<T,BASE> & table)
+{
+  for (int i = BASE; i < table.Size()+BASE; i++)
+    {
+      ost << i << ": ";
+      FlatArray<T> row = table[i];
+      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 0000000000..4bf983c06b
--- /dev/null
+++ b/contrib/Netgen/libsrc/general/template.hpp
@@ -0,0 +1,448 @@
+#ifndef FILE_TEMPLATE
+#define FILE_TEMPLATE
+
+/**************************************************************************/
+/* File:   template.hh                                                    */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Jun. 95                                                    */
+/**************************************************************************/
+
+/*
+   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 */
+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 */
+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)
+  {
+    INDEX_3 i(i1, i2, i3);
+    return i.Sort();
+  }
+
+  ///
+  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);
+};
+
+
+
+
+
+
+
+
+
+
+
+
+///
+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 b/contrib/Netgen/libsrc/geom2d/Makefile
new file mode 100644
index 0000000000..8371fa19e7
--- /dev/null
+++ b/contrib/Netgen/libsrc/geom2d/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for geometric library
+#
+src =  spline2d.cpp geom2dmesh.cpp splinegeometry2.cpp genmesh2d.cpp
+#
+lib = geom2d
+libpath = libsrc/geom2d
+#
+#
+include ../makefile.inc
+#
+
diff --git a/contrib/Netgen/libsrc/geom2d/genmesh2d.cpp b/contrib/Netgen/libsrc/geom2d/genmesh2d.cpp
new file mode 100644
index 0000000000..34f93b66ee
--- /dev/null
+++ b/contrib/Netgen/libsrc/geom2d/genmesh2d.cpp
@@ -0,0 +1,145 @@
+#include <mystdlib.h>
+#include <csg.hpp>
+#include <geometry2d.hpp>
+#include "meshing.hpp"
+
+namespace netgen
+{
+
+  // static ARRAY<Point<2> > points2;
+  //  static ARRAY<int> lp1, lp2;
+
+
+  extern void Optimize2d (Mesh & mesh, MeshingParameters & mp);
+
+
+
+
+  void MeshFromSpline2D (SplineGeometry2d & geometry,
+			 Mesh *& mesh, 
+			 MeshingParameters & mp)
+  {
+    int i, j, domnr;
+    double elto0, minx, miny, maxx, maxy;
+
+    //    mp.Print(*testout);
+
+    PointIndex pi;
+    SegmentIndex si;
+    SurfaceElementIndex sei;
+
+    double h = mp.maxh;
+
+    Box<2> bbox;
+    geometry.GetBoundingBox (bbox);
+
+    if (bbox.Diam() < h) 
+      {
+	h = bbox.Diam();
+	mp.maxh = h;
+      }
+
+    mesh = new Mesh;
+    mesh->SetDimension (2);
+    PrintMessage (1, "Generate Mesh from spline geometry");
+
+    geometry.PartitionBoundary (h, *mesh);
+
+    for (i = 0; i < geometry.GetNP(); i++)
+      if (geometry.GetPoint(i).hpref)
+	{
+	  double mindist = 1e99;
+	  PointIndex mpi;
+	  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].SetSingular();
+	}
+
+
+    int maxdomnr = 0;
+    for (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 (i = 1; i <= maxdomnr; i++)
+      mesh->AddFaceDescriptor (FaceDescriptor (i, 0, 0, i));
+
+    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();
+
+    int bnp = mesh->GetNP(); // boundary points
+
+    for (domnr = 1; domnr <= maxdomnr; domnr++)
+      {
+	PrintMessage (3, "Meshing domain ", domnr, " / ", maxdomnr);
+
+	int oldnf = mesh->GetNSE();
+
+	Meshing2 meshing (Box3d (pmin, pmax));
+
+	for (pi = PointIndex::BASE; 
+	     pi < bnp+PointIndex::BASE; pi++)
+	  meshing.AddPoint ( (*mesh)[pi], pi);
+      
+
+	PointGeomInfo gi;
+	gi.trignum = 1;
+	for (si = 0; si < mesh->GetNSeg(); si++)
+	  {
+	    if ( (*mesh)[si].domin == domnr)
+	      meshing.AddBoundaryElement ( (*mesh)[si].p1 + 1 - PointIndex::BASE, 
+					   (*mesh)[si].p2 + 1 - PointIndex::BASE, gi, gi);
+	    if ( (*mesh)[si].domout == domnr)
+	      meshing.AddBoundaryElement ( (*mesh)[si].p2 + 1 - PointIndex::BASE, 
+					   (*mesh)[si].p1 + 1 - PointIndex::BASE, gi, gi);
+	  }
+
+
+	mparam.checkoverlap = 0;
+	meshing.GenerateMesh (*mesh, h, domnr);
+
+	for (sei = oldnf; sei < mesh->GetNSE(); sei++)
+	  (*mesh)[sei].SetIndex (domnr);
+      }
+
+
+    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();
+
+
+#ifdef OPENGL
+    extern void Render();
+    Render();
+#endif
+
+  }
+
+
+}
diff --git a/contrib/Netgen/libsrc/geom2d/geom2dmesh.cpp b/contrib/Netgen/libsrc/geom2d/geom2dmesh.cpp
new file mode 100644
index 0000000000..1b679bd3c0
--- /dev/null
+++ b/contrib/Netgen/libsrc/geom2d/geom2dmesh.cpp
@@ -0,0 +1,55 @@
+#include <mystdlib.h>
+
+#include <csg.hpp>
+#include <geometry2d.hpp>
+#include <meshing.hpp>
+
+namespace netgen
+{
+
+  Refinement2d :: Refinement2d (const SplineGeometry2d & ageometry)
+    : Refinement(), geometry(ageometry)
+  {
+    ;
+  }
+
+  Refinement2d :: ~Refinement2d ()
+  {
+    ;
+  }
+  
+
+  void Refinement2d :: 
+  PointBetween (const Point3d & p1, const Point3d & p2, double secpoint,
+		int surfi, 
+		const PointGeomInfo & gi1, 
+		const PointGeomInfo & gi2,
+		Point3d & newp, PointGeomInfo & newgi)
+  {
+    newp = p1+secpoint*(p2-p1);
+    newgi.trignum = 1;
+  }
+
+
+
+  void Refinement2d :: 
+  PointBetween (const Point3d & p1, const Point3d & p2, double secpoint, 
+		int surfi1, int surfi2, 
+		const EdgePointGeomInfo & ap1, 
+		const EdgePointGeomInfo & ap2,
+		Point3d & newp, EdgePointGeomInfo & newgi)
+  {
+    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);
+  }
+
+}
diff --git a/contrib/Netgen/libsrc/geom2d/geom2dmesh.hpp b/contrib/Netgen/libsrc/geom2d/geom2dmesh.hpp
new file mode 100644
index 0000000000..6912cd7752
--- /dev/null
+++ b/contrib/Netgen/libsrc/geom2d/geom2dmesh.hpp
@@ -0,0 +1,38 @@
+#ifndef FILE_GEOM2DMESH
+#define FILE_GEOM2DMESH
+
+/**************************************************************************/
+/* File:   geom2dmesh.hh                                                  */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   22. Jan. 01                                                    */
+/**************************************************************************/
+
+
+class Refinement2d : public Refinement
+{
+  const SplineGeometry2d & geometry;
+
+public:
+  Refinement2d (const SplineGeometry2d & ageometry);
+  virtual ~Refinement2d ();
+  
+  virtual void PointBetween (const Point3d & p1, const Point3d & p2, double secpoint,
+			     int surfi, 
+			     const PointGeomInfo & gi1, 
+			     const PointGeomInfo & gi2,
+			     Point3d & newp, PointGeomInfo & newgi);
+
+  virtual void PointBetween (const Point3d & p1, const Point3d & p2, double secpoint,
+			     int surfi1, int surfi2, 
+			     const EdgePointGeomInfo & ap1, 
+			     const EdgePointGeomInfo & ap2,
+			     Point3d & newp, EdgePointGeomInfo & newgi);
+			     
+};
+
+
+
+
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/geom2d/geometry2d.hpp b/contrib/Netgen/libsrc/geom2d/geometry2d.hpp
new file mode 100644
index 0000000000..80d276e340
--- /dev/null
+++ b/contrib/Netgen/libsrc/geom2d/geometry2d.hpp
@@ -0,0 +1,20 @@
+#ifndef FILE_GEOMETRY2D
+#define FILE_GEOMETRY2D
+
+/* *************************************************************************/
+/* File:   geometry2d.hpp                                                  */
+/* Author: Joachim Schoeberl                                               */
+/* Date:   20. Jul. 02                                                     */
+/* *************************************************************************/
+
+#include <myadt.hpp>
+#include <gprim.hpp>
+
+namespace netgen
+{
+#include "spline2d.hpp"
+#include "splinegeometry2.hpp"
+#include "geom2dmesh.hpp"
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/geom2d/spline2d.cpp b/contrib/Netgen/libsrc/geom2d/spline2d.cpp
new file mode 100644
index 0000000000..8609e064d1
--- /dev/null
+++ b/contrib/Netgen/libsrc/geom2d/spline2d.cpp
@@ -0,0 +1,395 @@
+/*
+
+2d Spline curve for Mesh generator
+
+*/
+
+#include <mystdlib.h>
+#include <csg.hpp>
+#include <linalg.hpp>
+#include <meshing.hpp>
+
+namespace netgen
+{
+#include "spline2d.hpp"
+
+
+  void CalcPartition (double l, double h, double r1, double r2,
+		      double ra, double elto0, ARRAY<double> & points);
+
+
+
+  // calculates length of spline-curve
+  double SplineSegment :: Length () const
+  {
+    Point<2> p, pold;
+
+    int i, n = 100;
+    double dt = 1.0 / n;
+
+    pold = GetPoint (0);
+
+    double l = 0;
+    for (i = 1; i <= n; i++)
+      {
+	p = GetPoint (i * dt);
+	l += Dist (p, pold);
+	pold = p;
+      }
+    return l;
+  }
+
+
+
+  // partitionizes spline curve
+  void SplineSegment :: Partition (double h, double elto0,
+				   Mesh & mesh, Point3dTree & searchtree, int segnr) const
+  {
+    int i, j;
+    double l, r1, r2, ra;
+    double lold, dt, frac;
+    int n = 100;
+    Point<2> p, pold, mark, oldmark;
+    ARRAY<double> curvepoints;
+    double edgelength, edgelengthold;
+    l = Length();
+
+    r1 = StartPI().refatpoint;
+    r2 = EndPI().refatpoint;
+    ra = reffak;
+
+    //  cout << "Partition, l = " << l << ", h = " << h << endl;
+    CalcPartition (l, h, r1, r2, ra, elto0, curvepoints);
+    //  cout << "curvepoints = " << curvepoints << endl;
+
+    dt = 1.0 / n;
+
+    l = 0;
+    j = 1;
+
+    pold = GetPoint (0);
+    lold = 0;
+    oldmark = pold;
+    edgelengthold = 0;
+    ARRAY<int> locsearch;
+
+    for (i = 1; i <= n; i++)
+      {
+	p = GetPoint (i*dt);
+	l = lold + Dist (p, pold);
+	while (j < curvepoints.Size() && (l >= curvepoints[j] || i == n))
+	  {
+	    frac = (curvepoints[j]-lold) / (l-lold);
+	    mark = pold + frac * (p-pold);
+	    edgelength = i*dt + (frac-1)*dt;
+	    {
+	      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);
+	      if (locsearch.Size()) pi1 = locsearch[0];
+	      
+	      searchtree.GetIntersecting (mark3 - v, mark3 + v, locsearch);
+	      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);
+		  searchtree.Insert (oldmark3, pi1);
+		}
+	      if (pi2 == -1)
+		{
+		  pi2 = mesh.AddPoint(mark3);
+		  searchtree.Insert (mark3, pi2);
+		}
+
+	      // cout << "pi1 = " << pi1 << endl;
+	      // cout << "pi2 = " << pi2 << endl;
+	  
+	      Segment seg;
+	      seg.edgenr = segnr;
+	      seg.si = bc; // segnr;
+	      seg.p1 = pi1;
+	      seg.p2 = pi2;
+	      seg.domin = leftdom;
+	      seg.domout = rightdom;
+	      seg.epgeominfo[0].edgenr = segnr;
+	      seg.epgeominfo[0].dist = edgelengthold;
+	      seg.epgeominfo[1].edgenr = segnr;
+	      seg.epgeominfo[1].dist = edgelength;
+	      seg.singedge_left = hpref_left;
+	      seg.singedge_right = hpref_right;
+	      mesh.AddSegment (seg);
+	    }
+	
+	    oldmark = mark;
+	    edgelengthold = edgelength;
+	    j++;
+	  }
+    
+	pold = p;
+	lold = l;
+      }
+  }
+
+
+  void SplineSegment :: GetPoints (int n, ARRAY<Point<2> > & points)
+  {
+    points.SetSize (n);
+    if (n >= 2)
+      for (int i = 0; i < n; i++)
+	points[i] = GetPoint(double(i) / (n-1));
+  }
+
+
+
+  /* 
+     Implementation of line-segment from p1 to p2
+  */
+
+
+  LineSegment :: LineSegment (const GeomPoint2d & ap1, 
+			      const GeomPoint2d & ap2)
+    : p1(ap1), p2(ap2)
+  {
+    ;
+  }
+
+
+  Point<2> LineSegment :: GetPoint (double t) const
+  {
+    return p1 + t * (p2 - p1);
+  }
+
+  double LineSegment :: Length () const
+  {
+    return Dist (p1, p2);
+  }
+
+
+  void LineSegment :: PrintCoeff (ostream & ost) const
+  {
+    double dx = p2(0) - p1(0);
+    double dy = p2(1) - p1(1);
+    ost << "0 0 0 " <<  dy << " " << -dx << " " 
+	<< dx * p1(1) - dy * p1(0) << endl;
+  }
+
+
+
+
+
+  SplineSegment3 :: SplineSegment3 (const GeomPoint2d & ap1, 
+				    const GeomPoint2d & ap2,
+				    const GeomPoint2d & ap3)
+    : p1(ap1), p2(ap2), p3(ap3)
+  {
+    ;
+  }
+
+  Point<2> SplineSegment3 :: GetPoint (double t) const
+  {
+    double x, y, w;
+    double b1, b2, b3;
+
+    b1 = (1-t)*(1-t);
+    b2 = sqrt(2.0) * 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;
+
+    return Point<2> (x/w, y/w);
+  }
+
+
+  void SplineSegment3 :: PrintCoeff (ostream & ost) const
+  {
+    double t;
+    int i;
+    Point<2> p;
+    DenseMatrix a(6, 6);
+    DenseMatrix ata(6, 6);
+    Vector u(6), f(6);
+
+    //  ata.SetSymmetric(1);
+
+    t = 0;
+    for (i = 1; i <= 5; i++, t += 0.25)
+      {
+	p = GetPoint (t);
+	a.Elem(i, 1) = p(0) * p(0);
+	a.Elem(i, 2) = p(1) * p(1);
+	a.Elem(i, 3) = p(0) * p(1);
+	a.Elem(i, 4) = p(0);
+	a.Elem(i, 5) = p(1);
+	a.Elem(i, 6) = 1;
+      }
+    a.Elem(6, 1) = 1;
+
+    CalcAtA (a, ata);
+
+    u = 0;
+    u.Elem(6) = 1;
+    a.MultTrans (u, f);
+    ata.Solve (f, u);
+  
+    for (i = 1; i <= 6; i++)
+      ost << u.Get(i) << "  ";
+    ost << endl;
+  }
+
+
+
+
+  //########################################################################
+  //		circlesegment
+
+  CircleSegment :: CircleSegment (const GeomPoint2d & ap1, 
+				  const GeomPoint2d & ap2,
+				  const GeomPoint2d & ap3)
+    : p1(ap1), p2(ap2), p3(ap3)
+  {
+    Vec<2> v1,v2;
+  
+    v1 = p1 - p2;
+    v2 = p3 - p2;
+  
+    Point<2> p1t(p1(0)+v1[1], p1(1)-v1[0]);
+    Point<2> p2t(p3(0)+v2[1], p3(1)-v2[0]);
+    Line2d g1t(p1, p1t), g2t(p3, p2t);
+  
+    pm 	  = CrossPoint (g1t,g2t);
+    radius  = Dist(pm,StartPI());
+    w1      = Angle(Vec2d (p1 - pm));
+    w3      = Angle(Vec2d (p3 - pm));
+    if ( fabs(w3-w1) > M_PI )
+      {  
+	if ( w3>M_PI )   w3 -= 2*M_PI;
+	if ( w1>M_PI )   w1 -= 2*M_PI;
+      }
+  }
+ 
+  Point<2>  CircleSegment :: GetPoint (double t) const
+  {
+    if (t >= 1.0)  { return p3; }
+     
+    double phi = StartAngle() + t*(EndAngle()-StartAngle());
+    Vec2d  tmp(cos(phi),sin(phi));
+     
+    return pm + Radius()*tmp;
+  }
+  
+  void CircleSegment :: PrintCoeff (ostream & ost) const
+  {
+    double a,b,c,d,e,f;
+ 
+    a = b = 1.0;
+    c = 0.0;
+    d = -2.0 * pm[0];
+    e = -2.0 * pm[1];
+    f = sqr(pm[0]) + sqr(pm[1]) - sqr(Radius());
+ 
+    ost << a << "  " << b << "  " << c << "  " << d << "  " << e << "  " << f ;
+    ost << endl;
+  }
+
+
+
+  DiscretePointsSegment ::   DiscretePointsSegment (const ARRAY<Point<2> > & apts)
+    : pts (apts),
+      p1 (apts[0](0), apts[0](1), 1),
+      p2 (apts.Last()(0), apts.Last()(1), 1)
+
+  { ; }
+
+
+  DiscretePointsSegment :: ~DiscretePointsSegment ()
+  { ; }
+
+  Point<2> DiscretePointsSegment :: 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 Point<2> ((1-rest)*pts[segnr](0) + rest*pts[segnr+1](0),
+		     (1-rest)*pts[segnr](1) + rest*pts[segnr+1](1));
+  }
+
+  
+
+
+
+
+  //########################################################################
+
+
+
+
+  void CalcPartition (double l, double h, double r1, double r2,
+		      double ra, double elto0, ARRAY<double> & points)
+  {
+    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 (h/ra, t/elto0 + h/r1, (l-t)/elto0 + h/r2);
+	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 (h/ra, t/elto0 + h/r1, (l-t)/elto0 + h/r2);
+
+	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);
+  }
+
+
+}
diff --git a/contrib/Netgen/libsrc/geom2d/spline2d.hpp b/contrib/Netgen/libsrc/geom2d/spline2d.hpp
new file mode 100644
index 0000000000..f107a137f1
--- /dev/null
+++ b/contrib/Netgen/libsrc/geom2d/spline2d.hpp
@@ -0,0 +1,204 @@
+#ifndef FILE_SPLINE2D
+#define FILE_SPLINE2D
+
+/**************************************************************************/
+/* File:   spline2d.hh                                                    */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   24. Jul. 96                                                    */
+/**************************************************************************/
+
+
+/*
+  Spline curves for 2D mesh generation
+  */
+
+
+/// 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 */
+  virtual void PrintCoeff (ostream & ost) const = 0;
+
+  virtual void GetPoints (int n, ARRAY<Point<2> > & points);
+
+  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 string GetType(void) const {return "line";}
+};
+
+
+/// 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 string GetType(void) const {return "spline3";}
+};
+
+
+// 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;
+  ///
+  double Radius() const { return radius; }
+  ///
+  double StartAngle() const { return w1; }
+  ///
+  double EndAngle() const { return w3; }
+
+  virtual string GetType(void) const {return "circle";}
+};
+
+
+// Gundolf Haase  8/26/97
+/// elliptic curve with one axe parallel to line {P1,P2}
+/*
+class ellipsegment3 : public SplineSegment
+{
+  ///
+  GeomPoint2d *p1, *p2, *p3;
+public:
+  ///
+  SplineSegment3 (GeomPoint2d * ap1, GeomPoint2d * ap2, GeomPoint2d * ap3);
+  ///
+  virtual Point<2> GetPoint (double t) const;
+  ///
+  virtual GeomPoint2d * StartPI () const { return p1; };
+  ///
+  virtual GeomPoint2d * EndPI () const { return p3; }
+  ///
+  virtual void PrintCoeff (ostream & ost) const;
+};
+*/
+
+
+
+
+
+
+/// 
+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 { ; }
+};
+
+
+
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/geom2d/splinegeometry2.cpp b/contrib/Netgen/libsrc/geom2d/splinegeometry2.cpp
new file mode 100644
index 0000000000..a034d53989
--- /dev/null
+++ b/contrib/Netgen/libsrc/geom2d/splinegeometry2.cpp
@@ -0,0 +1,346 @@
+/*
+
+2d Spline curve for Mesh generator
+
+*/
+
+#include <mystdlib.h>
+#include <csg.hpp>
+#include <linalg.hpp>
+#include <meshing.hpp>
+
+
+namespace netgen
+
+{
+
+#include "spline2d.hpp"
+#include "splinegeometry2.hpp"
+
+
+
+SplineGeometry2d :: ~SplineGeometry2d()
+{
+  for(int i=0; i<splines.Size(); i++)
+    {
+      delete splines[i];
+    }
+  splines.DeleteAll();
+  geompoints.DeleteAll();
+}
+
+void SplineGeometry2d :: Load (const char * filename)
+{
+  ifstream infile;
+  int nump, numseg, leftdom, rightdom;
+  double x, y;
+  int hi1, hi2, hi3;
+  double hd;
+  char buf[50], ch;
+
+  infile.open (filename);
+
+  if (! infile.good() )
+    throw NgException(string ("2D Input file '") + 
+		      string (filename) +
+		      string ("' not available!"));
+
+  infile >> buf;   // file recognition
+  infile >> elto0;
+
+  infile >> nump;
+  for (int i = 0; i < nump; i++)
+    {
+      infile >> x >> y >> 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 (GeomPoint2d(x, y, hd));
+      geompoints.Last().hpref = flags.GetDefineFlag ("hpref");
+    }
+
+  infile >> numseg;
+  SplineSegment * spline = 0;
+  for (int i = 0; i < numseg; i++)
+    {
+      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 LineSegment(geompoints[hi1-1],
+				   geompoints[hi2-1]);
+	}
+      else if (strcmp (buf, "3") == 0)
+	{ // a rational spline
+	  infile >> hi1 >> hi2 >> hi3;
+	  spline = new SplineSegment3 (geompoints[hi1-1],
+				       geompoints[hi2-1],
+				       geompoints[hi3-1]);
+	}
+      else if (strcmp (buf, "4") == 0)
+	{ // an arc
+	  infile >> hi1 >> hi2 >> hi3;
+	  spline = new CircleSegment (geompoints[hi1-1],
+				      geompoints[hi2-1],
+				      geompoints[hi3-1]);
+	  break;
+	}
+      else if (strcmp (buf, "discretepoints") == 0)
+	{
+	  int npts;
+	  infile >> npts;
+	  ARRAY<Point<2> > pts(npts);
+	  for (int j = 0; j < npts; j++)
+	    infile >> pts[j](0) >> pts[j](1);
+
+	  spline = new DiscretePointsSegment (pts);
+	  cout << "pts = " << pts << endl;
+	}
+    
+      infile >> spline->reffak;
+      spline -> leftdom = leftdom;
+      spline -> rightdom = rightdom;
+      splines.Append (spline);
+
+
+      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);
+    
+      splines.Last()->bc = int (flags.GetNumFlag ("bc", i+1));
+      splines.Last()->hpref_left = int (flags.GetDefineFlag ("hpref")) || 
+	int (flags.GetDefineFlag ("hprefleft"));
+      splines.Last()->hpref_right = int (flags.GetDefineFlag ("hpref")) || 
+	int (flags.GetDefineFlag ("hprefright"));
+      splines.Last()->copyfrom = int (flags.GetNumFlag ("copy", -1));
+    }
+
+
+  infile.close();
+}
+
+
+
+void SplineGeometry2d :: 
+PartitionBoundary (double h, Mesh & mesh2d)
+{
+  Box<2> bbox;
+  GetBoundingBox (bbox);
+  double dist = Dist (bbox.PMin(), bbox.PMax());
+  Point<3> pmin(bbox.PMin()(0), bbox.PMin()(1), -dist);
+  Point<3> pmax(bbox.PMax()(0), bbox.PMax()(1), dist);
+
+  cout << "searchtree from " << pmin << " to " << pmax << endl;
+  Point3dTree searchtree (pmin, pmax);
+  
+  for (int i = 0; i < splines.Size(); i++)
+    if (splines[i]->copyfrom == -1)
+      splines[i]->Partition(h, elto0, mesh2d, searchtree, i+1);
+    else
+      CopyEdgeMesh (splines[i]->copyfrom, i+1, mesh2d, searchtree);
+}
+
+
+void SplineGeometry2d :: CopyEdgeMesh (int from, int to, Mesh & mesh, Point3dTree & searchtree)
+{
+  int i, j, k;
+
+  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);
+
+  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.p1) = 1;
+	  param.Elem(seg.p1) = seg.epgeominfo[0].dist;
+
+	  mappoints.Elem(seg.p2) = 1;
+	  param.Elem(seg.p2) = seg.epgeominfo[1].dist;
+	}
+    }
+
+  for (i = 1; i <= mappoints.Size(); i++)
+    {
+      if (mappoints.Get(i) != -1)
+	{
+	  Point<2> newp = splines.Get(to)->GetPoint (param.Get(i));
+	  Point<3> newp3 (newp(0), newp(1), 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);
+	}
+    }
+
+  // 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 = splines.Get(to)->bc;
+	  nseg.p1 = mappoints.Get(seg.p1);
+	  nseg.p2 = mappoints.Get(seg.p2);
+	  nseg.domin = splines.Get(to)->leftdom;
+	  nseg.domout = splines.Get(to)->rightdom;
+	  
+	  nseg.epgeominfo[0].edgenr = to;
+	  nseg.epgeominfo[0].dist = param.Get(seg.p1);
+	  nseg.epgeominfo[1].edgenr = to;
+	  nseg.epgeominfo[1].dist = param.Get(seg.p2);
+	  mesh.AddSegment (nseg);
+	}
+    }
+}
+  
+
+void SplineGeometry2d :: 
+GetBoundingBox (Box<2> & box) const
+{
+  if (!splines.Size())
+    {
+      box.Set (Point<2> (0,0));
+      return;
+    }
+
+  ARRAY<Point<2> > 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]);
+    }
+}
+
+void SplineGeometry2d :: 
+SetGrading (const double grading)
+{ elto0 = grading;}
+
+void SplineGeometry2d :: 
+AppendPoint (const double x, const double y, const double reffac, const bool hpref)
+{
+  geompoints.Append (GeomPoint2d(x, y, reffac));
+  geompoints.Last().hpref = hpref;
+}
+
+
+void SplineGeometry2d :: 
+AppendSegment(SplineSegment * spline, const int leftdomain, const int rightdomain, 
+	      const int bc, 
+	      const double reffac, const bool hprefleft, const bool hprefright,
+	      const int copyfrom)
+{
+  spline -> leftdom = leftdomain;
+  spline -> rightdom = rightdomain;
+  spline -> bc = (bc >= 0) ? bc : (splines.Size()+1);
+  spline -> reffak = reffac;
+  spline -> hpref_left = hprefleft;
+  spline -> hpref_right = hprefright;
+  spline -> copyfrom = copyfrom;
+  
+  splines.Append(spline);
+}
+
+void SplineGeometry2d :: 
+AppendLineSegment (const int n1, const int n2, const int leftdomain, const int rightdomain,
+		   const int bc, 
+		   const double reffac, const bool hprefleft, const bool hprefright,
+		   const int copyfrom)
+{
+  SplineSegment * spline = new LineSegment(geompoints[n1],geompoints[n2]);
+  AppendSegment(spline,leftdomain,rightdomain,bc,reffac,hprefleft,hprefright,copyfrom);  
+}
+void SplineGeometry2d :: 
+AppendSplineSegment (const int n1, const int n2, const int n3, const int leftdomain, const int rightdomain, 
+		     const int bc,
+		     const double reffac, const bool hprefleft, const bool hprefright,
+		     const int copyfrom)
+{
+  SplineSegment * spline = new SplineSegment3(geompoints[n1],geompoints[n2],geompoints[n3]);
+  AppendSegment(spline,leftdomain,rightdomain,bc,reffac,hprefleft,hprefright,copyfrom);
+}
+void SplineGeometry2d :: 
+AppendCircleSegment (const int n1, const int n2, const int n3, const int leftdomain, const int rightdomain,
+		     const int bc,  
+		     const double reffac, const bool hprefleft, const bool hprefright,
+		     const int copyfrom)
+{
+  SplineSegment * spline = new CircleSegment(geompoints[n1],geompoints[n2],geompoints[n3]);
+  AppendSegment(spline,leftdomain,rightdomain,bc,reffac,hprefleft,hprefright,copyfrom);
+}
+void SplineGeometry2d :: 
+AppendDiscretePointsSegment (const ARRAY< Point<2> > & points, const int leftdomain, const int rightdomain, 
+			     const int bc, 
+			     const double reffac, const bool hprefleft, const bool hprefright,
+			     const int copyfrom)
+{
+  SplineSegment * spline = new DiscretePointsSegment(points);
+  AppendSegment(spline,leftdomain,rightdomain,bc,reffac,hprefleft,hprefright,copyfrom);
+}
+
+}
diff --git a/contrib/Netgen/libsrc/geom2d/splinegeometry2.hpp b/contrib/Netgen/libsrc/geom2d/splinegeometry2.hpp
new file mode 100644
index 0000000000..66c2a4d79f
--- /dev/null
+++ b/contrib/Netgen/libsrc/geom2d/splinegeometry2.hpp
@@ -0,0 +1,83 @@
+#ifndef FILE_SPLINEGEOMETRY2
+#define FILE_SPLINEGEOMETRY2
+
+/**************************************************************************/
+/* File:   splinegeometry2.hpp                                            */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   24. Jul. 96                                                    */
+/**************************************************************************/
+
+
+
+/// 
+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 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 PartitionBoundary (double h, Mesh & mesh2d);
+
+  void CopyEdgeMesh (int from, int to, Mesh & mesh2d, Point3dTree & searchtree);
+
+  const ARRAY<SplineSegment*> & GetSplines () const
+  { return splines; }
+
+  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
diff --git a/contrib/Netgen/libsrc/gprim/Makefile b/contrib/Netgen/libsrc/gprim/Makefile
new file mode 100644
index 0000000000..608e522844
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/Makefile
@@ -0,0 +1,14 @@
+#
+# Makefile for geometric library
+#
+src =  geom2d.cpp geom3d.cpp  \
+	geomtest3d.cpp adtree.cpp transform3d.cpp geomfuncs.cpp
+
+# reftrans.cpp rot3d.cpp
+#
+lib = gprim
+libpath = libsrc/gprim
+#
+#
+include ../makefile.inc
+#
diff --git a/contrib/Netgen/libsrc/gprim/adtree.cpp b/contrib/Netgen/libsrc/gprim/adtree.cpp
new file mode 100644
index 0000000000..25f46183e2
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/adtree.cpp
@@ -0,0 +1,2245 @@
+#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;
+    ADTreeNode *next;
+    int dir;
+    int lr;
+
+    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;
+    ADTreeNode3 *next;
+    int dir;
+    int lr;
+
+    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)
+  {
+    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;
+    ADTreeNode6 *next;
+    int dir;
+    int lr;
+
+    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);
+    ADTreeNode6 * node;
+    int dir, stacks;
+
+    stack.SetSize (10000);
+    pis.SetSize(0);
+
+    stack.Elem(1).node = root;
+    stack.Elem(1).dir = 0;
+    stacks = 1;
+
+    while (stacks)
+      {
+	node = stack.Get(stacks).node;
+	dir = stack.Get(stacks).dir; 
+	stacks--;
+
+	if (node->pi != -1)
+	  {
+	  
+	    // 	  int in = 1;
+	    // 	  for (i = 0; i < 3; i++)
+	    // 	    if (/* node->data[i] < bmin[i]  || */ node->data[i] > bmax[i] || 
+	    // 		node->data[i+3] < bmin[i+3] /* || node->data[i+3] > bmax[i+3] */ )
+	    // 	      {
+	    // 		in = 0;
+	    // 		break;
+	    // 	      }
+
+	    // 	  if (in)
+	    // 	    pis.Append (node->pi);
+
+	    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;
+	if (ndir == 6)
+	  ndir = 0;
+
+	if (node->left && bmin[dir] <= node->sep)
+	  {
+	    stacks++;
+	    stack.Elem(stacks).node = node->left;
+	    stack.Elem(stacks).dir = ndir;
+	  }
+	if (node->right && bmax[dir] >= node->sep)
+	  {
+	    stacks++;
+	    stack.Elem(stacks).node = node->right;
+	    stack.Elem(stacks).dir = ndir;
+	  }
+      }
+  }
+
+  /*
+    void ADTree6 :: GetIntersecting (const float * bmin, 
+    const float * bmax,
+    ARRAY<int> & pis) const
+    {
+    static ARRAY<ADTreeNode6*> stack(10000);
+    static ARRAY<int> stackdir(10000);
+    ADTreeNode6 * node;
+    int dir, stacks;
+
+    stack.SetSize (10000);
+    stackdir.SetSize(10000);
+    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] > 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;
+    if (ndir == 6)
+    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 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 Point3d & pmin, const Point3d & pmax)
+  {
+    float pmi[3], pma[3];
+    for (int i = 0; i < 3; i++)
+      {
+	pmi[i] = pmin.X(i+1);
+	pma[i] = pmax.X(i+1);
+      }
+    tree = new ADTree3 (pmi, pma);
+  }
+
+  Point3dTree :: ~Point3dTree ()
+  {
+    delete tree;
+  }
+
+
+
+  void Point3dTree :: Insert (const Point3d & p, int pi)
+  {
+    static float pd[3];
+    pd[0] = p.X();
+    pd[1] = p.Y();
+    pd[2] = p.Z();
+    tree->Insert (pd, pi);
+  }
+
+  void Point3dTree :: GetIntersecting (const Point3d & pmin, const Point3d & pmax, 
+				       ARRAY<int> & pis) const
+  {
+    float pmi[3], pma[3];
+    for (int i = 0; i < 3; i++)
+      {
+	pmi[i] = pmin.X(i+1);
+	pma[i] = pmax.X(i+1);
+      }
+    tree->GetIntersecting (pmi, pma, pis);
+  }
+
+
+
+
+
+
+
+
+
+
+  Box3dTree :: Box3dTree (const Point3d & apmin, const Point3d & apmax)
+  {
+    boxpmin = apmin;
+    boxpmax = apmax;
+    float tpmin[6], tpmax[6];
+    for (int i = 0; i < 3; i++)
+      {
+	tpmin[i] = tpmin[i+3] = boxpmin.X(i+1);
+	tpmax[i] = tpmax[i+3] = boxpmax.X(i+1);
+      }
+    tree = new ADTree6 (tpmin, tpmax);
+  }
+
+  Box3dTree :: ~Box3dTree ()
+  {
+    delete tree;
+  }
+
+  void Box3dTree :: Insert (const Point3d & bmin, const Point3d & bmax, int pi)
+  {
+    static float tp[6];
+
+    for (int i = 0; i < 3; i++)
+      {
+	tp[i] = bmin.X(i+1);
+	tp[i+3] = bmax.X(i+1);
+      }
+
+    tree->Insert (tp, pi);
+  }
+
+  void Box3dTree ::GetIntersecting (const Point3d & pmin, const Point3d & pmax, 
+				    ARRAY<int> & pis) const
+  {
+    float tpmin[6];
+    float tpmax[6];
+
+    for (int i = 0; i < 3; i++)
+      {
+	tpmin[i] = boxpmin.X(i+1);
+	tpmax[i] = pmax.X(i+1);
+      
+	tpmin[i+3] = pmin.X(i+1);
+	tpmax[i+3] = boxpmax.X(i+1);
+      }
+
+    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 0000000000..64f3509e1a
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/adtree.hpp
@@ -0,0 +1,477 @@
+#ifndef FILE_ADTREE
+#define FILE_ADTREE
+
+/* *************************************************************************/
+/* File:   adtree.hh                                                       */
+/* Author: Joachim Schoeberl                                               */
+/* Date:   16. Feb. 98                                                     */
+/* Redesigned by Wolfram Muehlhuber, May 1998                              */
+/* *************************************************************************/
+
+
+
+/**
+  Alternating Digital Tree
+ */
+
+#include <mystdlib.h>
+#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:
+  Point3dTree (const Point3d & pmin, const Point3d & pmax);
+  ~Point3dTree ();
+  void Insert (const Point3d & p, int pi);
+  void DeleteElement (int pi) 
+    { tree->DeleteElement(pi); }
+  void GetIntersecting (const Point3d & pmin, const Point3d & pmax, 
+			ARRAY<int> & pis) const;
+  const ADTree3 & Tree() const { return *tree; };
+};
+
+
+
+class Box3dTree
+{
+  ADTree6 * tree;
+  Point3d boxpmin, boxpmax;
+public:
+  Box3dTree (const Point3d & apmin, const Point3d & apmax);
+  ~Box3dTree ();
+  void Insert (const Point3d & bmin, const Point3d & bmax, int pi);
+  void DeleteElement (int pi) 
+    { tree->DeleteElement(pi); }
+  void GetIntersecting (const Point3d & pmin, const Point3d & 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 0000000000..01463d0255
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/geom2d.cpp
@@ -0,0 +1,485 @@
+#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 0000000000..48b5eda71d
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/geom2d.hpp
@@ -0,0 +1,870 @@
+#ifndef FILE_GEOM2D
+#define FILE_GEOM2D
+
+/* *************************************************************************/
+/* File:   geom2d.hh                                                       */
+/* Author: Joachim Schoeberl                                               */
+/* Date:   5. Aug. 95                                                      */
+/* *************************************************************************/
+
+
+
+/* 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);
+double Angle (const Vec2d & v);
+double FastAngle (const Vec2d & v);
+double Angle (const Vec2d & v1, const Vec2d & v2);
+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);
+Point2d CrossPoint (const PLine2d & l1, const PLine2d & 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);
+
+///
+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 int 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;
+    }
+
+  ///
+  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 double Angle (const Vec2d & v);
+  ///
+  friend double FastAngle (const Vec2d & v);
+  ///
+  friend double Angle (const Vec2d & v1, const Vec2d & v2);
+  ///
+  friend 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 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 0000000000..eaee776778
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/geom3d.cpp
@@ -0,0 +1,650 @@
+#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)
+{
+  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 0000000000..56bff4ad6b
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/geom3d.hpp
@@ -0,0 +1,732 @@
+#ifndef FILE_GEOM3D
+#define FILE_GEOM3D
+
+/* *************************************************************************/
+/* File:   geom3d.hh                                                       */
+/* Author: Joachim Schoeberl                                               */
+/* Date:   5. Aug. 95                                                      */
+/* *************************************************************************/
+
+
+
+
+extern 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);
+
+/// 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]; }
+
+  ///
+  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 0000000000..b2ac83824a
--- /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 0000000000..b9228c8583
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/geomfuncs.hpp
@@ -0,0 +1,136 @@
+#ifndef FILE_GEOMFUNCS
+#define FILE_GEOMFUNCS
+
+/* *************************************************************************/
+/* File:   geomfuncs.hpp                                                   */
+/* Author: Joachim Schoeberl                                               */
+/* Date:   20. Jul. 02                                                     */
+/* *************************************************************************/
+
+
+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;
+}
+
+
+
+inline Vec<3> Cross (const Vec<3> & v1, const 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) );
+}
+
+
+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 0000000000..2db82cf9e9
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/geomobjects.hpp
@@ -0,0 +1,346 @@
+#ifndef FILE_OBJECTS
+#define FILE_OBJECTS
+
+/* *************************************************************************/
+/* File:   geomobjects.hpp                                                 */
+/* Author: Joachim Schoeberl                                               */
+/* Date:   20. Jul. 02                                                     */
+/* *************************************************************************/
+
+
+
+template <int D> class Vec;
+template <int D> class Point;
+
+
+template <int D>
+class Point
+{
+
+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); }
+
+
+  Point & operator= (const Point<D> & p2)
+  {
+    for (int i = 0; i < D; i++) x[i] = p2.x[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
+{
+
+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); }
+
+  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, 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 (const Box<D> & box) 
+  : Box<D> (box) 
+  { 
+    CalcDiamCenter();
+  };
+
+  ///
+  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/geomobjects2.hpp b/contrib/Netgen/libsrc/gprim/geomobjects2.hpp
new file mode 100644
index 0000000000..014a38525a
--- /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 0000000000..755f35a878
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/geomops.hpp
@@ -0,0 +1,391 @@
+#ifndef FILE_GEOMOPS
+#define FILE_GEOMOPS
+
+/* *************************************************************************/
+/* File:   geomops.hpp                                                     */
+/* Author: Joachim Schoeberl                                               */
+/* Date:   20. Jul. 02                                                     */
+/* *************************************************************************/
+
+
+/*
+
+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 0000000000..c615da14ec
--- /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 0000000000..14d1d58bd1
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/geomtest3d.cpp
@@ -0,0 +1,1223 @@
+#include <mystdlib.h>
+#include <myadt.hpp>
+
+#include <linalg.hpp>
+#include <gprim.hpp>
+
+namespace netgen
+{
+int
+IntersectTriangleLine (const Point3d ** tri, const Point3d ** 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 = 1; i <= 3; i++)
+    {
+      a.Elem(i, 1) = -vl.X(i);
+      a.Elem(i, 2) = vt1.X(i);
+      a.Elem(i, 3) = vt2.X(i);
+      rs.Elem(i) = vrs.X(i);
+    }
+
+  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.Get(1) >= -eps && lami.Get(1) <= 1+eps && 
+       lami.Get(2) >= -eps && lami.Get(3) >= -eps && 
+       lami.Get(2) + lami.Get(3) <= 1+eps)  && !
+      (lami.Get(1) >= eps && lami.Get(1) <= 1-eps && 
+       lami.Get(2) >= eps && lami.Get(3) >= eps && 
+       lami.Get(2) + lami.Get(3) <= 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.Get(1) >= 0 && lami.Get(1) <= 1 && 
+      lami.Get(2) >= 0 && lami.Get(3) >= 0 && lami.Get(2) + lami.Get(3) <= 1)
+    {
+
+      return 1;
+    }
+
+  return 0;
+}
+
+
+
+
+
+int IntersectTetTriangle (const Point3d ** tet, const Point3d ** 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;
+
+#ifdef MARK
+  MARK (inttettri1);
+#endif
+
+  double eps2 = eps * eps;
+  int loctripi[3], cnt = 0;
+  int loctetpi[4];
+
+  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;
+
+#ifdef MARK
+  MARK (inttettri2);
+#endif
+
+
+  //  (*testout) << "tet-trig inters, cnt = " << cnt << endl;
+  
+  // cnt .. number of common points
+  switch (cnt)
+    {
+    case 0:
+      {
+#ifdef MARK
+  MARK (inttettric0);
+#endif
+
+	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 Point3d * 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:
+      {
+#ifdef MARK
+  MARK (inttettric1);
+#endif
+
+	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:
+      {
+#ifdef MARK
+  MARK (inttettric2);
+#endif
+
+	// 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:
+      {
+#ifdef MARK
+  MARK (inttettric3);
+#endif
+
+	// common face
+	return 0;
+      }
+    }
+
+  (*testout) << "hit, cnt = " << cnt << endl;
+  return 1;
+}
+
+
+
+
+
+int IntersectTetTriangleRef (const Point3d ** tri, const int * tripi)
+{
+  int i, j;
+  double eps = 1e-8;
+  double eps2 = eps * eps;
+
+  static Point3d rtetp1(0, 0, 0);
+  static Point3d rtetp2(1, 0, 0);  
+  static Point3d rtetp3(0, 1, 0); 
+  static Point3d rtetp4(0, 0, 1);
+
+  static const Point3d * 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;
+
+#ifdef MARK
+  MARK (inttettriref2);
+#endif
+  
+
+  switch (cnt)
+    {
+    case 0:
+      {
+#ifdef MARK
+  MARK (inttettric0ref);
+#endif
+
+	Vec3d no, n;
+	//	int inpi[3];
+	int pside[3][4];
+
+	for (j = 0; j < 3; j++)
+	  {
+	    pside[j][0] = (*tri[j]).X() > -eps;
+	    pside[j][1] = (*tri[j]).Y() > -eps;
+	    pside[j][2] = (*tri[j]).Z() > -eps;
+	    pside[j][3] = (*tri[j]).X() + (*tri[j]).Y() + (*tri[j]).Z() < 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 Point3d * 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:
+      {
+#ifdef MARK
+  MARK (inttettric1ref);
+#endif
+
+	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:
+      {
+#ifdef MARK
+  MARK (inttettric2ref);
+#endif
+
+	// 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:
+      {
+#ifdef MARK
+  MARK (inttettric3ref);
+#endif
+
+	// common face
+	return 0;
+      }
+    }
+
+  (*testout) << "hit, cnt = " << cnt << endl;
+  return 1;
+}
+
+
+
+
+
+
+
+
+
+
+
+int IntersectTriangleTriangle (const Point3d ** tri1, const Point3d ** 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 Point3d * 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 Point3d ** pts, Point3d & c)
+{
+  /*
+  static DenseMatrix a(3), inva(3);
+  static Vector rs(3), sol(3);
+  int i;
+  double h = Dist(*pts[0], *pts[1]);
+
+  for (i = 1; i <= 3; i++)
+    {
+      const Point3d & p1 = *pts[0];
+      const Point3d & p2 = *pts[i];
+      Vec3d v(p1, p2);
+      a.Elem(i,1) = v.X();
+      a.Elem(i,2) = v.Y();
+      a.Elem(i,3) = v.Z();
+
+      rs.Elem(i) = 0.5 * (v * v);
+    }
+
+  if (fabs (a.Det()) <= 1e-12 * h * h * h)
+    {
+      (*testout) << "CalcSphereCenter: degenerated" << endl;
+      return 1;
+    }
+
+  CalcInverse (a, inva);
+  inva.Mult (rs, sol);
+
+  for (i = 1; i <= 3; i++)
+    c.X(i) = pts[0]->X(i) + sol.Elem(i);
+  */
+
+  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.Elem(1) = v1 * v1;
+  rs.Elem(2) = v2 * v2;
+
+  a.Elem(1,1) = 2 * rs.Get(1);
+  a.Elem(1,2) = a.Elem(2,1) = 2 * (v1 * v2);
+  a.Elem(2,2) = 2 * rs.Get(2);
+
+  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.Get(1);
+  v2 *= sol.Get(2);
+
+  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 0000000000..f801b8cdef
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/geomtest3d.hpp
@@ -0,0 +1,80 @@
+#ifndef FILE_GEOMTEST3D
+#define FILE_GEOMTEST3D
+
+/* *************************************************************************/
+/* File:   geomtest3d.hh                                                   */
+/* Author: Joachim Schoeberl                                               */
+/* Date:   13. Feb. 98                                                     */
+/* *************************************************************************/
+
+
+
+extern int
+IntersectTriangleLine (const Point3d ** tri, const Point3d ** 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 Point3d ** tet, const Point3d ** 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 Point3d ** tri1, const Point3d ** 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 Point3d ** pts, Point3d & 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);
+
+
+extern double MinDistLP2 (const Point2d & lp1, const Point2d & lp2, const Point2d & p);
+
+extern double MinDistLP2 (const Point3d & lp1, const Point3d & lp2, const Point3d & p);
+
+extern double MinDistTP2 (const Point3d & tp1, const Point3d & tp2, 
+			  const Point3d & tp3, const Point3d & p);
+
+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 0000000000..0f57ebac86
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/gprim.hpp
@@ -0,0 +1,26 @@
+#ifndef FILE_GPRIM
+#define FILE_GPRIM
+
+/* *************************************************************************/
+/* File:   gprim.hpp                                                        */
+/* Author: Joachim Schoeberl                                               */
+/* Date:   14. Aug. 97                                                     */
+/* *************************************************************************/
+
+
+namespace netgen
+{
+#include "geomobjects.hpp"
+#include "geomops.hpp"
+#include "geomfuncs.hpp"
+
+#include "geom2d.hpp"
+#include "geom3d.hpp"
+#include "geomtest3d.hpp"
+// #include "rot3d.hpp"
+#include "transform3d.hpp"
+// #include "reftrans.hpp"
+#include "adtree.hpp"
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/gprim/testgeom.cpp b/contrib/Netgen/libsrc/gprim/testgeom.cpp
new file mode 100644
index 0000000000..8413f0f2b5
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/testgeom.cpp
@@ -0,0 +1,20 @@
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <gprim.hpp>
+
+
+Vec<2> func1 (const Point<2> & a, const Point<2> & b)
+{
+  return a-b;
+}
+
+void func2 (Point<3> & a, Vec<3> & v)
+{
+  a += 3.4 * v;
+}
+
+void func3 (const Mat<2,2> & m, const Vec<2> & vc, Vec<2> & res)
+{
+  res += Trans (m) * vc;
+}
diff --git a/contrib/Netgen/libsrc/gprim/transform3d.cpp b/contrib/Netgen/libsrc/gprim/transform3d.cpp
new file mode 100644
index 0000000000..ea62fffe5a
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/transform3d.cpp
@@ -0,0 +1,173 @@
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <gprim.hpp>
+#include <linalg.hpp>
+
+namespace netgen
+{
+
+Transformation3d :: Transformation3d ()
+{
+  int i, j;
+  for (i = 0; i < 3; i++)
+    {
+      offset[i] = 0;
+      for (j = 0; j < 3; j++)
+	lin[i][j] = 0;
+    }
+}
+
+Transformation3d :: Transformation3d (const Vec3d & translate)
+{
+  int i, j;
+  for (i = 0; i < 3; i++)
+    for (j = 0; j < 3; j++)
+      lin[i][j] = 0;
+  for (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)
+{
+  int i, j;
+  for (i = 1; i <= 3; i++)
+    {
+      offset[i-1] = (*pp[0]).X(i);
+      for (j = 1; j <= 3; j++)
+	lin[i-1][j-1] = (*pp[j]).X(i) - (*pp[0]).X(i);
+    }
+}
+
+Transformation3d :: Transformation3d (const Point3d pp[])
+{
+  int i, j;
+  for (i = 1; i <= 3; i++)
+    {
+      offset[i-1] = pp[0].X(i);
+      for (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);
+  int i, j;
+  
+  for (i = 1; i <= 3; i++)
+    {
+      b.Elem(i) = offset[i-1];
+      for (j = 1; j <= 3; j++)
+	a.Elem(i, j) = lin[i-1][j-1];
+    }
+
+  ::netgen::CalcInverse (a, inva);
+  inva.Mult (b, sol);
+
+  for (i = 1; i <= 3; i++)
+    {
+      inv.offset[i-1] = -sol.Get(i);
+      for (j = 1; j <= 3; j++)
+	inv.lin[i-1][j-1] = inva.Elem(i, j);
+    }
+}
+
+
+void  Transformation3d:: 
+Combine (const Transformation3d & ta, const Transformation3d & tb)
+{
+  int i, j, k;
+
+  // o = o_a+ m_a o_b
+  // m = m_a m_b
+
+  for (i = 0; i <= 2; i++)
+    {
+      offset[i] = ta.offset[i];
+      for (j = 0; j <= 2; j++)
+	offset[i] += ta.lin[i][j] * tb.offset[j];
+    }
+  
+  for (i = 0; i <= 2; i++)
+    for (j = 0; j <= 2; j++)
+      {
+	lin[i][j] = 0;
+	for (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)
+{
+  int i, j;
+  ost << "offset = ";
+  for (i = 0; i <= 2; i++)
+    ost << trans.offset[i] << " ";
+  ost << endl << "linear = " << endl;
+  for (i = 0; i <= 2; i++)
+    {
+      for (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 0000000000..7472257297
--- /dev/null
+++ b/contrib/Netgen/libsrc/gprim/transform3d.hpp
@@ -0,0 +1,174 @@
+#ifndef FILE_TRANSFORM3D
+#define FILE_TRANSFORM3D
+
+/* *************************************************************************/
+/* File:   transform3d.hh                                                  */
+/* Author: Joachim Schoeberl                                               */
+/* Date:   22. Mar. 98                                                     */
+/* *************************************************************************/
+
+/*
+  Affine - Linear mapping in 3D space
+ */
+
+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);
+	}
+    }
+  /// 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));
+  }
+
+  /// 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/FlexLexer.h b/contrib/Netgen/libsrc/include/FlexLexer.h
new file mode 100644
index 0000000000..e242c836f8
--- /dev/null
+++ b/contrib/Netgen/libsrc/include/FlexLexer.h
@@ -0,0 +1,184 @@
+// $Header: /cvsroot/gmsh/contrib/Netgen/libsrc/include/FlexLexer.h,v 1.1 2005-09-21 17:29:38 geuzaine Exp $
+
+// FlexLexer.h -- define interfaces for lexical analyzer classes generated
+//		  by flex
+
+// Copyright (c) 1993 The Regents of the University of California.
+// All rights reserved.
+//
+// This code is derived from software contributed to Berkeley by
+// Kent Williams and Tom Epperly.
+//
+// Redistribution and use in source and binary forms are permitted provided
+// that: (1) source distributions retain this entire copyright notice and
+// comment, and (2) distributions including binaries display the following
+// acknowledgement:  ``This product includes software developed by the
+// University of California, Berkeley and its contributors'' in the
+// documentation or other materials provided with the distribution and in
+// all advertising materials mentioning features or use of this software.
+// Neither the name of the University nor the names of its contributors may
+// be used to endorse or promote products derived from this software without
+// specific prior written permission.
+// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+// This file defines FlexLexer, an abstract class which specifies the
+// external interface provided to flex C++ lexer objects, and yyFlexLexer,
+// which defines a particular lexer class.
+//
+// If you want to create multiple lexer classes, you use the -P flag
+// to rename each yyFlexLexer to some other xxFlexLexer.  You then
+// include <FlexLexer.h> in your other sources once per lexer class:
+//
+//	#undef yyFlexLexer
+//	#define yyFlexLexer xxFlexLexer
+//	#include <FlexLexer.h>
+//
+//	#undef yyFlexLexer
+//	#define yyFlexLexer zzFlexLexer
+//	#include <FlexLexer.h>
+//	...
+
+#ifndef __FLEX_LEXER_H
+// Never included before - need to define base class.
+#define __FLEX_LEXER_H
+
+
+extern "C++" {
+struct yy_buffer_state;
+typedef int yy_state_type;
+
+class FlexLexer {
+public:
+	virtual ~FlexLexer()	{ }
+
+	const char* YYText()	{ return yytext; }
+	int YYLeng()		{ return yyleng; }
+
+	virtual void
+		yy_switch_to_buffer( struct yy_buffer_state* new_buffer ) = 0;
+	virtual struct yy_buffer_state*
+		yy_create_buffer( istream* s, int size ) = 0;
+	virtual void yy_delete_buffer( struct yy_buffer_state* b ) = 0;
+	virtual void yyrestart( istream* s ) = 0;
+
+	virtual int yylex() = 0;
+
+	// Call yylex with new input/output sources.
+	int yylex( istream* new_in, ostream* new_out = 0 )
+		{
+		switch_streams( new_in, new_out );
+		return yylex();
+		}
+
+	// Switch to new input/output streams.  A nil stream pointer
+	// indicates "keep the current one".
+	virtual void switch_streams( istream* new_in = 0,
+					ostream* new_out = 0 ) = 0;
+
+	int lineno() const		{ return yylineno; }
+
+	int debug() const		{ return yy_flex_debug; }
+	void set_debug( int flag )	{ yy_flex_debug = flag; }
+
+protected:
+	char* yytext;
+	int yyleng;
+	int yylineno;		// only maintained if you use %option yylineno
+	int yy_flex_debug;	// only has effect with -d or "%option debug"
+};
+
+}
+#endif
+
+#if defined(yyFlexLexer) || ! defined(yyFlexLexerOnce)
+// Either this is the first time through (yyFlexLexerOnce not defined),
+// or this is a repeated include to define a different flavor of
+// yyFlexLexer, as discussed in the flex man page.
+#define yyFlexLexerOnce
+
+class yyFlexLexer : public FlexLexer {
+public:
+	// arg_yyin and arg_yyout default to the cin and cout, but we
+	// only make that assignment when initializing in yylex().
+	yyFlexLexer( istream* arg_yyin = 0, ostream* arg_yyout = 0 );
+
+	virtual ~yyFlexLexer();
+
+	void yy_switch_to_buffer( struct yy_buffer_state* new_buffer );
+	struct yy_buffer_state* yy_create_buffer( istream* s, int size );
+	void yy_delete_buffer( struct yy_buffer_state* b );
+	void yyrestart( istream* s );
+
+	virtual int yylex();
+	virtual void switch_streams( istream* new_in, ostream* new_out );
+
+protected:
+	virtual int LexerInput( char* buf, int max_size );
+	virtual void LexerOutput( const char* buf, int size );
+	virtual void LexerError( const char* msg );
+
+	void yyunput( int c, char* buf_ptr );
+	int yyinput();
+
+	void yy_load_buffer_state();
+	void yy_init_buffer( struct yy_buffer_state* b, istream* s );
+	void yy_flush_buffer( struct yy_buffer_state* b );
+
+	int yy_start_stack_ptr;
+	int yy_start_stack_depth;
+	int* yy_start_stack;
+
+	void yy_push_state( int new_state );
+	void yy_pop_state();
+	int yy_top_state();
+
+	yy_state_type yy_get_previous_state();
+	yy_state_type yy_try_NUL_trans( yy_state_type current_state );
+	int yy_get_next_buffer();
+
+	istream* yyin;	// input source for default LexerInput
+	ostream* yyout;	// output sink for default LexerOutput
+
+	struct yy_buffer_state* yy_current_buffer;
+
+	// yy_hold_char holds the character lost when yytext is formed.
+	char yy_hold_char;
+
+	// Number of characters read into yy_ch_buf.
+	int yy_n_chars;
+
+	// Points to current character in buffer.
+	char* yy_c_buf_p;
+
+	int yy_init;		// whether we need to initialize
+	int yy_start;		// start state number
+
+	// Flag which is used to allow yywrap()'s to do buffer switches
+	// instead of setting up a fresh yyin.  A bit of a hack ...
+	int yy_did_buffer_switch_on_eof;
+
+	// The following are not always needed, but may be depending
+	// on use of certain flex features (like REJECT or yymore()).
+
+	yy_state_type yy_last_accepting_state;
+	char* yy_last_accepting_cpos;
+
+	yy_state_type* yy_state_buf;
+	yy_state_type* yy_state_ptr;
+
+	char* yy_full_match;
+	int* yy_full_state;
+	int yy_full_lp;
+
+	int yy_lp;
+	int yy_looking_for_trail_begin;
+
+	int yy_more_flag;
+	int yy_more_len;
+	int yy_more_offset;
+	int yy_prev_more_offset;
+};
+
+#endif
diff --git a/contrib/Netgen/libsrc/include/csg.hpp b/contrib/Netgen/libsrc/include/csg.hpp
new file mode 100644
index 0000000000..ffd45ef0bf
--- /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 0000000000..bf0965c228
--- /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 0000000000..1e827aaf8c
--- /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 0000000000..a38e918698
--- /dev/null
+++ b/contrib/Netgen/libsrc/include/incvis.hpp
@@ -0,0 +1,33 @@
+// libraries for User interface:
+
+/*
+#include <tcl8.3.h>
+#include <tk8.3.h>
+
+#include <GL/gl.h>
+#include <GL/glu.h>
+#include "../togl/togl.h"
+
+#include <tix8.1.h>
+*/
+
+
+#include <tcl.h>
+#include <tk.h>
+
+
+#if TK_MAJOR_VERSION==8 && TK_MINOR_VERSION==4
+#define tcl_const const
+#else
+#define tcl_const
+#endif
+
+
+#include <GL/gl.h>
+#include <GL/glu.h>
+#include "../../togl/togl.h"
+
+
+
+// Just the init-call
+// #include <tix.h>
diff --git a/contrib/Netgen/libsrc/include/linalg.hpp b/contrib/Netgen/libsrc/include/linalg.hpp
new file mode 100644
index 0000000000..e96bd036c3
--- /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 0000000000..e41a88f9f2
--- /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 0000000000..d36bef05c1
--- /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 0000000000..c34ce56a8e
--- /dev/null
+++ b/contrib/Netgen/libsrc/include/mydefs.hpp
@@ -0,0 +1,29 @@
+#ifndef FILE_MYDEFS
+#define FILE_MYDEFS
+
+/**************************************************************************/
+/* File:   mydefs.hh                                                      */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   10. Mar. 98                                                    */
+/**************************************************************************/
+
+/*
+  defines for graphics, testmodes, ...
+*/
+
+
+// #define DEBUG
+
+
+#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 0000000000..2249aea3a2
--- /dev/null
+++ b/contrib/Netgen/libsrc/include/mystdlib.h
@@ -0,0 +1,69 @@
+#ifndef FILE_MYSTDLIB
+#define FILE_MYSTDLIB
+
+
+#include <iostream>
+#include <iomanip>
+#include <fstream>
+#include <sstream>
+
+#ifdef OLDCINCLUDE
+
+// e.g., CC compiler on SGI
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <malloc.h>
+#include <ctype.h>
+#include <time.h>
+
+#else
+ 
+// new standard
+#include <cstdlib>
+#include <cstdio>
+#include <cmath>
+#include <cctype>
+#include <ctime>
+#endif
+
+
+
+#include <new>
+#include <string>
+#include <typeinfo>
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+
+/*** Windows headers ***/
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <afxwin.h>
+#include <afxmt.h>
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN
+#include <winnt.h>
+#endif /* WIN32 */
+
+
+/*
+extern void* operator new(std::size_t) throw (std::bad_alloc);
+extern void* operator new[](std::size_t) throw (std::bad_alloc);
+extern void operator delete(void*) throw();
+extern void operator delete[](void*) throw();
+*/
+
+
+extern int mem_alloc;
+extern int mem_total_alloc;
+extern int mem_max_alloc;
+extern int mem_total_alloc_array;
+extern int mem_total_alloc_table;
+
+
+using namespace std;
+
+#endif
diff --git a/contrib/Netgen/libsrc/include/occgeom.hpp b/contrib/Netgen/libsrc/include/occgeom.hpp
new file mode 100644
index 0000000000..af258e0df0
--- /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 0000000000..6b8a0b61c8
--- /dev/null
+++ b/contrib/Netgen/libsrc/include/opti.hpp
@@ -0,0 +1 @@
+#include "../opti/opti.hpp"
diff --git a/contrib/Netgen/libsrc/include/stepgeom.hpp b/contrib/Netgen/libsrc/include/stepgeom.hpp
new file mode 100644
index 0000000000..d2c5c5e41e
--- /dev/null
+++ b/contrib/Netgen/libsrc/include/stepgeom.hpp
@@ -0,0 +1,10 @@
+#include "../stepgeom/geomanif.hh"
+#include "../stepgeom/geopac2d.hh"
+#include "../stepgeom/geopac3d.hh"
+#include "../stepgeom/geosplinesurf.hh"
+#include "../stepgeom/algprim.hh"
+#include "../stepgeom/scenery.hh"
+#include "../stepgeom/brep.hh"
+#include "../stepgeom/adtcrit.hh"
+#include "../stepgeom/STEPgeom.hh"
+#include "../stepgeom/visapprox.hh"
diff --git a/contrib/Netgen/libsrc/include/stepreader.hpp b/contrib/Netgen/libsrc/include/stepreader.hpp
new file mode 100644
index 0000000000..0214d58d9d
--- /dev/null
+++ b/contrib/Netgen/libsrc/include/stepreader.hpp
@@ -0,0 +1 @@
+#include "../stepgeom/STEPread.hh"
diff --git a/contrib/Netgen/libsrc/include/stlgeom.hpp b/contrib/Netgen/libsrc/include/stlgeom.hpp
new file mode 100644
index 0000000000..f1eea264e1
--- /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 0000000000..f026f5a458
--- /dev/null
+++ b/contrib/Netgen/libsrc/include/visual.hpp
@@ -0,0 +1 @@
+#include "../visualization/visual.hpp"
diff --git a/contrib/Netgen/libsrc/interface/Makefile b/contrib/Netgen/libsrc/interface/Makefile
new file mode 100644
index 0000000000..c47dbe2c5a
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/Makefile
@@ -0,0 +1,7 @@
+src = nginterface.cpp writeuser.cpp writediffpack.cpp writeabaqus.cpp writefluent.cpp writepermas.cpp writetochnog.cpp writetecplot.cpp wuchemnitz.cpp writetochnog.cpp writefeap.cpp writeelmer.cpp  writegmsh.cpp readuser.cpp importsolution.cpp 
+#
+lib = nginterface
+libpath = libsrc/interface
+#
+include ../makefile.inc
+#
diff --git a/contrib/Netgen/libsrc/interface/importsolution.cpp b/contrib/Netgen/libsrc/interface/importsolution.cpp
new file mode 100644
index 0000000000..3973d3927e
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/importsolution.cpp
@@ -0,0 +1,121 @@
+//
+//  Read solution file
+//
+
+
+#include <mystdlib.h>
+
+
+#include <myadt.hpp>
+#include <linalg.hpp>
+#include <csg.hpp>
+#include <meshing.hpp>
+
+#include "nginterface.h"
+
+namespace netgen
+{
+#include "writeuser.hpp"
+
+
+void ImportSolution (const char * filename)
+{
+  ifstream inf (filename);
+  char buf[100], name[1000];
+  int i, j, 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;
+	  if (strcmp (type, "element") == 0)
+	    soldata.soltype = NG_SOLUTION_ELEMENT;
+	  if (strcmp (type, "surfaceelement") == 0)
+	    soldata.soltype = NG_SOLUTION_SURFACE_ELEMENT;
+	  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/interface/nginterface.cpp b/contrib/Netgen/libsrc/interface/nginterface.cpp
new file mode 100644
index 0000000000..784be18dc5
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/nginterface.cpp
@@ -0,0 +1,1476 @@
+#include <mystdlib.h>
+
+
+#include <meshing.hpp>
+#include <csg.hpp>
+#include <geometry2d.hpp>
+#include <stlgeom.hpp>
+
+#ifdef OCCGEOMETRY
+#include <occgeom.hpp>
+#endif
+
+
+#include <visual.hpp>
+
+#include "nginterface.h"
+// #include <FlexLexer.h>
+
+
+// #include <mystdlib.h>
+
+
+namespace netgen
+{
+  extern AutoPtr<Mesh> mesh;
+  extern VisualSceneMesh vsmesh;
+  extern Tcl_Interp * tcl_interp;
+
+  extern AutoPtr<SplineGeometry2d> geometry2d;
+  extern AutoPtr<CSGeometry> geometry;
+  extern STLGeometry * stlgeometry;
+#ifdef OCCGEOMETRY
+  extern OCCGeometry * occgeometry;
+#endif
+
+#ifdef OPENGL
+  extern VisualSceneSolution vssolution;
+#endif
+  extern CSGeometry * ParseCSG (istream & istr);
+}
+
+
+using namespace netgen;
+
+/*
+  extern void * operator new (size_t s);
+  extern void * operator new [] (size_t s);
+  extern void operator delete (void * p);
+  extern void operator delete [] (void * p);
+*/
+
+// extern FlexLexer * lexer;
+
+
+
+void Ng_LoadGeometry (char * filename)
+{
+  ifstream infile (filename);
+
+  geometry.Reset ();
+  geometry2d.Reset ();
+
+#ifdef OCCGEOMETRY
+  delete occgeometry;
+  occgeometry = 0;
+#endif
+
+  if ((strcmp (&filename[strlen(filename)-3], "geo") == 0) ||
+      (strcmp (&filename[strlen(filename)-3], "GEO") == 0) ||
+      (strcmp (&filename[strlen(filename)-3], "Geo") == 0))
+    {
+      geometry.Reset (netgen::ParseCSG (infile));
+
+      if (!geometry)
+	{
+	  geometry.Reset (new CSGeometry ());
+	  throw NgException ("input file not found");
+	}
+
+      geometry -> FindIdenticSurfaces(1e-6);
+
+      double detail = atof (Tcl_GetVar (tcl_interp, "geooptions.detail", 0));
+      double facets = atof (Tcl_GetVar (tcl_interp, "geooptions.facets", 0));
+      Box<3> box (geometry->BoundingBox());
+      
+      if (atoi (Tcl_GetVar (tcl_interp, "geooptions.drawcsg", 0)))
+	geometry->CalcTriangleApproximation(box, detail, facets);
+
+      //      geometry->CalcTriangleApproximation (box, 0.01, 10);
+    }
+
+  else if (strcmp (&filename[strlen(filename)-4], "in2d") == 0)
+    {
+      geometry2d.Reset (new SplineGeometry2d());
+      geometry2d -> Load (filename);
+    }
+
+  else if ((strcmp (&filename[strlen(filename)-3], "stl") == 0) ||
+	   (strcmp (&filename[strlen(filename)-3], "STL") == 0) ||
+	   (strcmp (&filename[strlen(filename)-3], "Stl") == 0))
+    {
+      ifstream infile(filename);
+      stlgeometry = STLGeometry :: Load (infile);
+      stlgeometry->edgesfound = 0;
+      Mesh meshdummy;
+      stlgeometry->Clear();
+      stlgeometry->BuildEdges();
+      stlgeometry->MakeAtlas(meshdummy);
+      stlgeometry->CalcFaceNums();
+      stlgeometry->AddFaceEdges();
+      stlgeometry->LinkEdges();
+    }
+
+#ifdef OCCGEOMETRY
+  else if ((strcmp (&filename[strlen(filename)-4], "iges") == 0) ||
+	   (strcmp (&filename[strlen(filename)-3], "igs") == 0) ||
+	   (strcmp (&filename[strlen(filename)-3], "IGS") == 0) ||
+	   (strcmp (&filename[strlen(filename)-4], "IGES") == 0))
+    {
+      PrintMessage (1, "Load IGES geometry file ", filename);
+      occgeometry = LoadOCC_IGES (filename);
+    }
+  else if ((strcmp (&filename[strlen(filename)-4], "step") == 0) ||
+	   (strcmp (&filename[strlen(filename)-3], "stp") == 0) ||
+	   (strcmp (&filename[strlen(filename)-3], "STP") == 0) ||
+	   (strcmp (&filename[strlen(filename)-4], "STEP") == 0))
+    {
+      PrintMessage (1, "Load STEP geometry file ", filename);
+      occgeometry = LoadOCC_STEP (filename);
+    }
+#endif
+  else
+    {
+      cerr << "Unknown geometry extension!!" << endl;
+    }
+}                          
+
+
+void Ng_LoadMesh (char * filename)
+{
+  mesh.Reset (new Mesh());
+  mesh->Load (filename);
+}
+
+
+
+int Ng_GetDimension ()
+{
+  return mesh->GetDimension();
+}
+
+int Ng_GetNP ()
+{
+  return mesh->GetNP();
+}
+
+int Ng_GetNV ()
+{
+  return mesh->GetNV();
+}
+
+int Ng_GetNE ()
+{
+  if (mesh->GetDimension() == 3)
+    return mesh->GetNE();
+  else
+    return mesh->GetNSE();
+}
+
+int Ng_GetNSE ()
+{
+  if (mesh->GetDimension() == 3)
+    return mesh->GetNSE();
+  else
+    return mesh->GetNSeg();
+}
+
+void Ng_GetPoint (int pi, double * p)
+{
+  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:
+		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:
+	      {
+		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
+    {
+      int i;
+      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;
+    }
+}
+
+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;
+    }
+  return 0;
+}
+
+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.pmid < 0)
+	{
+	  epi[0] = seg.p1;
+	  epi[1] = seg.p2;
+	  
+	  if (np) *np = 2;
+	  return NG_SEGM;
+	}
+      else
+	{
+	  epi[0] = seg.p1;
+	  epi[1] = seg.p2;
+	  epi[2] = seg.pmid;
+
+	  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;
+}
+
+
+void Ng_GetNormalVector (int sei, int locpi, double * nv)
+{
+  nv[0] = 0; 
+  nv[1] = 0;
+  nv[2] = 1;
+
+  (*testout) << "Ng_GetNormalVector (sei = " << sei << ", locpi = " << locpi << ")" << endl;
+  
+  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 OCCGEOMETRY
+      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);
+	}
+      else
+#endif
+      if (geometry)
+	{
+	  (*testout) << "geometry defined" << endl;
+	  n = geometry->GetSurface (surfi) -> GetNormalVector(p);
+	  (*testout) << "aus is" << endl;
+	  nv[0] = n(0);
+	  nv[1] = n(1);
+	  nv[2] = n(2);
+	}
+    }
+}
+
+
+int Ng_FindElementOfPoint (double * p, double * lami, int build_searchtree, int index)
+{
+  if (mesh->GetDimension() == 3)
+    {
+      Point3d p3d(p[0], p[1], p[2]);
+      int ind = 
+	mesh->GetElementOfPoint(p3d, lami, build_searchtree != 0, index);
+      return ind;
+    }
+  else
+    {
+      double lam3[3];
+      Point3d p2d(p[0], p[1], 0);
+      int ind = 
+	mesh->GetElementOfPoint(p2d, lam3, build_searchtree != 0, index);
+      lami[0] = lam3[0];
+      lami[1] = lam3[1];
+      return ind;
+    }
+}
+
+
+
+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]);
+  //    (*testout) << "elnr = " << ei << ", eltrans, xl = " << xl << endl;
+      Point<3> xg;
+      Mat<3,3> dx;
+
+      mesh->GetCurvedElements().CalcElementTransformation (xl, ei-1, xg, dx);
+
+      // 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);
+	    }
+	}
+    }
+}
+
+
+void Ng_GetSurfaceElementTransformation (int sei, const double * xi, 
+					 double * x, double * dxdxi)
+{
+  if (mesh->GetDimension() == 2)
+    {
+      Point<3> xg;
+      Vec<3> dx;
+
+      // still 1-based arrays
+      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;
+      
+      // still 1-based arrays
+      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);
+	    }
+	}
+    }
+}
+
+
+
+void Ng_GetSurfaceElementNeighbouringDomains(const int selnr, int & in, int & out)
+{
+  in = mesh->GetFaceDescriptor((*mesh)[static_cast<SurfaceElementIndex>(selnr)].GetIndex()).DomainIn();
+  out = mesh->GetFaceDescriptor((*mesh)[static_cast<SurfaceElementIndex>(selnr)].GetIndex()).DomainOut();
+}
+
+
+void Ng_SetRefinementFlag (int ei, int flag)
+{
+  if (mesh->GetDimension() == 3)
+    mesh->VolumeElement(ei).SetRefinementFlag (flag != 0);
+  else
+    mesh->SurfaceElement(ei).SetRefinementFlag (flag != 0);
+}
+
+void Ng_SetSurfaceRefinementFlag (int ei, int flag)
+{
+  if (mesh->GetDimension() == 3)
+    mesh->SurfaceElement(ei).SetRefinementFlag (flag != 0);
+}
+
+
+void Ng_Refine (NG_REFINEMENT_TYPE reftype)
+{
+  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;
+  Refinement * ref;
+
+  if (geometry2d)
+    ref = new Refinement2d(*geometry2d);
+  else if (stlgeometry)
+    ref = new RefinementSTLGeometry(*stlgeometry);
+#ifdef OCCGEOMETRY
+  else if (occgeometry)
+    ref = new OCCRefinementSurfaces (*occgeometry);
+#endif
+  else if (geometry && mesh->GetDimension() == 3)
+    ref = new RefinementSurfaces(*geometry);
+  else
+    {
+      ref = new Refinement();
+    }
+
+  ref -> Bisect (*mesh, biopt);
+
+  mesh -> UpdateTopology();
+  // mesh -> GetCurvedElements().BuildCurvedElements (ref, mparam.elementorder);
+  delete ref;
+}
+
+void Ng_SecondOrder ()
+{
+  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
+    {
+      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_HighOrder (int order)
+{
+  Refinement * ref;
+
+  if (stlgeometry)
+    ref = new RefinementSTLGeometry (*stlgeometry);
+#ifdef OCCGEOMETRY
+  else if (occgeometry)
+    ref = new OCCRefinementSurfaces (*occgeometry);
+#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 (ref, order);
+
+  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;
+}
+
+
+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();
+}
+
+
+
+int Ng_GetNLevels ()
+{
+  return mesh->mglevels;
+}
+
+
+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);
+}
+
+
+
+
+
+
+void Ng_InitSolutionData (Ng_SolutionData * soldata)
+{
+  soldata -> name = NULL;
+  soldata -> data = NULL;
+  soldata -> components = 1;
+  soldata -> dist = 1;
+  soldata -> order = 1;
+  soldata -> iscomplex = 0;
+  soldata -> draw_surface = 1;
+  soldata -> draw_volume = 1;
+  soldata -> soltype = NG_SOLUTION_NODAL;
+  soldata -> solclass = 0;
+}
+
+void Ng_SetSolutionData (Ng_SolutionData * soldata)
+{
+#ifdef OPENGL
+  //   vssolution.ClearSolutionData ();
+  VisualSceneSolution::SolData * vss = new VisualSceneSolution::SolData;
+
+  //  cout << "Add solution " << soldata->name << ", type = " << soldata->soltype << endl;
+
+  vss->name = new char[strlen (soldata->name)+1];
+  strcpy (vss->name, soldata->name);
+
+  vss->data = soldata->data;
+  vss->components = soldata->components;
+  vss->dist = soldata->dist;
+  vss->order = soldata->order;
+  vss->iscomplex = bool(soldata->iscomplex);
+  vss->draw_surface = soldata->draw_surface;
+  vss->draw_volume = soldata->draw_volume;
+  vss->soltype = VisualSceneSolution::SolType (soldata->soltype);
+  vss->solclass = soldata->solclass;
+  vssolution.AddSolutionData (vss);
+#endif
+}
+
+void Ng_ClearSolutionData ()
+{
+  vssolution.ClearSolutionData();
+}
+
+
+
+void Ng_Redraw ()
+{
+#ifdef OPENGL
+  vssolution.UpdateSolutionTimeStamp();
+  Render();
+#endif
+}
+
+
+void Ng_SetVisualizationParameter (const char * name, const char * value)
+{
+#ifdef OPENGL
+  char buf[100];
+  sprintf (buf, "visoptions.%s", name);
+  cout << "name = " << name << ", value = " << value << endl;
+  cout << "set tcl-variable " << buf << " to " << value << endl;
+  Tcl_SetVar (tcl_interp, buf, const_cast<char*> (value), 0);
+  Tcl_Eval (tcl_interp, "Ng_Vis_Set parameters;");
+#endif
+}
+
+
+
+
+int firsttime = 1;
+int animcnt = 0;
+void PlayAnimFile(const char* name, int speed, int maxcnt)
+{
+  //extern Mesh * mesh;
+
+  /*
+  if (mesh.Ptr()) mesh->DeleteMesh();
+  if (!mesh.Ptr()) mesh = new Mesh();
+  */
+  mesh.Reset (new Mesh());
+
+  int ne, np, i, ti;
+
+  char str[80];
+  char str2[80];
+
+  //int tend = 5000;
+  //  for (ti = 1; ti <= tend; ti++)
+  //{
+  int rti = (animcnt%(maxcnt-1)) + 1;
+  animcnt+=speed;
+  
+  sprintf(str2,"%05i.sol",rti);
+  strcpy(str,"mbssol/");
+  strcat(str,name);
+  strcat(str,str2);
+
+  cout << "read file '" << str << "'" << endl;
+  
+  ifstream infile(str);
+  infile >> ne;
+  for (i = 1; i <= ne; i++)
+    {
+      int j;
+      Element2d tri(TRIG);
+      tri.SetIndex(1); //faceind
+      
+      for (j = 1; j <= 3; j++)
+	infile >> tri.PNum(j);
+
+      infile >> np;
+      for (i = 1; i <= np; i++)
+	{
+	  Point3d p;
+	  infile >> p.X() >> p.Y() >> p.Z();
+	  if (firsttime)
+	    mesh->AddPoint (p);
+	  else
+	    mesh->Point(i)=p;
+	}
+
+      //firsttime = 0;
+      Ng_Redraw();
+   }
+}
+
+		
+int Ng_GetNPeriodicVertices ()
+{
+  ARRAY<INDEX_2> apairs;
+  mesh->GetIdentifications().GetPairs (0, apairs);
+  return apairs.Size();
+}
+
+
+// pairs should be an integer array of 2*npairs
+void Ng_GetPeriodicVertices (int * pairs)
+{
+  ARRAY<INDEX_2> apairs;
+  mesh->GetIdentifications().GetPairs (0, 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 ()
+{
+  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(0, map);
+      //(*testout) << "ident-map " << id << ":" << endl << map << endl;
+
+      for (SegmentIndex si = 0; si < nse; si++)
+	{
+	  PointIndex other1 = map[(*mesh)[si].p1];
+	  PointIndex other2 = map[(*mesh)[si].p2];
+	  //  (*testout) << "seg = " << (*mesh)[si] << "; other = " 
+	  //     << other1 << "-" << other2 << endl;
+	  if (other1 && other2 && mesh->IsSegment (other1, other2))
+	    {
+	      cnt++;
+	    }
+	}
+    }
+  return cnt;
+}
+
+void Ng_GetPeriodicEdges (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(0, map);
+      
+      //(*testout) << "map = " << map << endl;
+
+      for (SegmentIndex si = 0; si < nse; si++)
+	{
+	  PointIndex other1 = map[(*mesh)[si].p1];
+	  PointIndex other2 = map[(*mesh)[si].p2];
+	  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);
+}
+
+
+///// 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();
+}
+
+
+
diff --git a/contrib/Netgen/libsrc/interface/nginterface.h b/contrib/Netgen/libsrc/interface/nginterface.h
new file mode 100644
index 0000000000..dcb8ddd332
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/nginterface.h
@@ -0,0 +1,245 @@
+#ifndef NGINTERFACE
+#define NGINTERFACE
+
+/**************************************************************************/
+/* File:   nginterface.hh                                                 */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   20. Nov. 99                                                    */
+/**************************************************************************/
+
+/*
+  
+  Application program interface to Netgen
+  
+
+ */
+
+
+// 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 
+  void Ng_LoadGeometry (char * filename);
+  
+  // load netgen mesh
+  void Ng_LoadMesh (char * filename);
+
+
+  // space dimension (2 or 3)
+  int Ng_GetDimension ();
+
+  // number of mesh points
+  int Ng_GetNP ();
+  
+  // number of mesh vertices (differs from GetNP for 2nd order elements)
+  int Ng_GetNV ();
+  
+  // number of mesh elements
+  int Ng_GetNE ();
+  
+  // number of surface triangles
+  int Ng_GetNSE ();
+  
+  // Get Point coordintes, index from 1 .. np
+  void Ng_GetPoint (int pi, double * p);
+  
+  // Get Element Points
+  NG_ELEMENT_TYPE Ng_GetElement (int ei, int * epi, int * np = 0);
+
+  // Get Element Type
+  NG_ELEMENT_TYPE Ng_GetElementType (int ei);
+
+  // Get sub-domain of element ei
+  int Ng_GetElementIndex (int ei);
+
+  // Get Material of element ei
+  char * Ng_GetElementMaterial (int ei);
+
+  // Get Surface Element Points
+  NG_ELEMENT_TYPE Ng_GetSurfaceElement (int ei, int * epi, int * np = 0);
+
+  // Get Surface Element Index
+  int Ng_GetSurfaceElementIndex (int ei);
+  
+  // Get normal vector of surface element node
+  void Ng_GetNormalVector (int sei, int locpi, double * nv);     
+  
+
+  // Find element of point, returns local coordinates
+  int Ng_FindElementOfPoint (double * p, double * lami,
+			     int build_searchtrees = 0, int index = -1);
+  
+
+  /// Curved Elemens:
+  /// xi..local coordinates
+  /// x ..global coordinates
+  /// dxdxi...D x D Jacobian matrix (row major storage)
+  void Ng_GetElementTransformation (int ei, const double * xi, 
+				    double * x, double * dxdxi);
+
+  /// Curved Elemens:
+  /// xi..local coordinates
+  /// x ..global coordinates
+  /// dxdxi...D x D-1 Jacobian matrix (row major storage)
+  void Ng_GetSurfaceElementTransformation (int sei, const double * xi, double * x, double * dxdxi);
+
+  
+  // Mark element for refinement
+  void Ng_SetRefinementFlag (int ei, int flag);
+  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 };
+  void Ng_Refine (NG_REFINEMENT_TYPE reftype);
+
+  // Use second order elements
+  void Ng_SecondOrder ();
+  void Ng_HighOrder (int order);
+  void Ng_HPRefinement (int levels);
+
+
+  // Topology and coordinate information of master element:
+
+  int Ng_ME_GetNVertices (NG_ELEMENT_TYPE et);
+  int Ng_ME_GetNEdges (NG_ELEMENT_TYPE et);
+  int Ng_ME_GetNFaces (NG_ELEMENT_TYPE et);
+
+  const NG_POINT * Ng_ME_GetVertices (NG_ELEMENT_TYPE et);
+  const NG_EDGE * Ng_ME_GetEdges (NG_ELEMENT_TYPE et);
+  const NG_FACE * Ng_ME_GetFaces (NG_ELEMENT_TYPE et);
+
+  int Ng_GetNEdges();
+  int Ng_GetNFaces();
+
+  
+  int Ng_GetElement_Edges (int elnr, int * edges, int * orient = 0);
+  int Ng_GetElement_Faces (int elnr, int * faces, int * orient = 0);
+
+  int Ng_GetSurfaceElement_Edges (int selnr, int * edges, int * orient = 0);
+  int Ng_GetSurfaceElement_Face (int selnr, int * orient = 0);
+
+  void Ng_GetSurfaceElementNeighbouringDomains(const int selnr, int & in, int & out);
+       
+  int Ng_GetFace_Vertices (int fnr, int * vert);
+  void Ng_GetEdge_Vertices (int ednr, int * vert);
+  int Ng_GetFace_Edges (int fnr, int * edge);
+
+  int Ng_GetNVertexElements (int vnr);
+  void Ng_GetVertexElements (int vnr, int * els);
+
+  int Ng_GetElementOrder (int enr);
+
+  // Multilevel functions:
+
+  // number of levels:
+  int Ng_GetNLevels ();
+  // get two parent nodes of node ni
+  void Ng_GetParentNodes (int ni, int * parents);
+
+  // get parent element (first child has always same number)
+  int Ng_GetParentElement (int ei);
+
+  // get parent surface element (first child has always same number)
+  int Ng_GetParentSElement (int ei);
+
+  // representant of anisotropic cluster
+  int Ng_GetClusterRepVertex (int vi);
+  int Ng_GetClusterRepEdge (int edi);
+  int Ng_GetClusterRepFace (int fai);
+  int Ng_GetClusterRepElement (int eli);
+
+
+  void Ng_SurfaceElementTransformation (int eli, double x, double y, 
+					double * p3d, double * jacobian);
+  
+namespace netgen {
+#include "../visualization/soldata.hpp"
+}
+
+  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
+  {
+    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
+  void Ng_InitSolutionData (Ng_SolutionData * soldata);
+  // set solution data
+  void Ng_SetSolutionData (Ng_SolutionData * soldata);
+  /// delete gridfunctions
+  void Ng_ClearSolutionData();
+  // redraw 
+  void Ng_Redraw();
+  //
+  void Ng_SetVisualizationParameter (const char * name, 
+				     const char * value);
+
+
+  // number of periodic vertices  
+  int Ng_GetNPeriodicVertices ();
+  // pairs should be an integer array of 2*npairs
+  void Ng_GetPeriodicVertices (int * pairs); 
+
+  // number of periodic edges  
+  int Ng_GetNPeriodicEdges ();
+  // pairs should be an integer array of 2*npairs
+  void Ng_GetPeriodicEdges (int * pairs); 
+
+
+  void Ng_PushStatus (const char * str);
+  void Ng_PopStatus ();
+  void Ng_SetThreadPercentage (double percent);
+  
+  //// added by Roman Stainko ....
+  int Ng_GetVertex_Elements( int vnr, int* elems);
+  int Ng_GetVertex_SurfaceElements( int vnr, int* elems );
+  int Ng_GetVertex_NElements( int vnr );
+  int Ng_GetVertex_NSurfaceElements( int vnr );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/contrib/Netgen/libsrc/interface/nglib.cpp b/contrib/Netgen/libsrc/interface/nglib.cpp
new file mode 100644
index 0000000000..d2e59f931e
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/nglib.cpp
@@ -0,0 +1,573 @@
+/**************************************************************************/
+/* File:   nglib.cc                                                       */
+/* 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>
+
+
+
+// #include <FlexLexer.h>
+
+namespace netgen {
+  extern void MeshFromSpline2D (SplineGeometry2d & geometry,
+				Mesh *& mesh, 
+				MeshingParameters & mp);
+}
+
+
+
+
+
+
+
+namespace nglib {
+#include "nglib.h"
+}
+
+using namespace netgen;
+
+// constants and types:
+
+namespace nglib
+{
+// initialize, deconstruct Netgen library:
+void Ng_Init ()
+{
+  mycout = &cout;
+  myerr = &cerr;
+  testout = new ofstream ("test.out");
+}
+
+void Ng_Exit ()
+{
+  ;
+}
+  
+
+
+Ng_Mesh * Ng_NewMesh ()
+{
+  Mesh * mesh = new Mesh;  
+  mesh->AddFaceDescriptor (FaceDescriptor (1, 1, 0, 1));
+  return (Ng_Mesh*)(void*)mesh;
+}
+
+void Ng_DeleteMesh (Ng_Mesh * mesh)
+{
+  delete (Mesh*)mesh;
+}
+
+
+// feeds points, surface elements and volume elements to the mesh
+void Ng_AddPoint (Ng_Mesh * mesh, double * x)
+{
+  Mesh * m = (Mesh*)mesh;
+  m->AddPoint (Point3d (x[0], x[1], x[2]));
+}
+  
+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);
+}
+
+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);
+}
+
+// ask for number of points, surface and volume elements
+int Ng_GetNP (Ng_Mesh * mesh)
+{
+  return ((Mesh*)mesh) -> GetNP();
+}
+
+int Ng_GetNSE (Ng_Mesh * mesh)
+{
+  return ((Mesh*)mesh) -> GetNSE();
+}
+
+int Ng_GetNE (Ng_Mesh * mesh)
+{
+  return ((Mesh*)mesh) -> GetNE();
+}
+
+
+//  return point coordinates
+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 surface and volume element in pi
+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: et = NG_TRIG6; break;
+    }
+  return et;
+}
+
+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;
+    }
+  return et;
+}
+
+
+
+// generates volume mesh from surface mesh
+Ng_Result Ng_GenerateVolumeMesh (Ng_Mesh * mesh, Ng_Meshing_Parameters * mp)
+{
+  Mesh * m = (Mesh*)mesh;
+  
+  
+  MeshingParameters mparam;
+  mparam.maxh = mp->maxh;
+  mparam.meshsizefilename = mp->meshsize_filename;
+
+  m->CalcLocalH();
+
+  MeshVolume (mparam, *m);
+  RemoveIllegalElements (*m);
+  OptimizeVolume (mparam, *m);
+
+  return NG_OK;
+}
+
+
+
+// 2D Meshing Functions:
+
+
+
+void Ng_AddPoint_2D (Ng_Mesh * mesh, double * x)
+{
+  Mesh * m = (Mesh*)mesh;
+  
+  m->AddPoint (Point3d (x[0], x[1], 0));
+}
+
+void Ng_AddBoundarySeg_2D (Ng_Mesh * mesh, int pi1, int pi2)
+{
+  Mesh * m = (Mesh*)mesh;
+
+  Segment seg;
+  seg.p1 = pi1;
+  seg.p2 = pi2;
+  m->AddSegment (seg);
+}
+  
+
+int Ng_GetNP_2D (Ng_Mesh * mesh)
+{
+  Mesh * m = (Mesh*)mesh;
+  return m->GetNP();
+}
+
+int Ng_GetNE_2D (Ng_Mesh * mesh)
+{
+  Mesh * m = (Mesh*)mesh;
+  return m->GetNSE();
+}
+
+int Ng_GetNSeg_2D (Ng_Mesh * mesh)
+{
+  Mesh * m = (Mesh*)mesh;
+  return m->GetNSeg();
+}
+  
+void Ng_GetPoint_2D (Ng_Mesh * mesh, int num, double * x)
+{
+  Mesh * m = (Mesh*)mesh;
+
+  Point3d & p = m->Point(num);
+  x[0] = p.X();
+  x[1] = p.Y();
+}
+
+void Ng_GetElement_2D (Ng_Mesh * mesh, int num, int * pi, int * matnum)
+{
+  const Element2d & el = ((Mesh*)mesh)->SurfaceElement(num);
+  for (int i = 1; i <= 3; i++)
+    pi[i-1] = el.PNum(i);
+  if (matnum)
+    *matnum = el.GetIndex();
+}
+
+
+void Ng_GetSegment_2D (Ng_Mesh * mesh, int num, int * pi, int * matnum)
+{
+  const Segment & seg = ((Mesh*)mesh)->LineSegment(num);
+  pi[0] = seg.p1;
+  pi[1] = seg.p2;
+
+  if (matnum)
+    *matnum = seg.edgenr;
+}
+
+
+
+
+Ng_Geometry_2D * Ng_LoadGeometry_2D (const char * filename)
+{
+  SplineGeometry2d * geom = new SplineGeometry2d();
+  geom -> Load (filename);
+  return (Ng_Geometry_2D *)geom;
+}
+
+Ng_Result Ng_GenerateMesh_2D (Ng_Geometry_2D * geom,
+			      Ng_Mesh ** mesh,
+			      Ng_Meshing_Parameters * mp)
+{
+  // use global variable mparam
+  //  MeshingParameters mparam;  
+  mparam.maxh = mp->maxh;
+  mparam.meshsizefilename = mp->meshsize_filename;
+  mparam.quad = mp->quad_dominated;
+
+  Mesh * m;
+  MeshFromSpline2D (*(SplineGeometry2d*)geom, m, mparam);
+  
+  cout << m->GetNSE() << " elements, " << m->GetNP() << " points" << endl;
+  
+  *mesh = (Ng_Mesh*)m;
+  return NG_OK;
+}
+
+
+
+void Ng_HP_Refinement (Ng_Geometry_2D * geom,
+		       Ng_Mesh * mesh,
+		       int levels)
+{
+  Refinement2d ref(*(SplineGeometry2d*)geom);
+  HPRefinement (*(Mesh*)mesh, &ref, levels);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ARRAY<STLReadTriangle> readtrias; //only before initstlgeometry
+ARRAY<Point<3> > readedges; //only before init stlgeometry
+ 
+void Ng_SaveMesh(Ng_Mesh * mesh, const char* filename)
+{
+  ((Mesh*)mesh)->Save(filename);
+}
+
+Ng_Mesh * Ng_LoadMesh(const char* filename)
+{
+  Mesh * mesh = new Mesh;
+  mesh->Load(filename);
+  return ( (Ng_Mesh*)mesh );
+}
+
+// loads geometry from STL file
+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
+Ng_STL_Geometry * Ng_STL_NewGeometry ()
+{
+  return (Ng_STL_Geometry*)(void*)new STLGeometry;
+} 
+
+// after adding triangles (and edges) initialize
+Ng_Result Ng_STL_InitSTLGeometry (Ng_STL_Geometry * geom)
+{
+  STLGeometry* geo = (STLGeometry*)geom;
+  geo->InitSTLGeometry(readtrias);
+  readtrias.SetSize(0);
+
+  if (readedges.Size() != 0)
+    {
+      int i;
+      /*
+      for (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:
+Ng_Result Ng_STL_MakeEdges (Ng_STL_Geometry * geom,
+		       Ng_Mesh* mesh,
+		       Ng_Meshing_Parameters * mp)
+{
+  STLGeometry* stlgeometry = (STLGeometry*)geom;
+  Mesh* me = (Mesh*)mesh;
+  
+  MeshingParameters mparam;
+
+  mparam.maxh = mp->maxh;
+  mparam.meshsizefilename = mp->meshsize_filename;
+
+  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.
+Ng_Result Ng_STL_GenerateSurfaceMesh (Ng_STL_Geometry * geom,
+				      Ng_Mesh* mesh,
+				      Ng_Meshing_Parameters * mp)
+{
+  STLGeometry* stlgeometry = (STLGeometry*)geom;
+  Mesh* me = (Mesh*)mesh;
+
+  MeshingParameters mparam;
+
+  mparam.maxh = mp->maxh;
+  mparam.meshsizefilename = mp->meshsize_filename;
+
+  /*
+  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
+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:
+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]));
+}
+
+
+
+Ng_Meshing_Parameters :: Ng_Meshing_Parameters()
+{
+  maxh = 1000;
+  fineness = 0.5;
+  secondorder = 0;
+  meshsize_filename = 0;
+  quad_dominated = 0;
+}
+
+
+}
+
+
+// compatibility functions:
+
+namespace netgen 
+{
+
+  char geomfilename[255];
+
+void MyError (const char * ch)
+{
+  cerr << ch;
+}
+
+//Destination for messages, errors, ...
+void Ng_PrintDest(const char * s)
+{
+  (*mycout) << s << flush;
+}
+
+double GetTime ()
+{
+  return 0;
+}
+
+void ResetTime ()
+{
+  ;
+}
+
+void MyBeep (int i)
+{
+  ;
+}
+
+}
diff --git a/contrib/Netgen/libsrc/interface/nglib.h b/contrib/Netgen/libsrc/interface/nglib.h
new file mode 100644
index 0000000000..20d745d470
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/nglib.h
@@ -0,0 +1,208 @@
+#ifndef NGLIB
+#define NGLIB
+
+/**************************************************************************/
+/* File:   nglib.hh                                                       */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   7. May. 2000                                                   */
+/**************************************************************************/
+
+/*
+  
+  Interface to the netgen meshing kernel
+  
+*/
+
+/// Data type for NETGEN mesh
+typedef void * Ng_Mesh;
+
+/// Data type for NETGEN CSG geomty
+typedef void * Ng_CSG_Geometry;
+
+/// Data type for NETGEN 2D geomty
+typedef void * Ng_Geometry_2D;
+
+/// Data type for NETGEN STL geomty
+typedef void * Ng_STL_Geometry;
+
+
+
+// max number of nodes per element
+#define NG_VOLUME_ELEMENT_MAXPOINTS 10
+
+// implemented element types:
+enum Ng_Volume_Element_Type { NG_TET = 1, NG_PYRAMID = 2, NG_PRISM = 3,
+				 NG_TET10 = 4 };
+
+// max number of nodes per surface element
+#define NG_SURFACE_ELEMENT_MAXPOINTS 6
+
+// implemented element types:
+enum Ng_Surface_Element_Type { NG_TRIG = 1, NG_QUAD = 2, 
+			       NG_TRIG6 = 3 };
+
+
+
+class Ng_Meshing_Parameters 
+{
+ public:
+
+  double maxh;
+  double fineness;   // 0 .. coarse, 1 .. fine
+  int secondorder;
+  char * meshsize_filename;
+  int quad_dominated;
+
+  Ng_Meshing_Parameters();
+};
+
+
+enum Ng_Result { 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 };
+
+
+
+
+
+// #ifdef __cplusplus
+// extern "C" 
+// {
+// #endif
+  
+  // initialize, deconstruct Netgen library:
+  void Ng_Init ();
+  void Ng_Exit ();
+  
+
+  // Generates new mesh structure
+  Ng_Mesh * Ng_NewMesh ();
+  void Ng_DeleteMesh (Ng_Mesh * mesh);
+  
+  // feeds points, surface elements and volume elements to the mesh
+  void Ng_AddPoint (Ng_Mesh * mesh, double * x);
+  void Ng_AddSurfaceElement (Ng_Mesh * mesh, Ng_Surface_Element_Type et,
+			     int * pi);
+  void Ng_AddVolumeElement (Ng_Mesh * mesh, Ng_Volume_Element_Type et,
+			    int * pi);
+  
+  // ask for number of points, surface and volume elements
+  int Ng_GetNP (Ng_Mesh * mesh);
+  int Ng_GetNSE (Ng_Mesh * mesh);
+  int Ng_GetNE (Ng_Mesh * mesh);
+
+  
+  //  return point coordinates
+  void Ng_GetPoint (Ng_Mesh * mesh, int num, double * x);
+
+  // return surface and volume element in pi
+  Ng_Surface_Element_Type 
+  Ng_GetSurfaceElement (Ng_Mesh * mesh, int num, int * pi);
+
+  Ng_Volume_Element_Type
+  Ng_GetVolumeElement (Ng_Mesh * mesh, int num, int * pi);
+
+
+  // Defines MeshSize Functions
+  void Ng_RestrictMeshSizeGlobal (Ng_Mesh * mesh, double h);
+  void Ng_RestrictMeshSizePoint (Ng_Mesh * mesh, double * p, double h);
+  void Ng_RestrictMeshSizeBox (Ng_Mesh * mesh, double * pmin, double * pmax, double h);
+  
+  // generates volume mesh from surface mesh
+  Ng_Result Ng_GenerateVolumeMesh (Ng_Mesh * mesh, Ng_Meshing_Parameters * mp);
+
+  void Ng_SaveMesh(Ng_Mesh * mesh, const char* filename);
+  Ng_Mesh * Ng_LoadMesh(const char* filename);
+
+
+
+
+
+  // **********************************************************
+  // **   2D Meshing                                         **
+  // **********************************************************
+
+
+  // feeds points and boundary to mesh
+
+  void Ng_AddPoint_2D (Ng_Mesh * mesh, double * x);
+  void Ng_AddBoundarySeg_2D (Ng_Mesh * mesh, int pi1, int pi2);
+  
+  // ask for number of points, elements and boundary segments
+  int Ng_GetNP_2D (Ng_Mesh * mesh);
+  int Ng_GetNE_2D (Ng_Mesh * mesh);
+  int Ng_GetNSeg_2D (Ng_Mesh * mesh);
+  
+  //  return point coordinates
+  void Ng_GetPoint_2D (Ng_Mesh * mesh, int num, double * x);
+
+  // return 2d triangles
+  void Ng_GetElement_2D (Ng_Mesh * mesh, int num, int * pi, int * matnum = NULL);
+
+  // return 2d boundary segment
+  void Ng_GetSegment_2D (Ng_Mesh * mesh, int num, int * pi, int * matnum = NULL);
+
+
+  // load 2d netgen spline geometry
+  Ng_Geometry_2D * Ng_LoadGeometry_2D (const char * filename);
+
+  // generate 2d mesh, mesh is allocated by function
+  Ng_Result Ng_GenerateMesh_2D (Ng_Geometry_2D * geom,
+				Ng_Mesh ** mesh,
+				Ng_Meshing_Parameters * mp);
+  
+  void Ng_HP_Refinement (Ng_Geometry_2D * geom,
+			 Ng_Mesh * mesh,
+			 int levels);
+  
+
+
+
+
+  // **********************************************************
+  // **   STL Meshing                                        **
+  // **********************************************************
+
+
+  // loads geometry from STL file
+  Ng_STL_Geometry * Ng_STL_LoadGeometry (const char * filename, int binary = 0);
+
+
+  // generate new STL Geometry
+  Ng_STL_Geometry * Ng_STL_NewGeometry ();
+  
+
+  // fills STL Geometry
+  // positive orientation
+  // normal vector may be null-pointer
+  void Ng_STL_AddTriangle (Ng_STL_Geometry * geom, 
+			   double * p1, double * p2, double * p3, 
+			   double * nv = NULL);
+
+  // add (optional) edges :
+  void Ng_STL_AddEdge (Ng_STL_Geometry * geom, 
+		       double * p1, double * p2);
+
+  // after adding triangles (and edges) initialize
+  Ng_Result Ng_STL_InitSTLGeometry (Ng_STL_Geometry * geom);
+
+  // automatically generates edges:
+  Ng_Result Ng_STL_MakeEdges (Ng_STL_Geometry * geom,
+			      Ng_Mesh* mesh,
+			      Ng_Meshing_Parameters * mp);
+
+  
+  // generates mesh, empty mesh must be already created.
+  Ng_Result Ng_STL_GenerateSurfaceMesh (Ng_STL_Geometry * geom,
+					Ng_Mesh * mesh,
+					Ng_Meshing_Parameters * mp);
+
+// #ifdef __cplusplus
+//  }
+// #endif
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/interface/printdest.cpp b/contrib/Netgen/libsrc/interface/printdest.cpp
new file mode 100644
index 0000000000..0db654d94f
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/printdest.cpp
@@ -0,0 +1,11 @@
+#include <mystdlib.h>
+#include <myadt.hpp>
+
+namespace netgen
+{
+  //Destination for messages, errors, ...
+  void Ng_PrintDest(const char * s)
+  {
+    (*mycout) << s << flush;
+  }
+}
diff --git a/contrib/Netgen/libsrc/interface/readuser.cpp b/contrib/Netgen/libsrc/interface/readuser.cpp
new file mode 100644
index 0000000000..f7d4d1eae8
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/readuser.cpp
@@ -0,0 +1,394 @@
+//
+//  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();
+
+  int i, j;
+
+  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 (i = 1; i <= np; i++)
+	{
+	  Point3d p;
+	  in >> p.X() >> p.Y() >> p.Z();
+	  mesh.AddPoint (p);
+	}
+      
+      
+      in >> nbe;
+      //      int invert = globflags.GetDefineFlag ("invertsurfacemesh");
+      for (i = 1; i <= nbe; i++)
+	{
+	  Element2d el;
+	  int hi;
+	  
+	  el.SetIndex(1);
+	  
+	  for (j = 1; j <= 3; j++)
+	    {
+	      in >> 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);
+	}
+      
+      mesh.ClearFaceDescriptors();
+      mesh.AddFaceDescriptor (FaceDescriptor(0,1,0,0));
+      
+      cout << "points: " << np << " faces: " << nbe << endl;
+    }
+  
+  
+  
+
+  
+  // Universal mesh (AVL)
+  if ( (strlen (filename) > 4) &&
+       strcmp (&filename[strlen (filename)-4], ".unv") == 0 )
+    {  
+      int i, j, k;
+      
+      double h;
+      char reco[100];
+      int np, nbe;
+      int invert;
+      
+      
+      ifstream in(filename);
+
+      invert = 0;    // globflags.GetDefineFlag ("invertsurfacemesh");
+      double scale = 1;  // globflags.GetNumFlag ("scale", 1);
+
+
+      while (in.good())
+	{
+	  in >> reco;
+	  if (strcmp (reco, "NODES") == 0)
+	    {
+	      cout << "nodes found" << endl;
+	      for (j = 1; j <= 4; j++)
+		in >> reco;  // read dummy
+
+	      while (1)
+		{
+		  int pi, hi;
+		  double x, y, z;
+		  Point3d p;
+
+		  in >> pi;
+		  if (pi == -1)
+		    break;
+
+		  in >> hi >> hi >> hi;
+		  in >> p.X() >> p.Y() >> p.Z();
+
+		  p.X() *= scale;
+		  p.Y() *= scale;
+		  p.Z() *= scale;
+
+
+		  mesh.AddPoint (p);
+		}
+	    }
+
+	  if (strcmp (reco, "ELEMENTS") == 0)
+	    {
+	      cout << "elements found" << endl;
+	      for (j = 1; j <= 4; j++)
+		in >> reco;  // read dummy
+
+	      while (1)
+		{
+		  int hi;
+		  in >> hi;
+		  if (hi == -1) break;
+		  for (j = 1; j <= 7; j++)
+		    in >> hi;
+	      
+		  Element2d el;
+		  el.SetIndex(1);
+		  in >> el.PNum(1) >> el.PNum(2) >> el.PNum(3);
+	      
+		  if (invert)
+		    swap (el.PNum(2), el.PNum(3));
+		  mesh.AddSurfaceElement (el);	  
+	      
+		  for (j = 1; j <= 5; j++)
+		    in >> hi;
+		}
+	    }
+	}
+      
+      mesh.ClearFaceDescriptors();
+      mesh.AddFaceDescriptor (FaceDescriptor(0,1,0,0));
+
+      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);
+	    }
+	  
+	  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 num, 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).X() < 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);
+	}
+    }
+
+}
+
+}
diff --git a/contrib/Netgen/libsrc/interface/writeabaqus.cpp b/contrib/Netgen/libsrc/interface/writeabaqus.cpp
new file mode 100644
index 0000000000..e9308b1472
--- /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).X() << ", ";
+      outfile << mesh.Point(i).Y() << ", ";
+      outfile << mesh.Point(i).Z() << "\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);
+      int 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;
+
+      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 0000000000..25f9b2986f
--- /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.PointType(i) != 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.PointType(i) != 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/writeelmer.cpp b/contrib/Netgen/libsrc/interface/writeelmer.cpp
new file mode 100644
index 0000000000..caf02e2c68
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/writeelmer.cpp
@@ -0,0 +1,127 @@
+
+//
+//  Write Elmer file
+//
+//
+
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <linalg.hpp>
+#include <csg.hpp>
+#include <meshing.hpp>
+
+namespace netgen
+{
+#include "writeuser.hpp"
+
+#include <sys/stat.h>
+
+
+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, rc;
+  char str[200];
+  
+  int inverttets = mparam.inverttets;
+  int invertsurf = mparam.inverttrigs;
+
+#ifdef WIN32
+  cerr << "not yet implemented for Windows platforms." << endl;
+  return;
+#else
+  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 << "1"     << "\n";
+  outfile_h << "504 1" << "\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 0000000000..817ee7c538
--- /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, zz, n;
+
+  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).X()/scale << "  ";
+      outfile.width(10);
+      outfile << mesh.Point(i).Y()/scale << "  ";
+      outfile.width(10);
+      outfile << mesh.Point(i).Z()/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 0000000000..5c08d59857
--- /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 0000000000..93def67783
--- /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/writepermas.cpp b/contrib/Netgen/libsrc/interface/writepermas.cpp
new file mode 100644
index 0000000000..9dc7662458
--- /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;
+                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).X() << " ";
+            outfile << mesh.Point(i).Y() << " ";
+            outfile << mesh.Point(i).Z() << "\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/writepermas2.cpp b/contrib/Netgen/libsrc/interface/writepermas2.cpp
new file mode 100644
index 0000000000..f78714c5a4
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/writepermas2.cpp
@@ -0,0 +1,173 @@
+//
+// Write Permas file
+// for Intes GmbH, Stuttgart
+//
+
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <linalg.hpp>
+#include <csg.hpp>
+#include <meshing.hpp>
+
+namespace netgen
+{
+#include "writeuser.hpp"
+
+
+
+void WritePermasFormat (const Mesh & mesh,
+			const string & filename)
+
+{
+  
+  ofstream outfile (filename.c_str());
+
+  outfile.precision(8);
+      
+  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;
+	  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).X() << " ";
+      outfile << mesh.Point(i).Y() << " ";
+      outfile << mesh.Point(i).Z() << "\n";
+    }
+}
+
+
+}
diff --git a/contrib/Netgen/libsrc/interface/writetecplot.cpp b/contrib/Netgen/libsrc/interface/writetecplot.cpp
new file mode 100644
index 0000000000..a3ddda3f9a
--- /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 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);
+	}
+    }
+      
+      
+  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).X() << " " /* Knoten Koordinaten */
+		    << mesh.Point(i).Y() << " "
+		    << mesh.Point(i).Z() << " "
+		    << 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 (i = 1; i <= nse; i++)
+		{
+		  const Element2d & el = mesh.SurfaceElement(i);
+		  INDEX_3 i3;
+		  for (j = 1; j <= 3; j++)
+		    i3.I(j) = el.PNum(j);
+		  i3.Sort();
+		  
+		  int elind = face2volelement.Get(i3);
+		}
+	}
+    }
+}
+
+
+}
diff --git a/contrib/Netgen/libsrc/interface/writetochnog.cpp b/contrib/Netgen/libsrc/interface/writetochnog.cpp
new file mode 100644
index 0000000000..50546dc2d1
--- /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, k;
+
+  for (i = 1; i <= np; i++)
+    {
+      outfile << "node " << " " << i << " ";
+      outfile << mesh.Point(i).X() << " ";
+      outfile << mesh.Point(i).Y() << " ";
+      outfile << mesh.Point(i).Z() << "\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 0000000000..83b9a050ce
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/writeuser.cpp
@@ -0,0 +1,899 @@
+//
+//  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)
+{
+  char *types[] =
+    {
+      "Neutral Format",
+      "Surface Mesh Format" ,
+      "DIFFPACK Format",
+      "TecPlot Format",     
+      "Tochnog Format",
+      "Abaqus Format",
+      "Fluent Format",
+      "Permas Format",
+      "FEAP Format",
+      "Elmer Format",
+      "STL Format",
+      "VRML Format",
+      "Gmsh Format",
+      //      { "Chemnitz Format" },
+      0
+    };
+
+  for (int i = 0; types[i]; i++)
+    names.Append (types[i]);
+}
+
+
+
+bool WriteUserFormat (const string & format, 	
+		      const Mesh & mesh,
+		      const CSGeometry & geom,
+		      const string & filename)
+{
+  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);
+
+  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);
+ 
+  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.p1;
+	  outfile << " ";
+	  outfile.width(8);
+	  outfile << seg.p2;
+
+	  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 = 1; j <= 3; j++)
+	{
+	  outfile.width(10);
+	  outfile << mesh.Point(i).X(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, j, k;
+  
+  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;
+}
+
+
+
+
+
+/*
+ *
+ *  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, k, l;
+
+      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, k, l;
+
+      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 0000000000..afb2a6d5e3
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/writeuser.hpp
@@ -0,0 +1,114 @@
+#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 ImportSolution (const char * 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);
+
+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);
+
+extern
+void WriteUserChemnitz (const Mesh & mesh,
+			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);
+
+
+
+extern void RegisterUserFormats (ARRAY<const char*> & names);
+extern bool WriteUserFormat (const string & format, 	
+			     const Mesh & mesh,
+			     const CSGeometry & 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 0000000000..82a513d1ca
--- /dev/null
+++ b/contrib/Netgen/libsrc/interface/wuchemnitz.cpp
@@ -0,0 +1,309 @@
+// 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:
+  VOLELEMENT () {};
+  int domnr, p1, p2, p3, p4;
+  int faces[4];
+  };
+  
+class SURFELEMENT
+  {
+  public:
+  SURFELEMENT () { };
+  int snr, p1, p2, p3;
+  };
+  
+
+class FACE
+  {
+  public:
+  FACE () { };
+  int p1, p2, p3;
+  int edges[3];
+  };
+
+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;
+        }
+      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;
+        }
+      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 b/contrib/Netgen/libsrc/linalg/Makefile
new file mode 100644
index 0000000000..0cb19b0932
--- /dev/null
+++ b/contrib/Netgen/libsrc/linalg/Makefile
@@ -0,0 +1,13 @@
+#
+# Makefile for linear algebra library
+#
+src = basemat.cpp densemat.cpp vector.cpp sparsmat.cpp polynomial.cpp
+#
+lib = la
+libpath = libsrc/linalg
+#
+#
+include ../makefile.inc
+#
+
+
diff --git a/contrib/Netgen/libsrc/linalg/basemat.cpp b/contrib/Netgen/libsrc/linalg/basemat.cpp
new file mode 100644
index 0000000000..889d79d668
--- /dev/null
+++ b/contrib/Netgen/libsrc/linalg/basemat.cpp
@@ -0,0 +1,472 @@
+#ifdef ABC
+
+#include <mystdlib.h>
+#include <linalg.hpp>
+
+
+// ofstream (*myerr) ("error.out");
+// ofstream (*myerr) ("NUL");
+
+
+namespace netgen
+{
+
+double BaseMatrix :: shit = 0;
+
+
+BaseMatrix :: BaseMatrix ()
+  {
+  height = width = 0;
+  symmetric = 0;
+  }
+
+BaseMatrix :: BaseMatrix (INDEX h, INDEX w)
+  {
+  if (!w) w = h;
+  height = h;
+  width = w;
+  symmetric = 0;
+  }
+
+void BaseMatrix :: SetSize (INDEX h, INDEX w)
+  {
+  if (!w) w = h;
+  height = h;
+  width = w;
+  }
+
+void BaseMatrix :: SetSymmetric (int sym)
+  {
+  symmetric = sym;
+  }
+
+double & BaseMatrix :: operator() (INDEX, INDEX)
+  {
+  (*myerr) << "BaseMatrix: operator() called" << endl;
+  return shit;
+  }
+
+double BaseMatrix :: operator() (INDEX, INDEX) const
+  {
+  (*myerr) << "BaseMatrix: operator() called" << endl;
+  return 0;
+  }
+
+
+
+ostream & operator<<(ostream & s, const BaseMatrix & m)
+  {
+  return m.Print (s);
+  }
+
+ostream & BaseMatrix :: Print (ostream & s) const
+  {
+  if (Symmetric()) s << "Symmetric" << endl;
+  for (INDEX i = 1; i <= Height(); i++)
+    {
+    for (INDEX j = 1; j < Width(); j++)
+      s << (*this)(i, j) << "  ";
+    s << (*this)(i, Width()) << endl;
+    }
+
+  return s;
+  }
+
+
+  /*
+TempVector BaseMatrix :: operator* (const Vector & v) const
+  {
+  Vector * prod = new Vector(Height());
+
+  if (Width() != v.Length())
+    {
+    (*myerr) << "\nMatrix and Vector don't fit 1" << endl;
+    }
+  else if (Height() != prod->Length())
+    {
+    (*myerr) << "Base_Matrix::operator*(Vector): prod vector not ok" << endl;
+    }
+  else
+    {
+    Mult (v, *prod);
+    }
+
+  return *prod;
+  }
+  */
+
+
+DenseMatrix operator* (const BaseMatrix & m1, const BaseMatrix & m2)
+  {
+  DenseMatrix temp (m1.Height(), m2.Width());
+  double sum;
+
+  if (m1.Width() != 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 (INDEX i = 1; i <= m1.Height(); i++)
+      for (INDEX j = 1; j <= m2.Width(); j++)
+        {
+        sum = 0;
+        for (INDEX k = 1; k <= m1.Width(); k++)
+          sum += m1(i, k) * m2(k, j);
+        temp(i, j) = sum;
+        }
+    }
+  return temp;
+  }
+
+
+DenseMatrix operator+ (const BaseMatrix & m1, const BaseMatrix & m2)
+  {
+  DenseMatrix temp (m1.Height(), m1.Width());
+  INDEX 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(i, j) = m1(i, j) + m2(i, j);
+        }
+    }
+  return temp;
+  }
+
+
+void BaseMatrix :: Mult (const FlatVector & /* v */,
+			 FlatVector & /* prod */) const
+  {
+    (*myerr) << "BaseMatrix :: Mult called" << endl;
+    double * x = 0;
+    *x = 1;
+    assert (1);
+  }
+
+void BaseMatrix :: MultTrans (const Vector &  v,
+      Vector & prod) const
+  {
+  if (Symmetric())
+    Mult (v, prod);
+  else
+    (*myerr) << "BaseMatrix :: MultTrans called for non symmetric matrix" << endl;
+  }
+
+void BaseMatrix :: Residuum (const Vector &  x,
+      const Vector & b, Vector & res) const
+  {
+  Mult (x, res);
+  res *= -1;
+  res.Add (1, b);
+  }
+
+void BaseMatrix :: ResiduumTrans (const Vector & x,
+      const Vector & b, Vector & res) const
+  {
+  MultTrans (x, res);
+  res *= -1;
+  res.Add (1, b);
+  }
+
+BaseMatrix * BaseMatrix :: Copy () const
+  {
+  (*myerr) << "BaseMatrix :: Copy called" << endl;
+  return NULL;
+  }
+
+
+Vector * BaseMatrix :: CreateVector () const
+  {
+  return new Vector (Height());
+  }
+
+
+
+/*
+void BaseMatrix :: Mult (const Vector & v, Vector & prod) const
+  {
+  double sum;
+
+  prod.SetLength (Height());
+
+  if (Width() != v.Length())
+    {
+    (*myerr) << "\nMatrix and Vector don't fit 2" << endl;
+    }
+  else if (Height() != prod.Length())
+    {
+    (*myerr) << "Base_Matrix::operator*(Vector): prod vector not ok" << endl;
+    }
+  else
+    {
+    for (INDEX i = 1; i <= Height(); i++)
+      {
+      sum = 0;
+
+      for (INDEX j = 1; j <= Width(); j++)
+        sum += (*this)(i,j) * v.Get(j);
+
+      prod.Set (i, sum);
+      }
+    }
+  }
+
+
+void BaseMatrix :: MultTrans (const Vector & v, Vector & prod) const
+  {
+  double sum;
+
+  prod.SetLength (Width());
+
+  if (Height() != v.Length())
+    {
+    (*myerr) << "\nMatrix and Vector don't fit 3" << endl;
+    }
+  else if (Width() != prod.Length())
+    {
+    (*myerr) << "Base_Matrix::operator*(Vector): prod vector not ok" << endl;
+    }
+  else
+    {
+    for (INDEX i = 1; i <= Width(); i++)
+      {
+      sum = 0;
+
+      for (INDEX j = 1; j <= Height(); j++)
+        sum += (*this)(j, i) * v.Get(j);
+
+      prod.Set (i, sum);
+      }
+    }
+  }
+
+
+void BaseMatrix :: Residuum (const Vector & x, const Vector & b, Vector & res) const
+  {
+  double sum;
+
+  res.SetLength (Height());
+
+  if (Width() != x.Length() || Height() != b.Length())
+    {
+    (*myerr) << "\nMatrix and Vector don't fit 4" << endl;
+    }
+  else if (Height() != res.Length())
+    {
+    (*myerr) << "Base_Matrix::operator*(Vector): prod vector not ok" << endl;
+    }
+  else
+    {
+    for (INDEX i = 1; i <= Height(); i++)
+      {
+      sum = b.Get(i);
+
+      for (INDEX j = 1; j <= Width(); j++)
+        sum -= (*this)(i,j) * x.Get(j);
+
+      res.Set (i, sum);
+      }
+    }
+  }
+*/
+
+
+
+
+
+
+
+void BaseMatrix :: SolveDestroy (const Vector & v, Vector & sol)
+  {
+  INDEX i, j, k;
+  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;
+    }
+
+  for (i = 1; i <= Height(); i++)
+    {
+    for (j = i+1; j <= Height(); j++)
+      {
+      q=(*this)(j,i) / (*this)(i,i);
+      for (k = i+1; k <= Height(); k++)
+        {
+        (*this)(j, k) -= q * (*this)(i,k);
+        }
+      sol.Elem(j) -= q * sol.Get(i);
+      }
+    }
+
+  for (i = Height(); i >= 1; i--)
+    {
+    q = sol.Elem(i);
+    for (j = i+1; j <= Height(); j++)
+      {
+      q -= (*this)(i,j) * sol.Get(j);
+      }
+    sol.Elem(i) = q / (*this)(i,i);
+    }
+  }
+
+void BaseMatrix :: Solve (const Vector & v, Vector & sol) const
+  {
+  BaseMatrix * temp = Copy();
+
+  if (temp->Height() != Height())
+    {
+    (*myerr) << "Solve: Matrix temp not allocated" << endl;
+    return;
+    }
+
+  temp->SolveDestroy (v, sol);
+
+  delete temp;
+  }
+
+
+  /*
+Vector BaseMatrix :: SolveDestroyFunc (const Vector & b) const
+{
+  return Vector(0);
+}
+*/
+
+
+Vector BaseMatrix :: Solve (const Vector & v) const
+  {
+  Vector sol (v.Size());
+
+  if (Width() != Height())
+    {
+    (*myerr) << "Solve: Matrix not square";
+    return v;
+    }
+  if (Width() != v.Size())
+    {
+    (*myerr) << "Solve: Matrix and Vector don't fit";
+    return v;
+    }
+  if (Width() != sol.Size())
+    {
+    (*myerr) << "Solve: Vector sol not allocated" << endl;
+    }
+
+  Solve (v, sol);
+
+  return sol;
+  }
+
+
+
+
+
+
+
+void BaseMatrix :: LU_Decomposition (DenseMatrix & l, DenseMatrix & u) const
+  {
+  INDEX i, j ,k;
+  double sum;
+  l.SetSize (Width());
+  u.SetSize (Width());
+
+  for (i = 1; i <= Width(); i++)
+    for (j = 1; j <= Width(); j++)
+      l(i, j) = u(i, j) = 0;
+
+  for (i = 1; i <= Width(); i++)
+    {
+    for (k = 1; k < i; k++)
+      {
+      sum = (*this)(i, k);
+      for (j = 1; j < k; j++)
+        sum -= l(i, j) * u(j, k);
+      l(i, k) = sum / u(k, k);
+      }
+    l(i, i) = 1;
+
+    for (k = i; k <= Width(); k++)
+      {
+      sum = (*this)(i, k);
+      for (j = 1; j < i; j++)
+        sum -= l(i, j) * u(j, k);
+      u(i, k) = sum;
+      }
+    }
+  }
+
+
+
+void Transpose (const BaseMatrix & m1, DenseMatrix & m2)
+  {
+  m2.SetSize (m1.Width(), m1.Height());
+  INDEX i, j;
+
+  for (i = 1; i <= m1.Height(); i++)
+    for (j = 1; j <= m1.Width(); j++)
+      m2(j, i) = m1(i, j);
+  }
+
+
+
+DenseMatrix * BaseMatrix :: MakeDenseMatrix () const
+{
+  DenseMatrix * dmat = new DenseMatrix (Height(), Width());
+  dmat -> SetSymmetric(Symmetric());
+
+  Vector x(Width()), y(Height());
+  INDEX i, j;
+
+  for (i = 1; i <= Width(); i++)
+    {
+      x = 0;
+      x.Elem(i) = 1;
+      Mult (x, y);
+      
+      for (j = 1; j <= Height(); j++)
+	dmat->Elem(j, i) = y.Get(j);
+    }
+
+  return dmat;
+}
+ 
+
+BaseMatrix * BaseMatrix :: InverseMatrix (const BitArray * /* inner */) const
+{
+  (*mycout) << "called basematrix::inversemarix" << endl;
+  return NULL;
+}
+
+
+
+}
+#endif
diff --git a/contrib/Netgen/libsrc/linalg/basemat.hpp b/contrib/Netgen/libsrc/linalg/basemat.hpp
new file mode 100644
index 0000000000..d47abe790d
--- /dev/null
+++ b/contrib/Netgen/libsrc/linalg/basemat.hpp
@@ -0,0 +1,109 @@
+#ifdef NONE
+
+#ifndef FILE_BASEMAT
+#define FILE_BASEMAT
+
+/**************************************************************************/
+/* File:   basemat.hh                                                     */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Oct. 94                                                    */
+/**************************************************************************/
+
+/* 
+   Base type for linear operator
+*/
+
+class DenseMatrix;
+
+///
+class BaseMatrix
+{
+protected:
+  ///
+  INDEX height, width;
+  ///
+  int symmetric;
+  ///
+  static double shit;
+
+public:
+  ///
+  BaseMatrix ();
+  ///
+  BaseMatrix (INDEX h, INDEX w = 0);
+  ///
+  virtual ~BaseMatrix () { };
+
+  ///
+  INDEX Width () const { return width; }
+  ///
+  INDEX Height () const { return height; }
+  ///
+  int Symmetric () const { return symmetric; }
+
+  ///
+  virtual void SetSize (INDEX h, INDEX w = 0);
+  ///
+  virtual void SetSymmetric (int sym = 1);
+
+  ///
+  virtual double & operator() (INDEX i, INDEX j);
+  ///
+  virtual double operator() (INDEX i, INDEX j) const;
+
+  ///
+  friend ostream & operator<<(ostream & s, const BaseMatrix & m);
+  ///
+  virtual ostream & Print (ostream & s) const;
+
+  ///
+  // TempVector operator* (const Vector & v) const;
+
+
+  ///
+  virtual void Mult (const FlatVector & v, FlatVector & prod) const;
+  ///
+  virtual void MultTrans (const Vector & v, Vector & prod) const;
+  ///
+  virtual void Residuum (const Vector & x, const Vector & b, Vector & res) const;
+  ///
+  virtual void ResiduumTrans (const Vector & x, const Vector & b, Vector & res) const;
+  //  virtual double EvaluateBilinearform (const Vector & x);
+
+  virtual BaseMatrix * Copy () const;
+  ///
+  virtual Vector * CreateVector () const;
+
+  ///
+  virtual void AddElementMatrix (const ARRAY<INDEX> & /* pnum */, 
+				 const BaseMatrix & /* elemmat */) { };
+  ///
+  virtual void MultElementMatrix (const ARRAY<INDEX> & /* pnum */, 
+				  const Vector & /* x */, Vector & /* y */) { };
+  ///
+  virtual void MultTransElementMatrix (const ARRAY<INDEX> & /* pnum */, 
+				       const Vector & /* x */, Vector & /* y */) { };
+
+
+  ///
+  virtual DenseMatrix * MakeDenseMatrix () const;
+  ///
+  virtual BaseMatrix * InverseMatrix (const class BitArray * inner = NULL)
+    const;
+
+  ///
+  virtual void SolveDestroy (const Vector & b, Vector & x);
+  ///
+  void Solve (const Vector & b, Vector & x) const;
+  ///
+  // virtual Vector SolveDestroyFunc (const Vector & b) const;
+  ///
+  Vector Solve (const Vector & b) const;
+  ///
+  virtual void LU_Decomposition (DenseMatrix & l, DenseMatrix & u) const;
+};
+
+
+#endif
+
+#endif
diff --git a/contrib/Netgen/libsrc/linalg/densemat.cpp b/contrib/Netgen/libsrc/linalg/densemat.cpp
new file mode 100644
index 0000000000..d15c123e0d
--- /dev/null
+++ b/contrib/Netgen/libsrc/linalg/densemat.cpp
@@ -0,0 +1,1443 @@
+#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;
+    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;
+  }
+
+
+
+
+  /*
+double & DenseMatrix :: operator() (int i, int j)
+{
+  if (i >= 1 && j >= 1 && i <= height && j <= width)
+    return Elem(i,j);
+  else (*myerr) << "DenseMatrix: index (" << i << "," << j << ") out of range (1.."
+		<< height << ",1.." << width << ")\n";
+  static double dummy = 0;
+  return dummy;
+}
+
+  double DenseMatrix :: operator() (int i, int j) const
+  {
+    if (i >= 1 && j >= 1 && i <= height && j <= width)
+      return Get(i,j);
+    else (*myerr) << "DenseMatrix: index (" << i << "," << j << ") out of range (1.."
+            << height << ",1.." << width << ")\n";
+
+    static double dummy = 0;
+    return dummy;
+  }
+  */
+
+DenseMatrix & DenseMatrix :: operator= (double v)
+  {
+  int i;
+  double * p = data;
+
+  if (data)
+    for (i = width*height; i > 0; i--, p++)
+      *p = v;
+
+  return *this;
+  }
+
+
+
+DenseMatrix & DenseMatrix :: operator*= (double v)
+  {
+  int i;
+  double * p = data;
+
+  if (data)
+    for (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 Get(1, 1);
+    case 2: return Get(1) * Get(4) - Get(2) * Get(3);
+
+    case 3: return Get(1) * Get(5) * Get(9)
+                 + Get(2) * Get(6) * Get(7)
+                 + Get(3) * Get(4) * Get(8)
+                 - Get(1) * Get(6) * Get(8)
+                 - Get(2) * Get(4) * Get(9)
+                 - Get(3) * Get(5) * Get(7);
+    default:
+      {
+      (*myerr) << "Matrix :: Det:  general size not implemented (size=" << width << ")" << endl;
+      return 0;
+      }
+    }
+  }
+
+
+void CalcInverse (const DenseMatrix & m1, DenseMatrix & m2)
+  {
+    //  int i, j, k, n;
+  double det;
+  //  DenseMatrix m1 = hm1;
+
+  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;
+      return;
+      }
+
+    det = 1e0 / det;
+    switch (m1.width)
+      {
+      case 1:
+        {
+        m2.Set(1, 1, det);
+        return;
+        }
+      case 2:
+        {
+        m2.Set(1, 1, det * m1.Get(4));
+        m2.Set(2, 2, det * m1.Get(1));  
+        m2.Set(1, 2, - det * m1.Get(2));
+        m2.Set(2, 1, - det * m1.Get(3));
+        return;
+        }
+      case 3:
+        {
+        m2.Set(1, 1,  det * (m1.Get(5) * m1.Get(9) - m1.Get(6) * m1.Get(8)));
+        m2.Set(2, 1, -det * (m1.Get(4) * m1.Get(9) - m1.Get(6) * m1.Get(7)));
+        m2.Set(3, 1,  det * (m1.Get(4) * m1.Get(8) - m1.Get(5) * m1.Get(7)));
+
+        m2.Set(1, 2, -det * (m1.Get(2) * m1.Get(9) - m1.Get(3) * m1.Get(8)));
+        m2.Set(2, 2,  det * (m1.Get(1) * m1.Get(9) - m1.Get(3) * m1.Get(7)));
+        m2.Set(3, 2, -det * (m1.Get(1) * m1.Get(8) - m1.Get(2) * m1.Get(7)));
+
+        m2.Set(1, 3,  det * (m1.Get(2) * m1.Get(6) - m1.Get(3) * m1.Get(5)));
+        m2.Set(2, 3, -det * (m1.Get(1) * m1.Get(6) - m1.Get(3) * m1.Get(4)));
+        m2.Set(3, 3,  det * (m1.Get(1) * m1.Get(5) - m1.Get(2) * m1.Get(4)));
+        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;
+	      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.Elem(p.Get(k)) = m2.Get(i, k);
+	  for (k = 1; k <= n; k++)
+	    m2.Elem(i, k) = hv.Get(k);
+	}
+
+
+
+    /*
+    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);
+      }
+    }
+  }
+
+
+
+#ifdef ABC
+BaseMatrix * DenseMatrix :: InverseMatrix (const BitArray * /* inner */) const
+{
+  if (Height() != Width())
+    {
+      (*myerr) << "BaseMatrix::InverseMatrix(): Matrix not symmetric" << endl;
+      return new DenseMatrix(1);
+    }
+  else
+    {
+      if (Symmetric())
+	{	
+	  (*mycout) << "Invmat not available" << endl;
+	  BaseMatrix * invmat = NULL;
+	  return invmat;
+	}
+
+      DenseMatrix * invmat = new DenseMatrix (Height());
+
+      CalcInverse (*this, *invmat);
+      return invmat;
+    }
+}
+#endif
+
+
+
+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.Get(1);
+
+      prod = 0;
+
+      for (i = 1; i <= h; i++)
+	{
+	  double val = *pv;
+	  ++pv;
+
+	  double * pprod = &prod.Elem(1);
+
+	  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 i, j;
+      int h = Height(); 
+      int w = Width();
+      const double * mp = &Get(1, 1);
+
+      for (i = 1; i <= h; i++)
+	{
+	  sum = b.Get(i);
+	  const double * xp = &x.Get(1);
+
+	  for (j = 1; j <= w; ++j, ++mp, ++xp)
+	    sum -= *mp * *xp;
+	  
+	  res.Elem(i) = 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.Elem(i) = 1 / sqrt(x);
+		}
+	      else
+		{
+		  Elem(j, i) = x * p.Get(i);
+		}
+	    }
+	}
+
+      for (i = 1; i <= n; i++)
+        Elem(i, i) = 1 / p.Get(i);
+
+      // A = L L^t 
+      // L stored in left-lower triangle
+
+
+      sol = v;
+
+      // Solve L sol = sol
+
+      for (i = 1; i <= n; i++)
+	{
+	  double val = sol.Get(i);
+
+	  const double * pij = &Get(i, 1);
+	  const double * solj = &sol.Get(1);
+
+	  for (j = 1; j < i; j++, ++pij, ++solj)
+	    val -= *pij * *solj;
+	  //	  for (j = 1; j < i; j++)
+	  //	    val -= Get(i, j) * sol.Get(j);
+
+	  sol.Elem(i) = val / Get(i, i);
+	}
+
+      // Solve L^t sol = sol
+
+      for (i = n; i >= 1; i--)
+	{
+	  double val = sol.Get(i) / Get(i, i);
+	  sol.Elem(i) = val;
+
+	  double * solj = &sol.Elem(1);
+	  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 i, j, k, n = Height();
+      for (i = 1; i <= n; i++)
+	{
+	  for (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 (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.Elem(j) -= q * sol.Get(i);
+		}
+	    }
+	}
+      
+      for (i = n; i >= 1; i--)
+	{
+	  q = sol.Get(i);
+	  for (j = i+1; j <= n; j++)
+	      q -= Get(i,j) * sol.Get(j);
+
+	  sol.Elem(i) = 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 0000000000..fadb128d6a
--- /dev/null
+++ b/contrib/Netgen/libsrc/linalg/densemat.hpp
@@ -0,0 +1,260 @@
+#ifndef FILE_DENSEMAT
+#define FILE_DENSEMAT
+
+/**************************************************************************/
+/* File:   densemat.hh                                                    */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Oct. 94                                                    */
+/**************************************************************************/
+
+/** 
+    Data type dense matrix
+*/
+
+
+#include <assert.h>
+
+class DenseMatrix
+{
+protected:
+  int height;
+  int width;
+  double * data;
+
+public:
+  ///
+  DenseMatrix ();
+  ///
+  DenseMatrix (int h, int w = 0);
+  ///
+  DenseMatrix (const DenseMatrix & m2);
+  ///
+  ~DenseMatrix ();
+
+  ///
+  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]; }
+
+  ///
+  DenseMatrix & operator= (const DenseMatrix & m2);
+  ///
+  DenseMatrix & operator+= (const DenseMatrix & m2);
+  ///
+  DenseMatrix & operator-= (const DenseMatrix & m2);
+
+  ///
+  DenseMatrix & operator= (double v);
+  ///
+  DenseMatrix & operator*= (double v);
+
+  ///
+  void Mult (const FlatVector & v, FlatVector & prod) const
+  {
+    double sum;
+    const double * mp, * sp;
+    double * dp;
+    
+#ifdef DEBUG
+    if (prod.Size() != height)
+      {
+	cerr << "Mult: wrong vector size " << endl;
+	assert (1);
+	// prod.SetSize (height);
+      }
+    
+
+    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.Elem(1);
+	for (int i = 1; i <= height; i++)
+	  {
+	    sum = 0;
+	    sp = &v.Get(1);
+	    
+	    for (INDEX j = 1; j <= width; j++)
+	      {
+		//        sum += Get(i,j) * v.Get(j);
+		sum += *mp * *sp;
+		mp++;
+		sp++;
+	      }
+	    
+	    *dp = sum;
+	    dp++;
+	  }
+      }
+  }
+
+  ///
+  void MultTrans (const Vector & v, Vector & prod) const;
+  ///
+  void Residuum (const Vector & x, const Vector & b, Vector & res) const;
+  ///
+  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);
+  ///
+  void Solve (const Vector & b, Vector & x) const;
+  ///
+  void SolveDestroy (const Vector & b, Vector & x);
+  ///
+  const double & Get(INDEX i, INDEX j) const { return data[(i-1)*width+j-1]; }
+  ///
+  const double & Get(INDEX i) const { return data[i-1]; }
+  ///
+  void Set(INDEX i, INDEX j, double v) { data[(i-1)*width+j-1] = v; }
+  ///
+  double & Elem(INDEX i, INDEX j) { return data[(i-1)*width+j-1]; }
+  ///
+  const double & ConstElem(INDEX i, INDEX 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]; }
+
+
+
+  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]; }
+};
+
+
+
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/linalg/linalg.hpp b/contrib/Netgen/libsrc/linalg/linalg.hpp
new file mode 100644
index 0000000000..4599fad48d
--- /dev/null
+++ b/contrib/Netgen/libsrc/linalg/linalg.hpp
@@ -0,0 +1,35 @@
+#ifndef FILE_LINALG
+#define FILE_LINALG
+
+/* *************************************************************************/
+/* File:   linalg.hpp                                                      */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Oct. 94                                                    */
+/* *************************************************************************/
+
+/* 
+
+   Data types for basic linear algebra
+   more data types are found in linalgl.hpp
+   
+   The basic concepts include the data types 
+   
+    Vector
+    SparseMatrix
+    DenseMatrix
+
+*/
+
+
+#include <myadt.hpp>
+namespace netgen
+{
+#include "vector.hpp"
+  // #include "basemat.hpp"
+#include "densemat.hpp"
+  // #include "sparsmat.hpp"
+#include "polynomial.hpp"
+}
+#endif
+
+
diff --git a/contrib/Netgen/libsrc/linalg/polynomial.cpp b/contrib/Netgen/libsrc/linalg/polynomial.cpp
new file mode 100644
index 0000000000..964dab9674
--- /dev/null
+++ b/contrib/Netgen/libsrc/linalg/polynomial.cpp
@@ -0,0 +1,216 @@
+#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;
+
+  //  (*testout) << "maxv = " << maxv << " =~= ";
+
+  /*
+  double x, y;
+  maxv = -1e8;
+  double val;
+  for (x = 0; x <= 1.01; x += 0.1)
+    for (y = 0; y <= 1.01; y += 0.1)
+      {
+	val = Value (x, y);
+	if (val > maxv)
+	  maxv = val;
+      }
+
+  //  (*testout) << maxv << endl;
+  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 0000000000..3108d4dd72
--- /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/sparsmat.cpp b/contrib/Netgen/libsrc/linalg/sparsmat.cpp
new file mode 100644
index 0000000000..f3249f82e6
--- /dev/null
+++ b/contrib/Netgen/libsrc/linalg/sparsmat.cpp
@@ -0,0 +1,1707 @@
+//#include <algorithm>
+#ifdef ABC
+
+#include <mystdlib.h>
+ 
+#include <linalg.hpp>
+
+
+namespace netgen
+{
+MatrixGraph :: MatrixGraph (int size, int aincrement)
+{
+  int i;
+  increment = aincrement;
+  
+  lines.SetSize (size);
+  for (i = 1;i <= size; i++)
+    {
+      lines.Elem(i).allocsize = increment;
+      lines.Elem(i).size = 1;
+      lines.Elem(i).diag = 1;
+      lines.Elem(i).col = new INDEX[increment];
+      lines.Elem(i).col[0] = i;
+    }
+}
+
+MatrixGraph :: MatrixGraph (const ARRAY<int> & linesize)
+{
+  INDEX i;
+  INDEX n = linesize.Size();
+  INDEX sum = 0;
+  INDEX * cola;
+
+  lines.SetSize (n);
+  increment = 0;
+
+  for (i = 1; i <= n; i++)
+    sum += linesize.Get(i);
+  
+  cola = new INDEX[sum];
+
+  sum = 0;
+  for (i = 1; i <= n; i++)
+    {
+      lines.Elem(i).allocsize=linesize.Get(i);
+      lines.Elem(i).size = 1;
+      lines.Elem(i).diag = 1;
+      lines.Elem(i).col = &cola[sum];
+      cola[sum] = i;
+      sum += linesize.Get(i);
+    }
+}
+
+int MatrixGraph :: GetPosition (INDEX i, INDEX j) const
+{
+  int k;
+  
+  INDEX * ip = lines.Get(i).col;
+  INDEX n = lines.Get(i).size;
+  for (k = 1; k <= n; k++, ip++)
+    {
+      if (j == *ip) return k;
+    }
+
+  return 0;
+  
+  /*  
+  //  quick search:
+
+  const INDEX * ip = lines.Get(i).col;
+  int max = lines.Get(i).size;
+
+  int l = 0, k = 1, newind;
+  
+  while (k < max)
+    k <<= 1;
+
+  while (k > 0)
+    {
+      k >>= 1;
+      newind = k+l;
+      if (newind >= max) continue;
+      if (ip[newind] <= j)
+	l += k;
+    }
+
+  if (l < max && ip[l] == j) 
+    return l+1;
+
+  return 0;
+  */
+}
+
+
+
+int MatrixGraph :: CreatePosition (INDEX i, INDEX j)    
+  {
+  int k;
+  MatrixGraph::linestruct * lin = &lines.Elem(i);
+
+ 
+
+  for (k = 1; k <= lin->size; k++)
+    {
+      if (lin->col[k-1] == j) 
+	return 0;
+      if (lin->col[k-1] > j) 
+	break;
+    }
+
+  // k ... position to enter new element
+
+  if (lin->size == lin->allocsize)
+    {
+      INDEX * tmpcol = new INDEX[lin->size + increment];
+      memcpy (tmpcol, lin->col, sizeof(INDEX) * lin->size);
+      delete lin->col;
+      lin->col = tmpcol;
+      lin->allocsize+=increment;
+    }
+
+  if (k <= lin->size)
+    memmove (&lin->col[k], &lin->col[k-1], 
+	     sizeof(INDEX) * (lin->size + 1 - k));
+  
+  lin->col[k-1] = j;
+  lin->size++;
+  if (j < i) lin->diag++;
+
+  return 1;
+  }
+
+
+
+double & SparseMatrix :: operator() (INDEX i, INDEX j)
+{
+  if (i >= 1 && i <= height && j >= 1 && j <= width)
+    return Elem(i, j);
+  else
+    {
+    (*myerr) << "SparseMatirx::operator(): out of range" << endl;
+    return shit;
+    }
+}
+ 
+double SparseMatrix :: operator() (INDEX i, INDEX j) const
+{
+  if (i >= 1 && i <= height && j >= 1 && j <= width)
+    return Get(i, j);
+  else
+    {
+    (*myerr) << "SparseMatirx::operator(): out of range" << endl;
+    return shit;
+    }
+}
+   
+
+
+
+
+
+SparseMatrix :: SparseMatrix (INDEX h, INDEX w)
+  : BaseMatrix (h, w)
+{
+  ;
+}
+
+
+
+
+void SparseMatrix :: Mult (const Vector & v, Vector & prod) const
+  {
+  double sum, vi;
+  INDEX i, j, n;
+
+  const INDEX * col;
+  const double * valp;
+
+  //  const Vector & v = bv.CastToVector();
+  //  Vector & prod = bprod.CastToVector();
+
+  prod.SetSize (Height());
+
+  /*
+  if (prod.Size() != Height() || v.Size() != Width())
+    {
+    (*myerr) << "SparseMatrix::Mult: Dimensions don't fit" << endl;
+    return;
+    }
+    */
+
+  if (!Symmetric())
+    {
+      n = Height();
+      INDEX w = Width();
+
+      /*
+      const MatrixGraph::linestruct * linep = &graph->lines.Get(1);   
+
+      for (i = 1; i <= n; i++) 
+	{
+	  sum = 0; 
+	  col = linep->col;
+	  valp = data.Get(i);
+	  
+	  for (j = 0; j < linep->size; j++, valp++, col++)
+	    sum += *valp * v.Get(*col); 
+
+	  linep++;
+	  prod.Set (i, sum);
+	}
+	*/
+
+      const MatrixGraph::linestruct * linep = &graph->lines.Get(1);   
+      const double * vp = &v.Get(1);
+      vp--;
+
+      for (i = 1; i <= n; i++) 
+	{
+	  sum = 0; 
+	  col = linep->col;
+	  valp = data.Get(i);
+	  const int ls = linep->size;
+	  
+	  if (i <= w)
+	    for (j = 0; j < ls; j++)
+	      sum += valp[j] * vp[col[j]];
+	  else
+	    // decrement one, because of diagonal element outside
+	    for (j = 0; j < ls-1; j++)
+	      sum += valp[j] * vp[col[j]];
+
+	  
+	  linep++;
+	  prod.Elem(i) = sum;
+	}
+
+    }
+  else
+    {
+      prod = 0;
+      n = Height();
+      
+      const MatrixGraph::linestruct * linep = &graph->lines.Get(1);   
+
+      for (i = 1; i <= n; i++)
+	{
+	  sum = 0; 
+	  //	  col = graph->lines.Get(i).col;
+	  col = linep->col;
+	  valp = data.Get(i);
+	  vi = v.Get(i);
+	  
+	  for (j = linep->diag-1; j > 0; j--, col++, valp++)
+	    {
+	      sum += (*valp) * v.Get(*col);
+	      prod.Elem(*col) += (*valp) * vi;
+	    }
+	  
+	  linep++;
+	  sum += (*valp) * v.Get(*col);
+	  prod.Elem(i) += sum;
+	}
+    }
+  }
+
+void SparseMatrix :: MultTrans (const Vector & v, Vector & prod) const
+  {
+  INDEX i, j, n, coln;
+  //  const Vector & v = bv.CastToVector();
+  //  Vector & prod = bprod.CastToVector();
+  const INDEX * col;
+  const double * valp;
+  double val;
+
+  prod.SetSize (Width());
+
+  if (prod.Size() != Width() || v.Size() != Height())
+    {
+    (*myerr) << "SparseMatrix::Mult: Dimensions don't fit" << endl;
+    return;
+    }
+
+  if (!Symmetric())
+    {
+    n = Height();
+    prod = 0;
+
+    for (i = 1; i <= n; i++)
+      {
+	col = graph->lines.Get(i).col;
+	valp = data.Get(i);
+	coln = graph->lines.Get(i).size;
+	val = v.Get(i);
+
+	//      lin = &lins.Get(i);
+	//      col = lin->col;
+	//      val = v.Get(i);
+      
+      for (j = 1; j <= coln; j++, col++, valp++)
+        prod.Elem(*col) += (*valp) * val;
+      }
+    }
+  else
+    {
+    Mult (v, prod);
+    }
+  }
+
+
+
+
+void SparseMatrix :: Residuum (const Vector & bx, const Vector & bb,
+      Vector & bres) const
+  {
+    BaseMatrix :: Residuum (bx, bb, bres);
+    /*
+  double sum, xi;
+  INDEX i, j, n;
+  colstruct * col;
+  const linestruct * lin;
+  const Vector & x = bx.CastToVector();
+  const Vector & b = bb.CastToVector();
+  Vector & res = bres.CastToVector();
+
+  res.SetSize (b.Size());
+
+  if (res.Size() != b.Size() || b.Size() != Height() ||
+      x.Size() != Width())
+    {
+    (*myerr) << "SparseMatrix::Residuum: Dimensions don't fit" << endl;
+    return;
+    }
+
+  n = Height();
+  if (!Symmetric())
+    {
+    for (i = 1; i <= Height(); i++)
+      {
+      lin = &lins.Get(i);
+      sum = b.Get(i);
+      col = lin->col;
+
+      for (j = lin->size; j > 0; j--, col++)
+        sum -= col->data * x.Get(col->colnr);
+
+      res.Set (i, sum);
+      }
+    }
+  else
+    {
+    res = b;
+    for (i = 1; i <= n; i++)
+      {
+      lin = &lins.Get(i);
+      sum = 0;
+      col = lin->col;
+      xi = x.Get(i);
+
+      for (j = lin->size; j > 0; j--, col++)
+        {
+        sum -= col->data * x.Get(col->colnr);
+        if (col->colnr != i)
+          res.Elem(col->colnr) -= col->data * xi;
+        }
+
+      res.Elem(i) += sum;
+      }
+    }
+    */
+  }
+
+
+void SparseMatrix :: ResiduumTrans (const Vector & /* bx */, 
+				    const Vector & /* bb */,
+				    Vector & /* bres */) const
+  {
+    cerr << "SparseMastrix :: Residuumtrans called, but not implemented" << endl;
+
+    /*
+  INDEX i, j, n;
+  colstruct * col;
+  const linestruct * lin;
+  const Vector & x = bx.CastToVector();
+  const Vector & b = bb.CastToVector();
+  Vector & res = bres.CastToVector();
+
+
+  res.SetSize (Width());
+
+  if (res.Size() != b.Size() || b.Size() != Width() ||
+      x.Size() != Height())
+    {
+    (*myerr) << "SparseMatrix::ResiduumTrans: Dimensions don't fit" << endl;
+    return;
+    }
+  
+  if (!Symmetric())
+    {
+    n = Height();
+
+    res = b;
+
+    for (i = 1; i <= n; i++)
+      {
+      lin = &lins.Get(i);
+      col = lin->col;
+
+      for (j = lin->size; j > 0; j--, col++)
+        res.Elem(col->colnr) -= col->data * x.Get(i);
+      }
+    }
+  else
+    {
+    Residuum (bx, bb, bres);
+    }
+    */
+  }
+
+
+
+ostream & SparseMatrix :: Print (ostream & s) const
+  {
+  INDEX i, j;
+
+  if (Symmetric()) s << "Lower half of matrix:" << endl;
+
+  for (i = 1; i <= Height(); i++)
+    {
+    s << "Line " << i << " ";
+
+    if (Symmetric())
+      {
+	for (j = 1 ; j <= GetDiagPos (i); j++)
+	  s << "(" << GetColIndex (i, j) << ": " << GetData (i, j) << ") ";
+      }
+    else
+      {
+	for (j = 1 ; j <= ElementsInLine (i); j++)
+	  s << "(" << GetColIndex (i, j) << ": " << GetData (i, j) << ") ";
+      }
+
+    s << endl;
+    }
+  return s;
+  }
+
+
+
+
+
+
+void SparseMatrix :: AddElementMatrix (const ARRAY<INDEX> & pnum,
+      const BaseMatrix & elemmat)
+{
+  int i, j, i1, i2, n;
+
+  n = pnum.Size();
+  static ARRAY<int> spnum, map;
+  spnum.SetSize (n);
+  map.SetSize (n);
+
+  for (i = 1; i <= n; i++)
+    spnum.Elem(i) = pnum.Get(i);
+
+  for (i = 1; i <= n; i++)
+    for (j = 1; j <= n-1; j++)
+      if (spnum.Elem(j) > spnum.Elem(j+1))
+	swap (spnum.Elem(j), spnum.Elem(j+1));
+
+  for (i = 1; i <= n; i++)
+    for (j = 1; j <= n; j++)
+      if (pnum.Get(i) == spnum.Get(j))
+	map.Elem(j) = i;
+
+
+  const DenseMatrix & delemmat = (const DenseMatrix&)elemmat;
+  if (Symmetric())
+    {
+      for (i1 = 1; i1 <= n; i1++)
+	{
+	  INDEX ii1 = spnum.Get(i1);
+	  const INDEX * coli = graph->lines.Get(ii1).col;
+	  double * val = data.Get(ii1);
+	  int ncol = graph->lines.Get(ii1).size;
+
+	  int hi = 0;
+	  for (i2 = 1; i2 <= i1; i2++)
+	    {
+	      while (coli[hi] < spnum.Get(i2))
+		hi++;
+	      val[hi] += delemmat.Get(map.Get(i1), map.Get(i2));
+	    }
+	}
+
+      /*
+      for (i1 = 1; i1 <= pnum.Size(); i1++)
+	for (i2 = 1; i2 <= i1; i2++)
+	  Elem(pnum.Get(i1), pnum.Get(i2)) += delemmat.Get(i1, i2);
+	  */
+    }
+  else
+    {
+      for (i1 = 1; i1 <= n; i1++)
+	{
+	  INDEX ii1 = spnum.Get(i1);
+	  const INDEX * coli = graph->lines.Get(ii1).col;
+	  double * val = data.Get(ii1);
+	  int ncol = graph->lines.Get(ii1).size;
+
+	  int hi = 0;
+	  for (i2 = 1; i2 <= n; i2++)
+	    {
+	      while (coli[hi] < spnum.Get(i2))
+		hi++;
+	      val[hi] += delemmat.Get(map.Get(i1), map.Get(i2));
+	    }
+	}
+
+      /*
+      for (i1 = 1; i1 <= pnum.Size(); i1++)
+	for (i2 = 1; i2 <= pnum.Size(); i2++)
+	  Elem(pnum.Get(i1), pnum.Get(i2)) += delemmat.Get(i1, i2);
+	  */
+    }
+}
+
+
+
+
+double SparseMatrix :: RowTimesVector (INDEX i, const Vector & v) const
+  {
+  const double * valp;
+  const INDEX * col;
+  int coln;
+  double sum;
+  int j;
+
+  /*
+  if (Width() > v.Size())
+    {
+    cerr << "SparseMatrix::RowTimesVector: Size doesn't fit" << endl;
+    return 0;
+    }
+    */
+
+  col = graph->lines.Get(i).col;
+  valp = data.Get(i);
+  sum = 0;
+  coln = Symmetric() ? graph->lines.Get(i).diag : graph->lines.Get(i).size;
+
+  for (j = 1; j <= coln; ++j, ++col, ++valp)
+    sum += (*valp) * v.Get(*col);
+
+  return sum;
+
+  /*
+	col = graph->lines.Get(i).col;
+	valp = data.Get(i);
+	coln = graph->lines.Get(i).size;
+	val = v.Get(i);
+
+	//      lin = &lins.Get(i);
+	//      col = lin->col;
+	//      val = v.Get(i);
+      
+      for (j = 1; j <= coln; j++, col++, valp++)
+        prod.Elem(*col) += (*valp) * val;
+	*/
+
+
+  }
+  
+
+
+void SparseMatrix :: AddRowToVector (INDEX i, double s, Vector & v) const
+{
+  const double * valp;
+  const INDEX * col;
+  double * vp;
+  int coln, j;
+  
+#ifdef debug  
+  if (Width() > v.Size())
+    {
+      cerr << "SparseMatrix::AddRowToVector: Size doesn't fit" 
+	   << "w = " << Width() << " len = " << v.Size() << endl;
+      return;
+    }
+#endif    
+
+  vp = &v.Elem(1) - 1;
+  valp = data.Get(i);
+  col = graph->lines.Get(i).col;
+  coln =  Symmetric() ? graph->lines.Get(i).diag : graph->lines.Get(i).size;
+
+  //  for (j = 0; j < coln; j++)
+  //    vp[col[j]] += s * valp[j];
+
+  for (j = coln-1; j >= 0; --j, ++valp, ++col)
+    vp[*col] += s * (*valp);
+}
+
+
+
+
+
+
+char SparseMatrix :: Used (INDEX i, INDEX j) const
+  {
+  return (graph->GetPosition(i, j) != 0);
+  }
+
+
+
+double SparseMatrix :: Get(INDEX i, INDEX j) const
+  {
+  if (Symmetric() && (j > i)) swap (i, j);
+    
+  int pos = graph->GetPosition(i, j);
+  if (pos)
+    return data.Get(i)[pos-1];
+  else 
+    return 0;
+  }
+  
+
+/*
+  quick search:
+
+  const colstruct * col = lins.Get(i).col;
+  INDEX max = lins.Get(i).size;
+
+  int l = 0, k = 1, newind;
+
+  while (k < max)
+    k <<= 1;
+
+
+  while (k > 0)
+    {
+    k >>= 1;
+    newind = k+l;
+    if (newind >= max) continue;
+    if (col[newind].colnr <= j)
+      l += k;
+    }
+
+  if (l < max && col[l].colnr == j) return col[l].data;
+
+  return 0;
+  */
+  
+
+
+void SparseMatrix :: Set(INDEX i, INDEX j, double v)
+  {
+  Elem (i, j) = v;
+  }
+
+
+
+double & SparseMatrix :: Elem(INDEX i, INDEX j)
+  {
+  if (Symmetric() && j > i) swap (i, j);
+  
+  int pos = graph->GetPosition (i, j);
+  if (!pos)
+    {
+      CreatePosition (i, j);
+      pos = graph->GetPosition (i, j);
+    }
+
+  return data.Elem(i)[pos-1];
+  }
+
+
+
+
+
+
+
+
+SparseMatrixFlex :: SparseMatrixFlex (INDEX h, INDEX w)
+  : SparseMatrix (h, w)
+{
+  INDEX i;
+  graph = 
+    mygraph = new MatrixGraph (h);
+
+  data.SetSize (h);
+  for (i = 1; i <= graph->Size(); i++)
+    {
+      data.Elem(i) = new double[graph->lines.Get(i).allocsize];
+      data.Elem(i)[0] = 0;
+    }
+}
+
+SparseMatrixFlex :: ~SparseMatrixFlex ()
+{
+  ;
+}
+
+
+void SparseMatrixFlex :: SetSize (INDEX /* h */, INDEX /* w */)
+{
+  cerr << "SparseMatrixFlex :: SetSize() not implemented" << endl;
+}
+
+void SparseMatrixFlex :: SetSymmetric (int sym)
+{
+  BaseMatrix::SetSymmetric (sym);
+}
+
+void SparseMatrixFlex :: ChangeSize (INDEX /* h */, INDEX /* w */)
+{
+  cerr << "SparseMatrixFlex :: ChangeSize() not implemented" << endl;
+}
+
+
+void SparseMatrixFlex :: DeleteElements ()
+{
+  ;
+} 
+
+BaseMatrix * SparseMatrixFlex :: Copy () const
+{
+  return (SparseMatrixFlex*)this;
+}
+
+
+int SparseMatrixFlex :: CreatePosition (INDEX i, INDEX j)
+{
+  int oldlinesize = graph->lines.Get(i).allocsize;
+  if (mygraph->CreatePosition(i, j))
+    {
+      int newlinesize = graph->lines.Get(i).allocsize;
+      if (oldlinesize != newlinesize)
+	{
+	  double * hdp = new double[newlinesize];
+	  memcpy (hdp, data.Elem(i), sizeof(double) * oldlinesize);
+	  delete data.Elem(i);
+	  data.Elem(i) = hdp;
+	}
+
+      int pos = graph->GetPosition (i, j);
+      int size = graph->lines.Get(i).size;
+
+      memmove (&data.Elem(i)[pos], &data.Elem(i)[pos-1], 
+	       sizeof(double) * (size-pos));
+      data.Elem(i)[pos-1] = 0;
+    }
+
+  return 0;
+}
+
+
+
+
+SparseMatrixFix :: SparseMatrixFix (const MatrixGraph & agraph,
+				     int asymmetric)
+  : SparseMatrix (agraph.Size())
+{
+  graph = &agraph;
+  SetSymmetric (asymmetric);
+
+  data.SetSize (graph->Size());
+
+  int i, nne;
+  double * block;
+  
+  nne = 0;
+  for (i = 1; i <= graph->Size(); i++)
+    {
+    if (Symmetric())
+      nne += graph->lines.Get(i).diag;
+    else
+      nne += graph->lines.Get(i).size;
+    }
+    
+  block = new double[nne];
+  
+  for (i = 0; i < nne; i++)
+    block[i] = 0;
+
+  nne = 0;
+  for (i = 1; i <= graph->Size(); i++)
+    {
+    data.Elem(i) = &block[nne];
+    if (Symmetric())
+      nne += graph->lines.Get(i).diag;
+    else
+      nne += graph->lines.Get(i).size;
+    }
+
+}
+
+
+SparseMatrixFix :: ~SparseMatrixFix ()
+{
+  ;
+}
+
+int SparseMatrixFix :: CreatePosition (INDEX i, INDEX j)
+{
+  (*myerr) << "SparseMatrixFix cannot change graph, requested: " 
+       << i << "-" << j << endl;
+  return 0;
+}
+
+#ifdef nothing
+
+
+SparseMatrix :: SparseMatrix () : BaseMatrix (), lins()
+  {
+  };
+
+SparseMatrix :: SparseMatrix (INDEX h, INDEX w) : BaseMatrix (h, w), lins (h)
+  {
+  if (!w) w = h;
+
+  for (INDEX i = 1; i <= h; i++)
+    {
+    lins[i].size = 0;
+    lins[i].maxsize = 0;
+    lins[i].col = NULL;
+    }
+  }
+
+SparseMatrix :: SparseMatrix (const SparseMatrix & m2) : BaseMatrix(), lins()
+  {
+  (*this) = m2;
+  }
+
+
+SparseMatrix :: ~SparseMatrix ()
+  {
+  DeleteElements ();
+  }
+
+
+
+void SparseMatrix :: SetSize (INDEX h, INDEX w)
+  {
+  if (!w) w = h;
+  DeleteElements ();
+
+  if (height == h && width == w) return;
+
+  height = h;
+  width = w;
+  lins.SetSize (height);
+
+  if (lins.Size () == height)
+    {
+    for (INDEX i = 1; i <= h; i++)
+      {
+      lins[i].size = 0;
+      lins[i].maxsize = 0;
+      lins[i].col = NULL;
+      }
+    }
+  else
+    {
+    height = width = 0;
+    (*myerr) << "SparseMatrix::SetSize: Out of memory" << endl;
+    }
+  }
+
+
+void SparseMatrix :: ChangeSize (INDEX h, INDEX w)
+  {
+  INDEX i;
+
+  if (!w) w = h;
+  if (height == h && width == w) return;
+
+  lins.SetSize (h);
+
+  if (lins.Size () == h)
+    {
+    for (i = height+1; i <= h; i++)
+      {
+      lins[i].size = 0;
+      lins[i].maxsize = 0;
+      lins[i].col = NULL;
+      }
+    for (i = h+1; i <= height; i++)
+      {
+      if (lins[i].col)
+        {
+        DeleteColStruct (lins[i].col, lins[i].maxsize);
+
+        lins[i].col = NULL;
+        lins[i].size = 0;
+        lins[i].maxsize = 0;
+        }
+      }
+
+    height = h;
+    width = w;
+    }
+  else
+    {
+    height = width = 0;
+    (*myerr) << "SparseMatrix::SetSize: Out of memory" << endl;
+    }
+  }
+
+
+void SparseMatrix :: SetSymmetric (int sym)
+  {
+  INDEX i, j;
+  int nr;
+
+  if (sym != Symmetric())
+    {
+    BaseMatrix :: SetSymmetric (sym);
+
+    if (!sym)
+      {
+      for (i = 1; i <= Height(); i++)
+        for (nr = 1; nr <= ElementsInLine (i); nr++)
+          {
+          j = GetIndex (i, nr);
+          if (j < i)
+            Elem (j, i) = GetData (i, nr);
+          }
+      }
+    else
+      {
+      DeleteElements();
+      }
+    }
+  }
+
+
+void SparseMatrix :: DeleteElements ()
+  {
+  for (INDEX i = 1; i <= lins.Size(); i++)
+    {
+    if (lins[i].col)
+      {
+      DeleteColStruct (lins[i].col, lins[i].maxsize);
+
+      lins[i].col = NULL;
+      lins[i].size = 0;
+      lins[i].maxsize = 0;
+      }
+    }
+  }
+
+
+
+double & SparseMatrix::operator() (INDEX i, INDEX j)
+{
+  if (i >= 1 && j >= 1 && i <= height && j <= width)
+    {
+      return Elem(i, j);
+    }
+  else (*myerr) << "\nindex (" << i << "," << j << ") out of range (1.."
+	     << height << ",1.." << width << ")\n";
+  return shit;
+}
+
+double SparseMatrix::operator() (INDEX i, INDEX j) const
+{
+  if (i >= 1 && j >= 1 && i <= height && j <= width)
+    {
+    return Get(i, j);
+    }
+  else (*myerr) << "\nindex (" << i << "," << j << ") out of range (1.."
+	     << height << ",1.." << width << ")\n";
+  return 0;
+}
+
+SparseMatrix & SparseMatrix :: operator= (const SparseMatrix & m2)
+  {
+  INDEX i, j;
+
+  SetSize (m2.Height(), m2.Width());
+  SetSymmetric (m2.Symmetric());
+
+  for (i = 1; i <= m2.Height(); i++)
+    for (j = 1; j <= m2.ElementsInLine(i); j++)
+      (*this).Elem(i, m2.GetIndex(i, j)) = m2.GetData(i, j);
+  return *this;
+  }
+
+
+SparseMatrix & SparseMatrix :: operator*= (double v)
+  {
+  INDEX i, j;
+
+  for (i = 1; i <= Height(); i++)
+    for (j = 1; j <= ElementsInLine(i); j++)
+      GetDataRef(i, j) *= v;
+  return *this;
+  }
+
+
+
+char SparseMatrix :: Used (INDEX i, INDEX j) const
+  {
+  if (Symmetric() && j > i) swap (i, j);
+
+  if (i < 1 || i > height) return 0;
+
+  const colstruct * col = lins.Get(i).col;
+  INDEX max = lins.Get(i).size;
+
+  for (int k = 0; k < max; k++, col++)
+    if (col->colnr == j) return 1;
+
+  return 0;
+  }
+
+
+
+double SparseMatrix :: Get(INDEX i, INDEX j) const
+  {
+  if (Symmetric() && j > i) swap (i, j);
+
+  const colstruct * col = lins.Get(i).col;
+  INDEX max = lins.Get(i).size;
+
+  int l = 0, k = 1, newind;
+
+  while (k < max)
+    k <<= 1;
+
+
+  while (k > 0)
+    {
+    k >>= 1;
+    newind = k+l;
+    if (newind >= max) continue;
+    if (col[newind].colnr <= j)
+      l += k;
+    }
+
+  if (l < max && col[l].colnr == j) return col[l].data;
+
+  return 0;
+
+  /*
+  for (INDEX k = 0; k < max; k++, col++)
+    if (col->colnr == j) 
+      {
+	if (l != k) cerr << "Set: ind not ok" << endl;
+        else cerr << "is ok" << endl;
+      return col->data;
+      }
+
+  return 0;
+  */
+  }
+
+
+void SparseMatrix :: Set(INDEX i, INDEX j, double v)
+  {
+  Elem (i, j) = v;
+  }
+
+
+
+double & SparseMatrix :: Elem(INDEX i, INDEX j)
+  {
+  if (Symmetric() && j > i) swap (i, j);
+
+  linestruct * lin = &lins.Elem(i);
+  colstruct * col, *ncol;
+
+  int size = lin->size;
+  int pos;
+
+  if (size)
+    {
+
+    // bereits Elemente in der Liste
+
+    if (j > lin->col[size-1].colnr)
+      {
+
+      // neues Element an letzter Position einfuegen
+
+      if (lin->maxsize > size)
+        {
+        lin->size++;
+        lin->col[size].colnr = j;
+        return lin->col[size].data = 0;
+        }
+
+      if ( (ncol = NewColStruct(lin->maxsize+4)/* new colstruct[lin->maxsize+4] */) != NULL)
+        {
+        memcpy (ncol, lin->col, sizeof(colstruct) * size);
+
+        DeleteColStruct (lin->col, lin->maxsize);
+
+        lin->maxsize += 4;
+        lin->col = ncol;
+        lin->size++;
+        ncol[size].colnr = j;
+        return ncol[size].data = 0;
+        }
+      else
+        {
+        (*myerr) << "SparseMatrix::Elem: Out of memory 1" << endl;
+        return shit;
+        }
+
+      }
+    else
+      {
+
+      for (col = lin->col; col->colnr < j; col++);
+                                        // Zeilenliste durchsuchen
+
+      if (col->colnr == j) return col->data;
+                                        // Element exisitiert bereits
+
+      if (lin->maxsize > size)
+        {
+        memmove (col+1, col, size_t((char*)&lin->col[size] - (char*)col));
+
+        lin->size++;
+        col->colnr = j;
+        return col->data = 0;
+        }
+
+      pos = size_t (col - lin->col);
+
+      if ( (ncol = NewColStruct(lin->maxsize+4) ) != NULL)
+        {
+
+        if (pos) memcpy (ncol, lin->col, sizeof(colstruct) * pos);
+        memcpy (ncol+(pos+1), col, sizeof(colstruct) * (size-pos));
+
+        DeleteColStruct (lin->col, lin->maxsize);
+//        delete lin->col;
+
+        lin->maxsize += 4;
+        lin->col = ncol;
+        lin->size++;
+        ncol[pos].colnr = j;
+        return ncol[pos].data = 0;
+        }
+      else
+        {
+        (*myerr) << "SparseMatrix::Elem: Out of memory 2" << endl;
+        return shit;
+        }
+      }
+    }
+  else
+    {
+    // kein Element in der Liste
+
+    if (lin->maxsize)
+      {
+      // Liste bereits angelegt
+
+      lin->size = 1;
+      lin->col->colnr = j;
+      return lin->col->data = 0;
+      }
+    else
+      {
+      if ( (lin->col = NewColStruct(6) ) != NULL )
+        {
+        lin->maxsize = 6;
+        lin->size = 1;
+        lin->col->colnr = j;
+        return lin->col->data = 0;
+        }
+      else
+        {
+        (*myerr) << "SparseMatrix::Elem: Out of memory 3" << endl;
+        return shit;
+        }
+      }
+    }
+  }
+
+
+
+void SparseMatrix :: Delete (INDEX i, int nr)
+  {
+  colstruct * col = lins[i].col;
+
+  nr--;
+  while (nr < lins[i].size-1)
+    {
+    col[nr].data = col[nr+1].data;
+    col[nr].colnr = col[nr+1].colnr;
+    nr++;
+    }
+  lins[i].size--;
+  }
+
+void SparseMatrix :: DeleteElem (INDEX i, INDEX j)
+  {
+  int nr;
+  int dec = 0;
+
+  if (Symmetric() && j > i) swap (i, j);
+
+  colstruct * col = lins[i].col;
+
+  for (nr = 0; nr < lins[i].size; nr++)
+    {
+    if (dec)
+      {
+      col[nr-1].data = col[nr].data;
+      col[nr-1].colnr = col[nr].colnr;
+      }
+    if (col[nr].colnr == j) dec = 1;
+    }
+  if (dec)
+    lins[i].size--;
+  }
+
+
+void SparseMatrix :: SetLineAllocSize (INDEX i, int j)
+  {
+  colstruct * ncol;
+
+
+  if (lins[i].maxsize < j)
+    {
+    if ( (ncol = NewColStruct(j)) != NULL)
+      {
+      memcpy (ncol, lins[i].col, sizeof(colstruct) * lins[i].size);
+
+      if (lins[i].maxsize)
+        DeleteColStruct (lins[i].col, lins[i].maxsize);
+
+      lins[i].maxsize = j;
+      lins[i].col = ncol;
+      }
+    else
+      (*myerr) << "SPARSE_MATIRX :: SetLineAllocSize: Out of Memory" << endl;
+    }
+  }
+
+
+
+
+
+
+
+
+
+  
+  
+
+
+
+SparseMatrix operator* (const SparseMatrix & m1,
+                         const SparseMatrix & m2)
+  {
+  SparseMatrix m(m1.Height(), m2.Width());
+  INDEX i, j, k, nr;
+
+  if (!m1.Symmetric() && !m2.Symmetric())
+    {
+    for (i = 1; i <= m1.Height(); i++)
+      {
+      for (j = 1; j <= m1.ElementsInLine(i); j++)
+        {
+        nr = m1.GetIndex (i, j);
+        for (k = 1; k <= m2.ElementsInLine(nr); k++)
+          {
+          m(i, m2.GetIndex(nr, k)) += m1.GetData(i, j) * m2.GetData(nr, k);
+          }
+        }
+      }
+    }
+  else
+    {
+    (*myerr) << "SparseMatrix :: operator* not implemented for symmetric case" << endl;
+    }
+  return m;
+  }
+
+
+SparseMatrix & SparseMatrix :: operator+= (const SparseMatrix & m2)
+  {
+  INDEX i, k;
+  int j;
+
+  if (Symmetric() == m2.Symmetric())
+    {
+    for (i = 1; i <= Height(); i++)
+      for (j = 1; j <= m2.ElementsInLine (i); j++)
+        {
+        k = m2.GetIndex (i, j);
+        Elem(i, k) += m2.GetData (i, j);
+        }
+    }
+  else
+    {
+    (*myerr) << "SparseMatrix :: operator+= not implemented for different cases" << endl;
+    }
+  return *this;
+  }
+
+
+
+SparseMatrix & SparseMatrix :: operator*= (const SparseMatrix & m2)
+  {
+  INDEX i, k;
+  int j, l;
+  colstruct * cs;
+  int ms, s;
+
+  if (!Symmetric() && !m2.Symmetric())
+    {
+    for (i = 1; i <= Height(); i++)
+      {
+      cs = lins[i].col;
+      s = lins[i].size;
+      ms = lins[i].maxsize;
+
+      lins[i].col = NULL;
+      lins[i].size = 0;
+      lins[i].maxsize = 0;
+
+
+      for (j = 0; j < s; j++)
+        {
+        k = cs[j].colnr;
+
+        for (l = 1; l <= m2.ElementsInLine (k); l++)
+          Elem(i, m2.GetIndex(k, l)) += cs[j].data * m2.GetData(k, l);
+        }
+
+      DeleteColStruct (cs, ms);
+      }
+    }
+  else
+    {
+    (*myerr) << "SparseMatrix :: operator*= not implemented for Symmetric matrices" << endl;
+    }
+
+  return *this;
+  }
+
+
+void SparseMatrix :: Solve (const Vector & v, Vector & sol) const
+  {
+  SparseMatrix temp = *this;
+  INDEX i, j, nr, k;
+  double q;
+
+  sol = v;
+
+
+  if (!Symmetric())
+    {
+    for (i = 1; i <= Height(); i++)
+      {
+      if (temp.ElementsInLine(i) < 1 || temp.GetIndex(i, 1) != i)
+        {
+        (*myerr) << "i = " << i << endl;
+        (*myerr) << "Solve: Matrix singular" << endl;
+        char ch;
+        cin >> ch;
+        }
+      for (j = 2; j <= temp.ElementsInLine(i); j++)
+        {
+        nr = temp.GetIndex(i, j);
+        if (temp.GetIndex(nr, 1) != i)
+          {
+          (*myerr) << temp << endl;
+          (*myerr) << "i = " << i << "j = " << j << "nr = " << nr << endl;
+          (*myerr) << "Solve: Graph not symmetrix" << endl;
+          char ch;
+          cin >> ch;
+          }
+
+        q = temp.GetData (nr, 1) / temp.GetData(i, 1);
+        temp.Delete (nr, 1);
+
+        for (k = 2; k <= temp.ElementsInLine (i); k++)
+          temp.Elem(nr, temp.GetIndex(i, k)) -= q * temp.GetData(i, k);
+
+        sol(nr) -= q * sol(i);
+        }
+      }
+
+    for (i = temp.Height(); i >= 1; i--)
+      {
+      for (j = 2; j <= temp.ElementsInLine(i); j++)
+        {
+        sol(i) -= temp.GetData(i, j) * sol(temp.GetIndex(i, j));
+        }
+      sol(i) /= temp.GetData(i, 1);
+      }
+    }
+  else
+    (*myerr) << "SparseMatrix :: Solve not implemented for symmetic case" << endl;
+  }
+
+
+
+
+
+void Transpose (const SparseMatrix & m1, SparseMatrix & m2)
+  {
+  INDEX i, j;
+
+  m2.SetSize(m1.Width(), m1.Height());
+  m2.SetSymmetric(m1.Symmetric());
+
+  if (!m1.Symmetric())
+    {
+    for (i = 1; i <= m1.Height(); i++)
+      for (j = 1; j <= m1.ElementsInLine(i); j++)
+        m2(m1.GetIndex(i, j), i) = m1.GetData(i, j);
+    }
+  else
+    m2 = m1;
+  }
+
+
+
+BaseMatrix * SparseMatrix :: Copy () const
+  {
+  return new SparseMatrix (*this);
+  }
+
+
+
+
+
+
+
+void SparseMatrix :: AddRowMatrix (INDEX row, const SparseMatrix & m2)
+  {
+  int i1, i2, i;
+  colstruct * cs1, * cs2, * ncs;
+  int s1, s2, s;
+
+  s1 = lins[row].size;
+  s2 = m2.lins[1].size;
+  cs1 = lins[row].col;
+  cs2 = m2.lins[1].col;
+
+  i1 = 0;
+  i2 = 0;
+  i = 0;
+
+
+  if (Symmetric())
+    {
+    while (s2 && cs2[s2-1].colnr > row) s2--;
+    }
+
+
+  while (i1 < s1 && i2 < s2)
+    {
+    if (cs1[i1].colnr < cs2[i2].colnr) i1++;
+    else if (cs1[i1].colnr > cs2[i2].colnr) i2++;
+    else { i1++; i2++; }
+    i++;
+    }
+
+  i += (s1 - i1);
+  i += (s2 - i2);
+
+  s = i;
+
+  if (s > s1)
+    {
+    ncs = NewColStruct (s);
+
+    i1 = 0;
+    i2 = 0;
+    i = 0;
+
+    while (i1 < s1 && i2 < s2)
+      {
+      if (cs1[i1].colnr < cs2[i2].colnr)
+        {
+        ncs[i].colnr = cs1[i1].colnr;
+        ncs[i].data = cs1[i1].data;
+        i1++;
+        }
+      else if (cs1[i1].colnr > cs2[i2].colnr)
+        {
+        ncs[i].colnr = cs2[i2].colnr;
+        ncs[i].data = cs2[i2].data;
+        i2++;
+        }
+      else
+        {
+        ncs[i].colnr = cs1[i1].colnr;
+        ncs[i].data = cs1[i1].data + cs2[i2].data;
+        i1++;
+        i2++;
+        }
+      i++;
+      }
+
+    while (i1 < s1)
+      {
+      ncs[i].colnr = cs1[i1].colnr;
+      ncs[i].data = cs1[i1].data;
+      i1++;
+      i++;
+      }
+
+    while (i2 < s2)
+      {
+      ncs[i].colnr = cs2[i2].colnr;
+      ncs[i].data = cs2[i2].data;
+      i2++;
+      i++;
+      }
+
+    if (lins[row].maxsize)
+      DeleteColStruct (cs1, lins[row].maxsize);
+
+    lins[row].col = ncs;
+    lins[row].size = s;
+    lins[row].maxsize = s;
+    }
+
+
+  else
+    {
+    i1 = 0;
+    i2 = 0;
+
+    while (i2 < s2)
+      {
+      if (cs1[i1].colnr == cs2[i2].colnr)
+        {
+        cs1[i1].data += cs2[i2].data;
+        i2++;
+        }
+      i1++;
+      }
+    }
+  }
+
+
+double SparseMatrix :: RowTimesVector (INDEX i, const Vector & v) const
+  {
+  const linestruct * lin;
+  const colstruct * col;
+  double sum;
+  int j;
+
+  if (Width() > v.Size())
+    {
+    cerr << "SparseMatrix::RowTimesVector: Size doesn't fit" << endl;
+    return 0;
+    }
+
+  lin = &lins.Get(i);
+  sum = 0;
+  col = lin->col;
+
+  for (j = lin->size; j > 0; j--, col++)
+    sum += col->data * v.Get(col->colnr);
+
+  return sum;
+  }
+  
+void SparseMatrix :: AddRowToVector (INDEX i, double s, Vector & v) const
+  {
+  const linestruct * lin;
+  const colstruct * col;
+  int j;
+
+  if (Width() > v.Size())
+    {
+    cerr << "SparseMatrix::AddRowToVector: Size doesn't fit" 
+    	 << "w = " << Width() << " len = " << v.Size() << endl;
+    return;
+    }
+
+  lin = &lins.Get(i);
+  col = lin->col;
+
+  for (j = lin->size; j > 0; j--, col++)
+    v.Elem(col->colnr) += s * col->data;
+  }
+
+
+static ARRAY<void*> poolarray;
+static ARRAY<int> poolsizes;
+
+
+SparseMatrix::colstruct * SparseMatrix :: NewColStruct (int s)
+  {
+//  return new colstruct[s];
+
+
+  int i, j;
+  void * p;
+  colstruct * cs;
+
+  if (s > 20) return new colstruct[s];
+
+  for (i = 1; i <= poolsizes.Size(); i++)
+    if (poolsizes.Get(i) == s) break;
+
+  if (i > poolsizes.Size())
+    {
+    poolsizes.Append(s);
+    poolarray.Append((void*)NULL);
+    i = poolsizes.Size();
+    }
+
+  p = poolarray.Get(i);
+  if (!p)
+    {
+    poolarray.Elem(i) = p = cs = new colstruct[10 * s];
+    for (j = 0; j < 9; j++)
+      *(void**)(void*)(&cs[s * j]) = &cs[s * (j+1)];
+    *(void**)(void*)(&cs[s * 9]) = NULL;
+    }
+
+  poolarray.Elem(i) = *(void**)p;
+  return (colstruct*)p;
+  }
+
+void SparseMatrix :: DeleteColStruct (colstruct * cs, int s)
+  {
+//  delete cs;
+//  return;
+
+  int i;
+
+  if (s > 20)
+    {
+    delete cs;
+    return;
+    }
+
+  for (i = 1; i <= poolsizes.Size(); i++)
+    if (poolsizes.Get(i) == s) break;
+
+
+  if (i > poolsizes.Size())
+    {
+    (*myerr) << "SparseMatrix :: DeleteColStruct: Size not found" << endl;
+    return;
+    }
+
+
+  *(void**)(void*)cs = poolarray.Get(i);
+  poolarray.Elem(i) = cs;
+  }
+
+void SparseMatrix :: SetGraph (const class TABLE<INDEX> & graph)
+  { 
+  int i, j, es, ad, nne;
+  colstruct * block;
+  
+  nne = 0;
+  for (i = 1; i <= graph.Size(); i++)
+    {
+    if (Symmetric())
+      {
+      es = 0;
+      for (j = 1; j <= graph.EntrySize(i); j++)
+        if (graph.Get(i, j) <= i)
+          es++;
+      }
+    else
+      es = graph.EntrySize(i);
+    nne += es;
+    }
+    
+  oneblock = 1;
+  block = new colstruct[nne];
+  
+  ad = 0;
+  for (i = 1; i <= graph.Size(); i++)
+    {
+    if (Symmetric())
+      {
+      es = 0;
+      for (j = 1; j <= graph.EntrySize(i); j++)
+        if (graph.Get(i, j) <= i)
+          es++;
+      }
+    else
+      es = graph.EntrySize(i);
+      
+    lins.Elem(i).size = 0;
+    lins.Elem(i).maxsize = es;
+    lins.Elem(i).col = &block[ad];
+    ad += es;
+    }
+  }
+
+
+
+
+#endif
+}
+#endif
diff --git a/contrib/Netgen/libsrc/linalg/sparsmat.hpp b/contrib/Netgen/libsrc/linalg/sparsmat.hpp
new file mode 100644
index 0000000000..74aaa3134e
--- /dev/null
+++ b/contrib/Netgen/libsrc/linalg/sparsmat.hpp
@@ -0,0 +1,265 @@
+#ifdef NONE
+
+#ifndef FILE_SPARSMAT
+#define FILE_SPARSMAT
+
+/* *************************************************************************/
+/* File:   sparsmat.hh                                                    */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Oct. 94                                                    */
+/* *************************************************************************/
+
+
+class SparseMatrix;
+
+/** 
+  The graph of a sparse matrix.
+  The graph is stored for a non-symmetric matrix
+*/
+class MatrixGraph
+{
+  /// data structure for one row of matrix
+  struct linestruct 
+  { 
+    /// allocated size
+    int allocsize;    
+    /// used size
+    int size;    
+    /// diag element (always allocated)      
+    int diag;  
+    /// colume numbers  
+    INDEX* col;
+  };
+
+  /// graph of matrix
+  ARRAY<linestruct> lines;
+  /// increment of allocated line-memory on overflow
+  int increment;
+  
+public:
+  //@{ @name Constructors, Destructor
+
+  /// Height of matrix, increment value for line-overflow
+  MatrixGraph (int size, int aincrement = 5);
+  /// Allocates graph with elements per row given in ARRAY linesize
+  MatrixGraph (const ARRAY<int> & linesize);
+  //@}
+
+  
+  /// returns position of Element (i, j), 0 for unused
+  int GetPosition (INDEX i, INDEX j) const;
+
+  /// returns position of new element
+  int CreatePosition (INDEX i, INDEX j); 
+
+  /// Returns height of matrix
+  int Size() const { return lines.Size(); }
+
+
+  friend class SparseMatrix;
+  friend class SparseMatrixFlex;
+  friend class SparseMatrixFix;
+  friend class PointJacobiPrecond;
+};
+
+
+
+
+
+
+/**
+  Base class for sparse matrix.
+  Matrix to work with in most applications
+  */
+class SparseMatrix : public BaseMatrix
+{
+protected:
+  /// graph of matrix
+  const MatrixGraph * graph;
+  /// pointer to matrix values in each row
+  ARRAY<double*> data;
+
+public:
+
+  /// returns reference, fail save but slow
+  virtual double & operator() (INDEX i, INDEX j);
+  /// returns value, fail save but slow
+  virtual double operator() (INDEX i, INDEX j) const;
+
+
+  /// prod = matrix x v
+  virtual void Mult (const Vector & v, Vector & prod) const;
+  /// prod = matrix^T x v
+  virtual void MultTrans (const Vector & v, Vector & prod) const;
+  /// res = b - mat x x
+  virtual void Residuum (const Vector & x, const Vector & b, 
+			 Vector & res) const;
+  /// res = b - mat^T x x
+  virtual void ResiduumTrans (const Vector & x, const Vector & b,
+			      Vector & res) const;
+
+
+  /**
+    Add element matrix to sparse matrix.
+    The graph of the element-matrix must be symmetric.
+    Global point numbers are given in pnum.
+   */
+  virtual void AddElementMatrix (const ARRAY<INDEX> & pnum, const BaseMatrix & elemmat);
+
+  /// for multigrid-extension, should be removed from here
+  void GSStepToInner (const Vector & b, Vector & x, double dump,
+      const BitArray & inner) const;
+
+  /// for multigrid-extension, should be removed from here
+  void GSStepBackToInner (const Vector & b, Vector & x, double dump,
+      const BitArray & inner) const;
+
+  /// 
+  virtual ostream & Print (ostream & s) const;
+
+  /** Scalar product of i-th row times vector.
+    For symmetric matrices only lower left block 
+    (including diagonal) is used.
+    */
+  double RowTimesVector (INDEX i, const Vector & v) const;
+  /** Adds s times the i-th row of matrix to vector v.
+    For symmetric matrices only lower left block 
+    (including diagonal) is used.
+    */
+  void AddRowToVector (INDEX i, double s, Vector & v) const;
+
+  /** Number of elements in line.
+    For symmetric matrices GetDiagPos must be used for
+    most purposes.
+    */
+  int ElementsInLine (INDEX i) const 
+    { return graph->lines.Get(i).size; }
+
+  /** Columne index of nr-th non-zero element in row i */
+  INDEX GetColIndex (INDEX i, int nr) const
+    { return graph->lines.Get(i).col[nr-1]; }
+
+  /** Referece to columne index of nr-th non-zero 
+    element in row i */
+  const INDEX & GetColIndexRef (INDEX i, int nr) const
+    { return graph->lines.Get(i).col[nr-1]; }
+
+  /** Value of nr-th non-zero element in row i */
+  double GetData (INDEX i, int nr) const 
+    { return data.Get(i)[nr-1]; }
+
+  /** Reference to value of nr-th non-zero element in row i */
+  const double & GetDataRef (INDEX i, int nr) const
+    { return data.Get(i)[nr-1]; }
+
+  /** Returns value of diagonal element in row i */
+  double GetDiag (INDEX i) const 
+    { return data.Get(i)[graph->lines.Get(i).diag-1]; }
+
+  /** Which position has diagonal element in row i ? */
+  int GetDiagPos (INDEX i) const
+    { return graph->lines.Get(i).diag; }
+
+  /** Returns matrix value of row i, col j.
+    For symmetric matrices the indices will be sorted in
+    this function */
+  double Get(INDEX i, INDEX j) const;
+
+  /** Set value of element (i, j) to v.
+    For symmetric matrices element (j, i) is set. */
+  void Set(INDEX i, INDEX j, double v);
+
+  /** Returns reference to element (i, j).
+    For symmetric matrices a reference to (j, i) is returned */
+  double & Elem(INDEX i, INDEX j);
+
+  /** Is element (i, j) used ? */
+  char Used (INDEX i, INDEX j) const;
+
+protected:
+  /// A sparse matrix must not be constructed
+  SparseMatrix (INDEX h, INDEX w = 0);
+  /// Allocates matrix position
+  virtual int CreatePosition (INDEX i, INDEX j) = 0;
+
+private:
+  ///
+  friend class ScalarBlockJacobiPrecond;
+  friend class PointJacobiPrecond;
+};
+
+
+
+
+/** Sparse matrix with flexible graph.
+  On demand, a matrix position is allocated */
+class SparseMatrixFlex : public SparseMatrix
+{ 
+ /// non-const pointer to graph.
+ MatrixGraph * mygraph;
+
+public:
+  ///
+  SparseMatrixFlex ();
+  ///
+  SparseMatrixFlex (INDEX h, INDEX w = 0);
+  ///
+  SparseMatrixFlex (const SparseMatrix & m2);
+  ///
+  virtual ~SparseMatrixFlex ();
+
+  /// 
+  virtual void SetSize (INDEX h, INDEX w = 0);
+  ///
+  virtual void SetSymmetric (int sym = 1);
+  ///
+  virtual void ChangeSize (INDEX h, INDEX w = 0);
+  ///
+  void DeleteElements ();
+
+
+  ///
+  SparseMatrix & operator= (const SparseMatrix & m2);
+  ///
+  SparseMatrix & operator*= (double v);
+
+
+  ///
+  virtual BaseMatrix * Copy () const;
+  ///
+  void Delete (INDEX i, int nr);
+  ///
+  void DeleteElem (INDEX i, INDEX j);
+
+  ///
+  void SetLineAllocSize (INDEX i, int j);
+
+protected:
+  ///
+  virtual int CreatePosition (INDEX i, INDEX j);
+
+};
+
+
+
+
+/** Sparse matrix with fixed graph.
+  After construction of the matrix, the graph
+  must not be changed. */
+class SparseMatrixFix : public SparseMatrix
+{
+public:
+  ///
+  SparseMatrixFix (const MatrixGraph & agraph, int asymmetric = 0);
+  ///
+  virtual ~SparseMatrixFix ();
+  
+protected:
+  /// CreatePosition is not allowed for SparseMatrixFix -> error
+  virtual int CreatePosition (INDEX i, INDEX j);
+};
+
+
+#endif
+
+#endif
diff --git a/contrib/Netgen/libsrc/linalg/vector.cpp b/contrib/Netgen/libsrc/linalg/vector.cpp
new file mode 100644
index 0000000000..05b5a0a71e
--- /dev/null
+++ b/contrib/Netgen/libsrc/linalg/vector.cpp
@@ -0,0 +1,787 @@
+#ifdef abc
+#include <mystdlib.h>
+#include <linalg.hpp>
+#include <algorithm>
+
+namespace netgen
+{
+
+double BaseVector :: shit = 0;
+
+// %FD Constructs a vector of length zero
+BaseVector :: BaseVector ()
+  {
+  length = 0;
+  }
+
+// %FD Constructs a vector of given length
+BaseVector :: BaseVector (
+    INDEX alength  // length of the vector
+    )
+  {
+  length = alength;
+  }
+
+// %FD Sets length of the vector, old vector will be destroyed
+void
+BaseVector :: SetLength (
+    INDEX alength        // new length of the vector
+    )
+  {
+  length = alength;
+  }
+
+// %FD Changes length of the vector, old values stay valid
+void
+BaseVector :: ChangeLength (
+    INDEX alength        // new length of the vector
+    )
+  {
+  length = alength;
+  }
+
+
+
+// %FD { Write a vector with the help of the '<<' operator onto a stream }
+ostream &    // stream for further use
+operator<< (
+    ostream & s,            // stream to write vector onto
+    const BaseVector & v   // vector to write
+    )
+  {
+  return v.Print (s);
+  }
+
+
+// %FD{ Divides every component of the vector by the scalar c.
+//      The function checks for division by zero }
+BaseVector &      // result vector
+BaseVector :: operator/= (
+    double c       // scalar to divide by
+    )
+  {
+  if (c)
+    return (*this) *= (1/c);
+  else
+    {
+    (*myerr) << "operator/=: division by zero" << endl;
+    return *this;
+    }
+  }
+
+
+// %FD Creates a copy of the object
+BaseVector *      // pointer to the new vector
+BaseVector :: Copy () const
+  {
+  cerr << "Base_vector::Copy called" << endl << flush;
+  return NULL;
+  }
+
+
+
+
+void BaseVector :: GetElementVector (const ARRAY<INDEX> & pnum,
+				 BaseVector & elvec) const
+{
+  int i;
+  for (i = 1; i <= pnum.Size(); i++)
+    elvec(i) = (*this)(pnum.Get(i));
+}
+
+void BaseVector :: SetElementVector (const ARRAY<INDEX> & pnum,
+				 const BaseVector & elvec)
+{
+  int i;
+  for (i = 1; i <= pnum.Size(); i++)
+    (*this)(pnum.Get(i)) = elvec(i);
+}
+
+
+void BaseVector :: AddElementVector (const ARRAY<INDEX> & pnum,
+				 const BaseVector & elvec)
+{
+  int i;
+  for (i = 1; i <= pnum.Size(); i++)
+    (*this)(pnum.Get(i)) += elvec(i);
+}
+
+
+
+
+
+
+
+
+
+
+
+TempVector :: ~TempVector ()
+  {
+  delete vec;
+  }
+
+TempVector BaseVector :: operator+ (const BaseVector & v2) const
+  {
+  return (*Copy()) += v2;
+  }
+
+TempVector BaseVector :: operator- (const BaseVector & v2) const
+  {
+  return (*Copy()) -= v2;
+  }
+
+TempVector BaseVector :: operator- () const
+  {
+  return (*Copy()) *= -1;
+  }
+
+
+TempVector operator* (const BaseVector & v1, double scal) 
+  {
+  return (*v1.Copy()) *= scal;
+  }
+
+TempVector operator/ (const BaseVector & v1, double scal) 
+  {
+  return (*v1.Copy()) /= scal;
+  }
+
+
+TempVector operator* (double scal, const BaseVector & v1)
+  {
+  return v1 * scal;
+  }
+
+
+
+
+
+BaseVector * TempVector :: Copy () const
+  {
+  return vec->Copy();
+  }
+
+
+
+
+
+
+
+
+
+
+double Vector :: shit = 0;
+
+class clVecpool
+{
+public:
+  ARRAY<double *> vecs;
+  ARRAY<INDEX> veclens;
+
+  ~clVecpool();
+};
+
+clVecpool :: ~clVecpool()
+{
+  int i;
+  for (i = 1; i <= vecs.Size(); i++)
+    delete vecs.Elem(i);
+}
+
+static clVecpool vecpool;
+
+
+
+static double * NewDouble (INDEX len)
+{
+  if (len < 10)
+    return new double[len];
+  else
+    {
+      int i;
+      for (i = 1; i <= vecpool.veclens.Size(); i++)
+	if (vecpool.veclens.Get(i) == len)
+	  {
+	    double * hvec = vecpool.vecs.Get(i);
+	    vecpool.vecs.DeleteElement(i);
+	    vecpool.veclens.DeleteElement(i);
+	    return hvec;
+	  }
+
+      return new double[len];
+    }
+}
+
+static void DeleteDouble (INDEX len, double * dp)
+{
+  if (len < 10)
+    delete [] dp;
+  else
+    {
+      vecpool.vecs.Append (dp);
+      vecpool.veclens.Append (len);
+    }
+}
+
+
+
+Vector :: Vector () : BaseVector()
+  {
+  data = NULL;
+  }
+
+Vector :: Vector (INDEX alength) : BaseVector (alength)
+  {
+  if (length)
+    {
+      //    data = new double[length];
+      data = NewDouble (length);
+
+    if (!data)
+      {
+      length = 0;
+      (*myerr) << "Vector not allocated" << endl;
+      }
+    }
+  else
+    data = NULL;
+  }
+
+
+Vector :: Vector (const Vector & v2)
+  {
+  length = v2.length;
+
+  if (length)
+    {
+      //    data = new double[length];
+      data = NewDouble (length);
+
+    if (data)
+      {
+      memcpy (data, v2.data, length * sizeof (double));
+      }
+    else
+      {
+      length = 0;
+      (*myerr) << "Vector::Vector : Vector not allocated" << endl;
+      }
+    }
+  else
+    data = NULL;
+  }
+
+
+Vector :: ~Vector ()
+{
+  //  veclenfile << "~Vector delete: " << length << endl;
+  if (data) 
+    {
+      DeleteDouble (length, data);
+      //      delete [] data;
+    }
+
+}
+
+void Vector :: SetLength (INDEX alength)
+  {
+  if (length == alength) return;
+
+  if (data) 
+    {
+      DeleteDouble (length, data);
+      //      delete [] data;
+    }
+  data = NULL;
+  length = alength;
+
+  if (length == 0) return;
+  //  data = new double[length];
+  data = NewDouble (length);
+
+  if (!data)
+    {
+    length = 0;
+    (*myerr) << "Vector::SetLength: Vector not allocated" << endl;
+    }
+  }
+
+void Vector :: ChangeLength (INDEX alength)
+{
+  (*mycout) << "Vector::ChangeLength called" << endl;
+  if (length == alength) return;
+  
+  if (alength == 0)
+    {
+      //    delete [] data;
+      DeleteDouble (length, data);
+      length = 0;
+      return;
+    }
+  
+  double * olddata = data;
+
+  data = NewDouble (alength);
+  //  data = new double[alength];
+  if (!data)
+    {
+    length = 0;
+    (*myerr) << "Vector::SetLength: Vector not allocated" << endl;
+    delete [] olddata;
+    }
+
+  memcpy (data, olddata, min2(alength, length));
+
+  delete [] olddata;
+  length = alength;
+  }
+
+/// NEW RM
+void Vector::SetBlockLength (INDEX /* blength */)
+{
+  MyError("BaseVector::SetBlockLength was called for a Vector");
+}
+
+
+double & Vector :: operator() (INDEX i)
+  {
+  if (i >= 1 && i <= length) return Elem(i);
+  else (*myerr) << "\nindex " << i << " out of range ("
+                                << 1 << "," << Length() << ")\n";
+  return shit;
+  }
+
+double Vector :: operator() (INDEX i) const
+  {
+  if (i >= 1 && i <= length) return Get(i);
+  else (*myerr) << "\nindex " << i << " out of range ("
+                                << 1 << "," << Length() << ")\n" << flush;
+  return shit;
+  }
+
+
+
+double Vector :: SupNorm () const
+  {
+  double sup = 0;
+
+  for (INDEX i = 1; i <= Length(); i++)
+    if (fabs (Get(i)) > sup)
+      sup = fabs(Get(i));
+
+  return sup;
+  }
+
+double Vector :: L2Norm () const
+  {
+  double sum = 0;
+
+  for (INDEX i = 1; i <= Length(); i++)
+    sum += Get(i) * Get(i);
+
+  return sqrt (sum);
+  }
+
+double Vector :: L1Norm () const
+  {
+  double sum = 0;
+
+  for (INDEX i = 1; i <= Length(); i++)
+    sum += fabs (Get(i));
+
+  return sum;
+  }
+
+double Vector :: Max () const
+  {
+  if (!Length()) return 0;
+  double m = Get(1);
+  for (INDEX i = 2; i <= Length(); i++)
+    if (Get(i) > m) m = Get(i);
+  return m;
+  }
+
+double Vector :: Min () const
+  {
+  if (!Length()) return 0;
+  double m = Get(1);
+  for (INDEX i = 2; i <= Length(); i++)
+    if (Get(i) < m) m = Get(i);
+  return m;
+  }
+
+
+/*
+ostream & operator<<(ostream & s, const Vector & v)
+  {
+  int w = s.width();
+  if (v.Length())
+    {
+    s.width(0);
+    s << '(';
+    for (INDEX i = 1; i < v.Length(); i++)
+      {
+      s.width(w);
+      s << v.Get(i) << ",";
+      if (i % 8 == 0) s << endl << ' ';
+      }
+    s.width(w);
+    s << v.Get(v.Length()) << ')';
+    }
+  else
+    s << "(Vector not allocated)";
+
+  return s;
+  }
+*/
+
+ostream & Vector :: Print (ostream & s) const
+  {
+  int w = s.width();
+  if (Length())
+    {
+    s.width(0);
+    s << '(';
+    for (INDEX i = 1; i < Length(); i++)
+      {
+      s.width(w);
+      s << Get(i) << ",";
+      if (i % 8 == 0) s << endl << ' ';
+      }
+    s.width(w);
+    s << Get(Length()) << ')';
+    }
+  else
+    s << "(Vector not allocated)";
+
+  return s;
+  }
+
+
+
+BaseVector & Vector :: operator+= (const BaseVector & v2)
+  {
+  const Vector & hv2 = v2.CastToVector();
+
+  if (Length() == hv2.Length())
+    for (INDEX i = 1; i <= Length(); i++)
+      Elem (i) += hv2.Get(i);
+  else
+    (*myerr) << "operator+= illegal dimension" << endl;
+  return *this;
+  }
+
+BaseVector & Vector :: operator-= (const BaseVector & v2)
+  {
+  const Vector & hv2 = v2.CastToVector();
+
+  if (Length() == hv2.Length())
+    for (INDEX i = 1; i <= Length(); i++)
+      Elem (i) -= hv2.Get(i);
+  else
+    (*myerr) << "operator-= illegal dimension" << endl;
+  return *this;
+  }
+
+BaseVector & Vector :: operator*= (double c)
+  {
+  for (INDEX i = 1; i <= Length(); i++)
+    Elem(i) *= c;
+  return *this;
+  }
+
+
+
+BaseVector & Vector :: Add (double scal, const BaseVector & v2)
+  {
+  const Vector & hv2 = v2.CastToVector();
+
+  if (Length() == hv2.Length())
+    {
+    double * p1 = data;
+    double * p2 = hv2.data;
+
+    for (INDEX i = Length(); i > 0; i--)
+      {
+      (*p1) += scal * (*p2);
+      p1++; p2++;
+      }
+    }
+  else
+    (*myerr) << "Vector::Add: illegal dimension" << endl;
+  return *this;
+  }
+
+BaseVector & Vector :: Add2 (double scal, const BaseVector & v2,
+                             double scal3, const BaseVector & v3)
+  {
+  const Vector & hv2 = v2.CastToVector();
+  const Vector & hv3 = v3.CastToVector();
+
+  if (Length() == hv2.Length())
+    {
+    double * p1 = data;
+    double * p2 = hv2.data;
+    double * p3 = hv3.data;
+
+    for (INDEX i = Length(); i > 0; i--)
+      {
+      (*p1) += (scal * (*p2) + scal3 * (*p3));
+      p1++; p2++; p3++;
+      }
+    }
+  else
+    (*myerr) << "Vector::Add: illegal dimension" << endl;
+  return *this;
+  }
+
+BaseVector & Vector :: Set (double scal, const BaseVector & v2)
+  {
+  const Vector & hv2 = v2.CastToVector();
+
+  if (Length() == hv2.Length())
+    {
+    double * p1 = data;
+    double * p2 = hv2.data;
+
+    for (INDEX i = Length(); i > 0; i--)
+      {
+      (*p1) = scal * (*p2);
+      p1++; p2++;
+      }
+    }
+  else
+    (*myerr) << "Vector::Set: illegal dimension" << endl;
+  return *this;
+  }
+
+
+BaseVector & Vector :: Set2 (double scal , const BaseVector & v2,
+      double scal3, const BaseVector & v3)
+  {
+  const Vector & hv2 = v2.CastToVector();
+  const Vector & hv3 = v3.CastToVector();
+
+
+  if (Length() == hv2.Length())
+    {
+    double * p1 = data;
+    double * p2 = hv2.data;
+    double * p3 = hv3.data;
+
+    for (INDEX i = Length(); i > 0; i--)
+      {
+      (*p1) = scal * (*p2) + scal3 * (*p3);
+      p1++; p2++; p3++;
+      }
+    }
+  else
+    (*myerr) << "Vector::Set: illegal dimension" << endl;
+  return *this;
+  }
+
+
+void Vector :: GetPart (int startpos, BaseVector & v2) const
+{
+  Vector & hv2 = v2.CastToVector();
+
+  if (Length() >= startpos + v2.Length() - 1)
+    {
+      const double * p1 = &Get(startpos);
+      double * p2 = &hv2.Elem(1);
+      
+      memcpy (p2, p1, hv2.Length() * sizeof(double));
+    }
+  else
+    MyError ("Vector::GetPart: Vector to short");
+}
+
+
+// NEW RM
+void Vector :: SetPart (int startpos, const BaseVector & v2)
+{
+  const Vector & hv2 = v2.CastToVector();
+  INDEX i;
+  INDEX n = v2.Length();
+
+  if (Length() >= startpos + n - 1)
+    {
+      double * p1 = &Elem(startpos);
+      const double * p2 = &hv2.Get(1);
+
+      for (i = 1; i <= n; i++)
+	{
+	  (*p1) = (*p2);
+	  p1++;
+	  p2++;
+	}
+    }
+  else
+    MyError ("Vector::SetPart: Vector to short");
+}
+
+void Vector :: AddPart (int startpos, double val, const BaseVector & v2)
+{
+  const Vector & hv2 = v2.CastToVector();
+  INDEX i;
+  INDEX n = v2.Length();
+
+  if (Length() >= startpos + n - 1)
+    {
+      double * p1 = &Elem(startpos);
+      const double * p2 = &hv2.Get(1);
+
+      for (i = 1; i <= n; i++)
+	{
+	  (*p1) += val * (*p2);
+	  p1++;
+	  p2++;
+	}
+    }
+  else
+    MyError ("Vector::AddPart: Vector to short");
+}
+
+
+
+
+double Vector :: operator* (const BaseVector & v2) const
+  {
+  const Vector & hv2 = v2.CastToVector();
+
+  double sum = 0;
+  double * p1 = data;
+  double * p2 = hv2.data;
+
+  if (Length() == hv2.Length())
+    {
+    for (INDEX i = Length(); i > 0; i--)
+      {
+      sum += (*p1) * (*p2);
+      p1++; p2++;
+      }
+    }
+  else
+    (*myerr) << "Scalarproduct illegal dimension" << endl;
+  return sum;
+  }
+
+void Vector :: SetRandom ()
+  {
+  INDEX i;
+  for (i = 1; i <= Length(); i++)
+    Elem(i) = rand ();
+
+  double l2 = L2Norm();
+  if (l2 > 0)
+    (*this) /= l2;
+    //    Elem(i) = 1.0 / double(i);
+    //    Elem(i) = drand48();
+  }
+
+
+/*
+TempVector Vector :: operator- () const
+  {
+  Vector & sum = *(Vector*)Copy();
+
+  if (sum.Length () == Length())
+    {
+    for (INDEX i = 1; i <= Length(); i++)
+      sum.Set (i, Get(i));
+    }
+  else
+    (*myerr) << "operator+ (Vector, Vector): sum.Length() not ok" << endl;
+  return sum;
+  }
+*/
+
+BaseVector & Vector::operator= (const Vector & v2)
+  {
+  SetLength (v2.Length());
+
+  if (data == v2.data) return *this;
+  
+  if (v2.Length() == Length())
+    memcpy (data, v2.data, sizeof (double) * Length());
+  else
+    (*myerr) << "Vector::operator=: not allocated" << endl;
+
+  return *this;
+  }
+
+BaseVector & Vector::operator= (const BaseVector & v2)
+  {
+  const Vector & hv2 = v2.CastToVector();
+
+  SetLength (hv2.Length());
+
+  if (data == hv2.data) return *this;
+  
+  if (hv2.Length() == Length())
+    memcpy (data, hv2.data, sizeof (double) * Length());
+  else
+    (*myerr) << "Vector::operator=: not allocated" << endl;
+
+  return *this;
+  }
+
+
+BaseVector & Vector::operator= (double scal)
+  {
+  if (!Length()) (*myerr) << "Vector::operator= (double) : data not allocated"
+                      << endl;
+
+  for (INDEX i = 1; i <= Length(); i++)
+    Set (i, scal);
+
+  return *this;
+  }
+
+
+BaseVector * Vector :: Copy () const
+  {
+  return new Vector (*this);
+  }
+
+
+void Vector :: Swap (BaseVector & v2)
+  {
+  Vector & hv2 = v2.CastToVector();
+  swap (length, hv2.length);
+  swap (data, hv2.data);
+  }
+
+
+
+
+void Vector :: GetElementVector (const ARRAY<INDEX> & pnum,
+				 BaseVector & elvec) const
+{
+  int i;
+  Vector & helvec = elvec.CastToVector();
+  for (i = 1; i <= pnum.Size(); i++)
+    helvec.Elem(i) = Get(pnum.Get(i));
+}
+
+void Vector :: SetElementVector (const ARRAY<INDEX> & pnum,
+				 const BaseVector & elvec)
+{
+  int i;
+  const Vector & helvec = elvec.CastToVector();
+  for (i = 1; i <= pnum.Size(); i++)
+    Elem(pnum.Get(i)) = helvec.Get(i);
+}
+
+
+void Vector :: AddElementVector (const ARRAY<INDEX> & pnum,
+				 const BaseVector & elvec)
+{
+  int i;
+  const Vector & helvec = elvec.CastToVector();
+  for (i = 1; i <= pnum.Size(); i++)
+    Elem(pnum.Get(i)) += helvec.Get(i);
+}
+}
+#endif
diff --git a/contrib/Netgen/libsrc/linalg/vector.hpp b/contrib/Netgen/libsrc/linalg/vector.hpp
new file mode 100644
index 0000000000..acba491792
--- /dev/null
+++ b/contrib/Netgen/libsrc/linalg/vector.hpp
@@ -0,0 +1,485 @@
+#ifndef FILE_VECTOR
+#define FILE_VECTOR
+
+/* *************************************************************************/
+/* File:   vector.hh                                                      */
+/* 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.data[i];
+    return *this;
+  }
+
+  FlatVector & Set (double scal, const FlatVector & v2)
+  {
+    for (int i = 0; i < s; i++) 
+      data[i] = scal * v2.data[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.data[i] + scal2 * v2.data[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
+{
+
+public:
+  Vector () 
+  { s = 0; data = 0; }
+  Vector (int as)
+  { s = as; data = new double[s]; }
+  ~Vector ()
+  { delete [] data; }
+
+  Vector & operator= (const FlatVector & v) 
+  { memcpy (data, &v.Get(1), 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;
+	delete [] data;
+	data = new double [s];
+      }
+  }
+
+};
+
+
+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;
+}
+
+
+
+
+
+
+
+
+
+
+#ifdef OLDVEC
+
+class TempVector;
+class Vector;
+class BlockVector;
+
+/** Data types for vectors.
+   
+   Every Vector data structure is derived from a BaseVector class.
+   A BaseVector provides virtual functions for the scalar-vector
+   and vector-vector operations. 
+   If the return value of a function is a vector, then there should
+   be used a TempVector class. This avoids one additional constructor/
+   destructor call.
+   Finally, a Vector - class contains the data of a Vector in dense
+   form.
+   
+   Vector - Operations:
+   
+     Vector ( x )       Constructor empty, with given length or 
+                        given vector to copy  
+                       
+     SetLength () 
+     ChangeLength()     Change vector-length with/without destroing the vector
+     Length()           return vector-length
+     
+     Copy()             Construct a vector of same type and contents
+     
+     operator()         Save vector access
+     Set, Get, Elem:    Fast vector access for setting, receiving and reference
+
+     +, -, *, =, +=, -=, *=, /=
+     			virtual vector operations
+                     
+     SupNorm, L2Norm, L1Norm, Min, Max
+			Vector operations 
+			
+     Set (s, v), Add (s, v)
+     			Fast scalar-vector operations
+     			
+     Print ()           stream output of a vector
+
+*/
+
+class BaseVector
+{
+protected:
+  ///
+  INDEX length;
+  ///
+  static double shit;
+  
+public:
+  ///
+  BaseVector ();
+  ///
+  BaseVector (INDEX alength);
+  ///
+  virtual ~BaseVector () { };
+  ///
+  virtual void SetSize (INDEX asize) { SetLength(asize); }
+  ///
+  virtual void SetLength (INDEX alength);
+  ///
+  virtual void ChangeLength (INDEX alength);
+  /// Size should be prefered !!!
+  INDEX Length () const { return length; }
+  /// 
+  INDEX Size() const { return length; }
+
+    // NEW RM ---> in BlockVector
+    // rtual void SetBlockLength (INDEX blength) = 0;
+
+  ///
+  virtual BaseVector & operator= (const BaseVector & /* v2 */) { return *this; }
+  ///
+  virtual BaseVector & operator= (double /* scal */) { return *this; }
+
+  ///
+  virtual double & operator() (INDEX /* i */) { return shit; }
+  ///
+  virtual double operator() (INDEX /* i */) const { return 0; }
+
+  ///
+  virtual double SupNorm () const = 0;
+  ///
+  virtual double L2Norm () const = 0;
+  ///
+  virtual double L1Norm () const = 0;
+  ///
+  virtual double Min () const = 0;
+  ///
+  virtual double Max () const = 0;
+
+  ///
+  virtual BaseVector & operator+= (const BaseVector & v2) = 0;
+  ///
+  virtual BaseVector & operator-= (const BaseVector & v2) = 0;
+  ///
+  virtual BaseVector & operator*= (double scal) = 0;
+  ///
+  virtual BaseVector & operator/= (double scal);
+
+  ///
+  virtual TempVector operator+ (const BaseVector & v2) const;
+  ///
+  virtual TempVector operator- (const BaseVector & v2) const;
+  ///
+  virtual TempVector operator- () const;
+  ///
+  virtual double operator* (const BaseVector & v2) const = 0;
+  ///
+  friend TempVector operator* (const BaseVector & v1, double scal);
+  ///
+  friend TempVector operator/ (const BaseVector & v1, double scal);
+  ///
+  friend TempVector operator* (double scal, const BaseVector & v1);
+
+  ///
+  virtual BaseVector & Add (double /* scal */, const BaseVector & /* v2 */) { return *this; }
+  ///
+  virtual BaseVector & Add2 (double /* scal */, const BaseVector & /* v2 */,
+                        double /* scal3 */, const BaseVector & /* v3 */) { return *this; }
+  ///
+  virtual BaseVector & Set (double /* scal */, const BaseVector & /* v2 */) { return *this; }
+  ///
+  virtual BaseVector & Set2 (double /* scal */, const BaseVector & /* v2 */,
+                        double /* scal3 */, const BaseVector & /* v3 */) { return *this; }
+
+    ///
+    virtual void SetRandom () { };
+
+    
+    ///
+    virtual void GetPart (int /* startpos */, 
+			  BaseVector & /* v2*/ ) const { };
+    ///
+    virtual void SetPart (int /* startpos */,  
+			  const BaseVector & /* v2 */) { };
+    ///
+    virtual void AddPart (int /* startpos */, double /* val */, 
+			  const BaseVector & /* v2 */) { };
+
+  ///
+  virtual void GetElementVector (const ARRAY<INDEX> & pnum,
+				 BaseVector & elvec) const;
+  ///
+  virtual void SetElementVector (const ARRAY<INDEX> & pnum,
+				 const BaseVector & elvec);
+  ///
+  virtual void AddElementVector (const ARRAY<INDEX> & pnum,
+				 const BaseVector & elvec);
+
+  ///
+  friend ostream & operator<<(ostream & s, const BaseVector & v);
+  ///
+  virtual ostream & Print (ostream & s) const { return s; }
+
+  ///
+  virtual BaseVector * Copy () const;
+  /// 
+  virtual int IsVector () const { return 0; }
+  ///
+  virtual int IsBlockVector () const { return 0; }
+  ///
+  virtual Vector & CastToVector () { return *(Vector*)this; }
+  ///
+  virtual const Vector & CastToVector () const { return *(Vector*)this; }
+  ///
+  virtual BlockVector & CastToBlockVector () { return *(BlockVector*)this; }
+  ///
+  virtual const BlockVector & CastToBlockVector () const { return *(BlockVector*)this; }
+  };
+
+
+///
+class TempVector : public BaseVector
+{
+  ///
+  BaseVector * vec;
+
+  public:
+  ///
+  TempVector (BaseVector & v1) { vec = & v1; }
+  ///
+  ~TempVector ();
+  ///
+  virtual Vector & CastToVector ()
+      { return vec->CastToVector(); }
+  ///
+  virtual const Vector & CastToVector () const
+      { return vec->CastToVector(); }
+  ///
+  virtual BlockVector & CastToBlockVector ()
+      { return vec->CastToBlockVector(); }
+  ///
+  virtual const BlockVector & CastToBlockVector () const
+      { return vec->CastToBlockVector(); }
+
+
+  ///
+  virtual BaseVector & operator+= (const BaseVector & /* v2 */) { return *this; }
+  ///
+  virtual BaseVector & operator-= (const BaseVector & /* v2 */) { return *this; }
+  ///
+  virtual BaseVector & operator*= (double /* scal */) { return *this; }
+  ///
+  virtual double operator* (const BaseVector & /* v2 */) const { return 0; }
+
+  ///
+  virtual double SupNorm () const { return vec->SupNorm(); }
+  ///
+  virtual double L2Norm () const { return vec->L2Norm(); }
+  ///
+  virtual double L1Norm () const { return vec->L1Norm(); }
+  ///
+  virtual double Min () const { return vec->Min(); }
+  ///
+  virtual double Max () const { return vec->Max(); }
+
+  ///
+  virtual void Swap (BaseVector &) { };
+  ///
+  virtual BaseVector * Copy () const;
+
+
+  };
+
+
+///
+class Vector : public BaseVector
+{
+  ///
+  double * data;
+  ///
+  static double shit;
+  
+public:
+  ///
+  Vector ();
+  ///
+  Vector (INDEX alength);
+  ///
+  Vector (const Vector & v2);
+  ///
+  virtual ~Vector ();
+
+  ///
+  virtual void SetLength (INDEX alength);
+  ///
+  virtual void ChangeLength (INDEX alength);
+  /// NEW RM
+  virtual void SetBlockLength (INDEX blength);
+
+  ///
+  virtual BaseVector & operator= (const BaseVector & v2);
+  ///
+  virtual BaseVector & operator= (const Vector & v2);
+  ///
+  virtual BaseVector & operator= (double scal);
+
+  ///
+  double & operator() (INDEX i);
+  ///
+  double operator() (INDEX i) const;
+
+  ///
+  virtual double SupNorm () const;
+  ///
+  virtual double L2Norm () const;
+  ///
+  virtual double L1Norm () const;
+  ///
+  virtual double Min () const;
+  ///
+  virtual double Max () const;
+
+  ///
+  virtual BaseVector & operator+= (const BaseVector & v2);
+  ///
+  virtual BaseVector & operator-= (const BaseVector & v2);
+  ///
+  virtual BaseVector & operator*= (double scal);
+
+  ///
+  virtual double operator* (const BaseVector & v2) const;
+
+
+  ///
+  virtual BaseVector & Add (double scal, const BaseVector & v2);
+  ///
+  virtual BaseVector & Add2 (double scal, const BaseVector & v2,
+                        double scal3, const BaseVector & v3);
+  ///
+  virtual BaseVector & Set (double scal, const BaseVector & v2);
+  ///
+  virtual BaseVector & Set2 (double scal , const BaseVector & v2,
+                        double scal3, const BaseVector & v3);
+
+    ///
+    virtual void GetPart (int startpos, BaseVector & v2) const;
+    ///
+    virtual void SetPart (int startpos, const BaseVector & v2);
+    ///
+    virtual void AddPart (int startpos, double val, const BaseVector & v2);
+
+  ///
+  virtual void SetRandom ();
+
+  ///
+  virtual ostream & Print (ostream & s) const;
+  ///
+  virtual BaseVector * Copy () const;
+  ///
+  virtual void Swap (BaseVector &);
+
+  ///
+  const double & Get (INDEX i) const { return data[i-1]; }
+  ///
+  void Set (INDEX i, double v) { data[i-1] = v; }
+  ///
+  double & Elem (INDEX i) { return data[i-1]; }
+
+  ///
+  virtual int IsVector () const { return 1; }
+
+
+  ///
+  virtual void GetElementVector (const ARRAY<INDEX> & pnum,
+				 BaseVector & elvec) const;
+  ///
+  virtual void SetElementVector (const ARRAY<INDEX> & pnum,
+				 const BaseVector & elvec);
+  ///
+  virtual void AddElementVector (const ARRAY<INDEX> & pnum,
+				 const BaseVector & elvec);
+  };
+
+#endif
+
+#endif
+
+
diff --git a/contrib/Netgen/libsrc/makefile.inc b/contrib/Netgen/libsrc/makefile.inc
new file mode 100644
index 0000000000..76369d5fb6
--- /dev/null
+++ b/contrib/Netgen/libsrc/makefile.inc
@@ -0,0 +1,48 @@
+#
+#
+# Make-Include-File for library
+# Joachim Schoeberl, 17.04.96
+#
+#
+CPP_DIR=../..
+LIBSRC_DIR=$(CPP_DIR)/libsrc
+LIB_DIR=$(CPP_DIR)/lib/$(MACHINE)
+
+OCC_DIR=../../occ
+OCCINC_DIR=$(OCC_DIR)/inc
+OCCLIB_DIR=$(OCC_DIR)/lib
+#
+include $(LIBSRC_DIR)/makefile.mach.$(MACHINE)
+#
+CPLUSPLUSFLAGS1 = -c -I$(LIBSRC_DIR)/include -I$(OCCINC_DIR) 
+#
+ARFLAGS = r
+#
+LIBB=$(LIB_DIR)/lib$(lib).a
+#
+.PRECIOUS: .cpp .c
+.SUFFIXES: .cpp .c .o 
+#
+.cpp.o:
+	$(CPLUSPLUS) $(CPLUSPLUSFLAGS1) $(CPLUSPLUSFLAGS2) $(CPLUSPLUSFLAGSLIBRARY) $<
+.c.o:
+	$(CPLUSPLUS) $(CPLUSPLUSFLAGS1) $(CPLUSPLUSFLAGS2) $(CPLUSPLUSFLAGSLIBRARY) $<
+#
+#
+$(LIBB):: $(LIB_DIR) 
+#
+# make lib from sources:
+#
+$(LIBB):: $(src) 
+	$(CPLUSPLUS) $(CPLUSPLUSFLAGS1) $(CPLUSPLUSFLAGS2) $(CPLUSPLUSFLAGSLIBRARY)  $?
+	@$(AR) $(ARFLAGS) $@ *.o
+	-@$(RM) *.o
+	-@$(RANLIB) $@
+#
+#
+#
+$(LIB_DIR) :
+	-@mkdir $(CPP_DIR)/lib
+	@mkdir $(LIB_DIR)
+#
+#
diff --git a/contrib/Netgen/libsrc/makefile.mach.FREEBSD b/contrib/Netgen/libsrc/makefile.mach.FREEBSD
new file mode 100644
index 0000000000..6f903d4e60
--- /dev/null
+++ b/contrib/Netgen/libsrc/makefile.mach.FREEBSD
@@ -0,0 +1,26 @@
+#
+# Machine dependent make include file for gcc
+# 
+#
+#CC=gcc
+CPLUSPLUS=$(CC)
+AR=ar
+LINK=$(CC)
+#MAKE=make
+RM=rm
+RANLIB=ranlib
+#
+# Machine dependent flags:
+#
+include $(LOCALBASE)/lib/tixConfig.sh
+include $(LOCALBASE)/lib/tcl$(TCL_VER)/tclConfig.sh
+include $(LOCALBASE)/lib/tk$(TK_VER)/tkConfig.sh
+tcltklib = `echo $(TIX_BUILD_LIB_SPEC)` `echo $(TK_LIB_SPEC)` `echo $(TCL_LIB_FLAG)`
+
+CFLAGS2 =
+CPLUSPLUSFLAGS2 = $(CXXFLAGS) -I$(X11BASE)/include -DLINUX -DOPENGL
+CPLUSPLUSFLAGS3 = -I$(LIBSRC_DIR)/step `echo $(TCL_INCLUDE_SPEC)` `echo -I$(TK_PREFIX)`/include/tk`echo $(TK_VERSION)`
+#
+LINKFLAGS2 =  -L$(LOCALBASE)/lib -L$(X11BASE)/lib
+
+SYSLIB2 = -lstdc++
diff --git a/contrib/Netgen/libsrc/makefile.mach.INTEL b/contrib/Netgen/libsrc/makefile.mach.INTEL
new file mode 100644
index 0000000000..deef5b091c
--- /dev/null
+++ b/contrib/Netgen/libsrc/makefile.mach.INTEL
@@ -0,0 +1,34 @@
+#
+# Machine dependent make include file
+#
+CC=icc
+CPLUSPLUS=$(CC)
+AR=ar
+LINK=$(CC)
+MAKE=make
+RM=rm
+RANLIB=ranlib
+#
+# Machine dependent flags:
+#
+CFLAGS2 =
+CPLUSPLUSFLAGS2 = -O2 -wd1572 -Ob2 -DLINUX -DOPENGL -DNGSOLVE \
+	-Qoption,c,-ip_ninl_max_stats=1000 \
+	-Qoption,c,-ip_ninl_min_stats=50 \
+	-Qoption,c,-ip_ninl_max_total_stats=1000 \
+	-gcc-name=/usr/bin/g++
+#  
+LINKFLAGS2 =  -L/usr/openwin/lib -L/usr/X11R6/lib -L/usr/lib/GL3.5 \
+	-gcc-name=/usr/bin/g++
+#
+# SYSLIB2 = /opt/experimental/lib/libstdc++.a
+# SYSLIB2 = -lstdc++ 
+# -lgcc_s
+# SYSLIB2 = -L/usr/lib/lapack -lblas -lstdc++
+
+# goalngs = goalngs
+
+
+goalngs=goalngs
+
+appngs =  lib/$(MACHINE)/*.o -lngsolvebasic
\ No newline at end of file
diff --git a/contrib/Netgen/libsrc/makefile.mach.LINUX b/contrib/Netgen/libsrc/makefile.mach.LINUX
new file mode 100644
index 0000000000..f5df356167
--- /dev/null
+++ b/contrib/Netgen/libsrc/makefile.mach.LINUX
@@ -0,0 +1,31 @@
+#
+# Machine dependent make include file
+# 
+#
+# CC=/opt/gcc-dev/bin/gcc 
+# CC=/usr/local/bin/gcc
+CC=gcc
+CPLUSPLUS=$(CC)
+AR=ar
+LINK=$(CC)
+MAKE=make
+RM=rm
+RANLIB=ranlib
+#
+# Machine dependent flags:
+#
+CFLAGS2 =
+
+CPLUSPLUSFLAGS2 = -O2 -I/usr/include/GL3.5 -DLINUX -DOPENGL \
+	-ftemplate-depth-99 -finline-limit=20000 \
+	-funroll-loops  -DNGSOLVE
+
+LINKFLAGS2 =   -L/usr/openwin/lib -L/usr/X11R6/lib -L/usr/lib/GL3.5 -lstdc++ 
+
+
+goalngs=goalngs
+
+# lapack =  -llapack  -lblas -lgmp -lg2c
+
+
+appngs =  lib/$(MACHINE)/*.o -lngsolvebasic
\ No newline at end of file
diff --git a/contrib/Netgen/libsrc/makefile.mach.LINUXGCC33 b/contrib/Netgen/libsrc/makefile.mach.LINUXGCC33
new file mode 100644
index 0000000000..0a6ee84afb
--- /dev/null
+++ b/contrib/Netgen/libsrc/makefile.mach.LINUXGCC33
@@ -0,0 +1,33 @@
+#
+# Machine dependent make include file
+# 
+#
+# CC=/opt/gcc33/bin/gcc 
+# CC=/usr/local/bin/gcc
+CC=gcc
+CPLUSPLUS=$(CC)
+AR=ar
+LINK=$(CC)
+MAKE=make
+RM=rm
+RANLIB=ranlib
+#
+#
+CFLAGS2 =
+CPLUSPLUSFLAGS2 = -O2 -DLINUX -DOPENGL \
+	-ftemplate-depth-99 -finline-limit=20000 \
+	-mcpu=pentium4  -fforce-addr  -funroll-loops \
+	-DnoTRAFO -DNGSOLVE -DnoADDON -DnoPML -DnoLAPACK \
+	-DnoOCCGEOMETRY -I/usr/include/g++/backward -I./occ/inc -DnoDEBUG 
+#	
+#
+#  
+LINKFLAGS2 = -L/usr/openwin/lib -L/usr/X11R6/lib -L/usr/lib/GL3.5 
+
+SYSLIB2 =  -lstdc++ 
+
+goalngs   = goalngs
+
+appngs =  lib/$(MACHINE)/*.o -lngsolvebasic
+
+# occlib = -L$(OCCLIB_DIR) -lTKIGES -lTKBRep -lTKSTEP -lTKSTL
\ No newline at end of file
diff --git a/contrib/Netgen/libsrc/makefile.mach.SGI b/contrib/Netgen/libsrc/makefile.mach.SGI
new file mode 100644
index 0000000000..b75920ce26
--- /dev/null
+++ b/contrib/Netgen/libsrc/makefile.mach.SGI
@@ -0,0 +1,19 @@
+#
+# Machine dependent make include file
+# 
+CC=CC
+CPLUSPLUS=$(CC)
+AR=ar
+LINK=$(CC)
+MAKE=make -k
+RM=rm
+RANLIB=echo
+#
+CFLAGS2 =
+#
+CPLUSPLUSFLAGS2 = -O2 -LANG:std -OPT:Olimit=0 -I/usr/X11R6/include -I/usr/local/include -I/nfs/home/joachim/tcltk/include -DOPENGL -woff 1174 -woff 1682 -woff 1681 -woff 1552 -DOLDCINCLUDE
+# -woff 3262,3203,1174,1110
+#
+#  -I/usr/freeware/include
+LINKFLAGS2 = -LANG:std  -L/usr/openwin/lib -L/usr/freeware/lib32 -L/usr/X11R6/lib -L/usr/local/lib  -w -L/nfs/home/joachim/tcltk/lib
+
diff --git a/contrib/Netgen/libsrc/makefile.mach.SGIGCC b/contrib/Netgen/libsrc/makefile.mach.SGIGCC
new file mode 100644
index 0000000000..c43363c5e7
--- /dev/null
+++ b/contrib/Netgen/libsrc/makefile.mach.SGIGCC
@@ -0,0 +1,53 @@
+#
+# Machine dependent make include file
+# 
+#
+# CC=/opt/experimental/bin/gcc 
+CC=/usr/freeware/bin/gcc
+CPLUSPLUS=$(CC)
+AR=ar
+LINK=$(CC)
+MAKE=make
+RM=rm
+RANLIB=ranlib
+#
+# Machine dependent flags:
+#
+CFLAGS2 =
+# I/opt/experimental/include/g++-v3 
+CPLUSPLUSFLAGS2 = -O2 -save-temps -fverbose-asm -I/usr/local/include -I/usr/freeware/include -I/usr/freeware/include/tcl -I/usr/freeware/include/tk -I/usr/include/GL3.5 -DLINUX -DOPENGL -I../lapack/\
+	-ftemplate-depth-99
+#  -finline-limit=10000 
+#	-mcpu=pentiumpro -fforce-addr -Wdisabled-optimization  -funroll-loops 
+#   -funroll-all-loops   
+# -dr -dt -df
+#	-fforce-addr \
+#	-fssa -fdce -fschedule-insns2 -fstrict-aliasing -frename-registers \
+#	-freg-struct-return 
+#	-fargument-noalias-global -fargument-noalias \
+#	--param max-gcse-memory=100000000 \
+#	--param max-delay-slot-live-search=100000 \
+#	--param max-pending-list-length=10000 \
+#	-fssa \
+#	-fomit-frame-pointer -fno-enforce-eh-specs -fno-defer-pop \
+#	-fforce-mem -fstrict-aliasing \
+#	-fno-implement-inlines \
+#	-foptimize-sibling-calls \
+#	-frerun-cse-after-loop -fcse-follow-jumps -fexpensive-optimizations \
+#	-fstrength-reduce -frerun-loop-opt -fcse-skip-blocks -fgcse \
+#	  -pedantic \
+# -fno-implicit-templates
+#
+#  -I/usr/local/intel/mkl/INCLUDE
+# -fsyntax-only
+# -fomit-frame-pointer
+# -funroll-loops
+#  
+LINKFLAGS2 = -L/usr/local/lib -L/usr/openwin/lib -L/usr/X11R6/lib -L/usr/lib/GL3.5  -L/usr/freeware/lib32 -L/usr/X11R6/lib -L/usr/local/lib
+
+# SYSLIB2 = libstdc++.
+# SYSLIB2 = -lstdc++ 
+# -lgcc_s
+# SYSLIB2 = -L/usr/lib/lapack -lblas -lstdc++
+
+
diff --git a/contrib/Netgen/libsrc/makefile.mach.SUN b/contrib/Netgen/libsrc/makefile.mach.SUN
new file mode 100644
index 0000000000..f927ae06f1
--- /dev/null
+++ b/contrib/Netgen/libsrc/makefile.mach.SUN
@@ -0,0 +1,16 @@
+#
+# Machine dependent make include file
+# 
+CC=CC
+CPLUSPLUS=$(CC)
+AR=ar
+LINK=$(CC)
+MAKE=make -k
+RM=rm
+RANLIB=ranlib
+#
+CFLAGS2 =
+#
+CPLUSPLUSFLAGS2 =  -fast -O2 -I/usr/openwin/share/include 
+LINKFLAGS2 = -L/usr/openwin/lib 
+
diff --git a/contrib/Netgen/libsrc/meshing/Makefile b/contrib/Netgen/libsrc/meshing/Makefile
new file mode 100644
index 0000000000..c0f17db986
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/Makefile
@@ -0,0 +1,16 @@
+src = adfront2.cpp adfront3.cpp geomsearch.cpp global.cpp \
+        meshtool.cpp \
+        netrule2.cpp netrule3.cpp parser2.cpp parser3.cpp ruler2.cpp ruler3.cpp \
+        meshtype.cpp meshclass.cpp improve2.cpp smoothing2.cpp improve3.cpp smoothing3.cpp \
+	improve2gen.cpp meshing2.cpp meshing3.cpp  \
+	localh.cpp delaunay.cpp topology.cpp clusters.cpp \
+	tetrarls.cpp triarls.cpp quadrls.cpp meshfunc.cpp meshfunc2d.cpp \
+	refine.cpp bisect.cpp zrefine.cpp secondorder.cpp hprefinement.cpp \
+	boundarylayer.cpp specials.cpp msghandler.cpp \
+	pyramidrls.cpp pyramid2rls.cpp prism2rls.cpp curvedelems.cpp curvedelems2.cpp
+#
+lib = mesh
+libpath = libsrc/meshing
+#
+include ../makefile.inc
+#
diff --git a/contrib/Netgen/libsrc/meshing/adfront2.cpp b/contrib/Netgen/libsrc/meshing/adfront2.cpp
new file mode 100644
index 0000000000..01b591f477
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/adfront2.cpp
@@ -0,0 +1,526 @@
+/*
+  Advancing front class for surfaces
+*/
+
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+
+namespace netgen
+{
+
+  /*
+AdFront2::FrontPoint2 :: FrontPoint2 ()
+{
+  globalindex = 0;
+  nlinetopoint = 0;
+  frontnr = INT_MAX-10;    // attention: overflow on calculating  INT_MAX + 1
+  mgi = NULL;
+}
+  */
+
+AdFront2::FrontPoint2 :: FrontPoint2 (const Point3d & ap, PointIndex agi,
+				      MultiPointGeomInfo * amgi)
+{
+  p = ap;
+  globalindex = agi;
+  nlinetopoint = 0;
+  frontnr = INT_MAX-10;
+
+  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::FrontPoint2 :: ~FrontPoint2 ()
+{
+//  if (mgi) delete mgi;
+}
+*/
+
+
+AdFront2::FrontLine :: FrontLine ()
+{
+  lineclass = 1;
+}
+
+AdFront2::FrontLine :: FrontLine (const INDEX_2 & al)
+{
+  l = al;
+  lineclass = 1;
+}
+
+
+
+
+
+
+AdFront2 :: AdFront2 (const Box3d & aboundingbox)
+  : boundingbox(aboundingbox), 
+    linesearchtree(boundingbox.PMin(), boundingbox.PMax()),
+    cpointsearchtree(boundingbox.PMin(), boundingbox.PMax())
+{
+  nfl = 0;
+  //  allflines = new INDEX_2_HASHTABLE<int> (100000);
+  allflines = 0;
+
+  minval = 0;
+  starti = 1;
+}
+
+AdFront2 :: ~AdFront2 ()
+{
+  delete allflines;
+}
+
+
+void AdFront2 :: PrintOpenSegments (ostream & ost) const
+{
+ if (nfl > 0)
+    {
+      int i;
+      ost << nfl << " open front segments left:" << endl;
+      for (i = 1; i <= lines.Size(); i++)
+	if (lines.Get(i).Valid())
+	  ost << GetGlobalIndex (lines.Get(i).L().I1()) << "-"
+	      << GetGlobalIndex (lines.Get(i).L().I2()) << endl;
+	    
+    }
+}
+
+
+void AdFront2 :: GetPoints (ARRAY<Point3d> & apoints) const
+{
+  for (int i = 0; i < points.Size(); i++)
+    apoints.Append (points[i].P());
+}
+
+
+
+
+
+INDEX AdFront2 :: AddPoint (const Point3d & p, PointIndex globind, 
+			    MultiPointGeomInfo * mgi)
+{
+  // inserts at empty position or resizes array
+  int pi;
+
+  if (delpointl.Size() != 0)
+    {
+      pi = delpointl.Last();
+      delpointl.DeleteLast ();
+
+      points.Elem(pi) = FrontPoint2 (p, globind, mgi);
+    }
+  else
+    {
+      pi = points.Append (FrontPoint2 (p, globind, mgi));
+    }
+
+  if (mgi)
+    {
+      cpointsearchtree.Insert (p, pi);
+    }
+
+  return pi;
+}
+
+
+INDEX AdFront2 :: AddLine (INDEX pi1, INDEX pi2,
+			   const PointGeomInfo & gi1, const PointGeomInfo & gi2)
+{
+  int minfn;
+  INDEX li;
+
+  FrontPoint2 & p1 = points.Elem(pi1);
+  FrontPoint2 & p2 = points.Elem(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.Elem(li) = FrontLine (INDEX_2(pi1, pi2));
+    }
+  else
+    {
+      li = lines.Append(FrontLine (INDEX_2(pi1, pi2)));
+    }
+
+  
+  if (!gi1.trignum || !gi2.trignum)
+    {
+      cout << "ERROR: in AdFront::AddLine, illegal geominfo" << endl;
+    }
+  
+  lines.Elem(li).SetGeomInfo (gi1, gi2);
+
+  Box3d lbox;
+  lbox.SetPoint(p1.P());
+  lbox.AddPoint(p2.P());
+
+  linesearchtree.Insert (lbox.PMin(), lbox.PMax(), li);
+
+  /*
+  (*testout) << "add front line: " << p1.P() << " - " << p2.P()
+  	     << " Dist = " << Dist (p1.P(), p2.P()) << endl;
+  */
+
+  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 (INDEX li)
+{
+  int i;
+  INDEX pi;
+
+  nfl--;
+
+  for (i = 1; i <= 2; i++)
+    {
+      pi = lines.Get(li).L().I(i);
+      points.Elem(pi).RemoveLine();
+
+      if (!points.Get(pi).Valid())
+	{
+	  delpointl.Append (pi);
+	  if (points.Elem(pi).mgi)
+	    {
+	      cpointsearchtree.DeleteElement (pi);
+	      delete points.Elem(pi).mgi;
+	      points.Elem(pi).mgi = NULL;
+	    }
+	}
+    }
+
+  if (allflines)
+    {
+      allflines->Set (INDEX_2 (GetGlobalIndex (lines.Get(li).L().I1()),
+			       GetGlobalIndex (lines.Get(li).L().I2())), 2);
+    }
+
+  lines.Elem(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 (INDEX li)
+{
+  lines.Elem(li).IncrementClass();
+}
+
+
+void AdFront2 :: ResetClass (INDEX li)
+{
+  lines.Elem(li).ResetClass();
+}
+
+
+
+int AdFront2 :: SelectBaseLine (Point3d & p1, Point3d & p2, 
+				const PointGeomInfo *& geominfo1,
+				const PointGeomInfo *& geominfo2,
+				int & qualclass)
+{
+  int i, hi;
+
+  /*  
+      int minval;
+      int baselineindex;
+      minval = INT_MAX;
+  for (i = 1; i <= lines.Size(); i++)
+    if (lines.Get(i).Valid())
+      {
+	hi = lines.Get(i).LineClass() +
+	  points.Get(lines.Get(i).L().I1()).FrontNr() +
+	  points.Get(lines.Get(i).L().I2()).FrontNr();
+	
+	if (hi < minval)
+	  {
+	    minval = hi;
+	    baselineindex = i;
+	  }
+      }
+  */
+
+  /*
+  static int minval = 0;
+  static int starti = 1;
+  */
+  int baselineindex = 0; 
+
+  for (i = starti; i <= lines.Size(); i++)
+    {
+      if (lines.Get(i).Valid())
+	//      const ILINE * lp = &lines.Get(i).l;
+	//      if (lp->I1() >= 0)
+	{
+	  hi = lines.Get(i).LineClass() +
+	    points.Get(lines.Get(i).L().I1()).FrontNr() +
+	    points.Get(lines.Get(i).L().I2()).FrontNr();
+	  
+	  if (hi <= minval)
+	    {
+	      minval = hi;
+	      baselineindex = i;
+	      break;
+	    }
+	}
+    }
+  
+  if (!baselineindex)
+    {
+      // (*testotu) << "nfl = " << nfl << " tot l = " << lines.Size() << endl;
+      minval = INT_MAX;
+      for (i = 1; i <= lines.Size(); i++)
+	if (lines.Get(i).Valid())
+	  {
+	    hi = lines.Get(i).LineClass() +
+	      points.Get(lines.Get(i).L().I1()).FrontNr() +
+	      points.Get(lines.Get(i).L().I2()).FrontNr();
+	    
+	    if (hi < minval)
+	      {
+		minval = hi;
+		baselineindex = i;
+	      }
+	  }
+    }
+  starti = baselineindex+1;
+
+
+
+  p1 = points.Get(lines.Get(baselineindex).L().I1()).P();
+  p2 = points.Get(lines.Get(baselineindex).L().I2()).P();
+  geominfo1 = &lines.Get(baselineindex).GetGeomInfo(1);
+  geominfo2 = &lines.Get(baselineindex).GetGeomInfo(2);
+
+  qualclass = lines.Get(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)
+{
+  int i, j, ii;
+  int pstind;
+  int pi;
+  Point3d midp, p0;
+
+  pstind = lines.Get(baselineindex).L().I1();
+  p0 = points.Get(pstind).P();
+
+  loclines.Append(lines.Get(baselineindex).L());
+  lindex.Append(baselineindex);
+
+  static ARRAY<int> nearlines;
+
+  nearlines.SetSize(0);
+
+  linesearchtree.GetIntersecting (p0 - Vec3d(xh, xh, xh),
+				  p0 + Vec3d(xh, xh, xh),
+				  nearlines);
+
+  for (ii = 1; ii <= nearlines.Size(); ii++)
+    {
+      i = nearlines.Get(ii);
+
+      if (lines.Get(i).Valid() && i != baselineindex)
+	{
+	  // const Point3d & p1 = points.Get(lines.Get(i).L().I1()).P();
+	  // const Point3d & p2 = points.Get(lines.Get(i).L().I2()).P();
+
+	  //	  midp = Center (p1, p2);
+	  //	  if (Dist (midp, p0) <= xh + 0.5 * Dist (p1, p2))
+	    {
+	      loclines.Append(lines.Get(i).L());
+	      lindex.Append(i);
+	    }
+	}
+    }
+
+  static ARRAY<int> invpindex;
+
+  invpindex.SetSize (points.Size());
+  for (i = 1; i <= loclines.Size(); i++)
+    {
+      invpindex.Elem(loclines.Get(i).I1()) = 0;
+      invpindex.Elem(loclines.Get(i).I2()) = 0;
+    }
+
+  for (i = 1; i <= loclines.Size(); i++)
+    {
+      for (j = 1; j <= 2; j++)
+	{
+	  pi = loclines.Get(i).I(j);
+	  if (invpindex.Get(pi) == 0)
+	    {
+	      pindex.Append (pi);
+	      invpindex.Elem(pi) = pindex.Size();
+	      loclines.Elem(i).I(j) = locpoints.Append (points.Get(pi).P());
+	    }
+	  else
+	    loclines.Elem(i).I(j) = invpindex.Get(pi);
+	}
+    }
+
+  pgeominfo.SetSize (locpoints.Size());
+  for (i = 1; i <= pgeominfo.Size(); i++)
+    pgeominfo.Elem(i).Init();
+
+
+  for (i = 1; i <= loclines.Size(); i++)
+    for (j = 1; j <= 2; j++)
+      {
+	int lpi = loclines.Get(i).I(j);
+	
+	const PointGeomInfo & gi = 
+	  lines.Get(lindex.Get(i)).GetGeomInfo (j);
+	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 (i = 1; i <= locpoints.Size(); i++)
+    {
+      int pi = pindex.Get(i);
+      
+      if (points.Get(pi).mgi)
+	for (j = 1; j <= points.Get(pi).mgi->GetNPGI(); j++)
+	  pgeominfo.Elem(i).AddPointGeomInfo (points.Get(pi).mgi->GetPGI(j));
+    }
+	
+  
+
+  /*
+  for (i = 1; i <= points.Size(); i++)
+    if (points.Get(i).Valid() && 
+	Dist (points.Get(i).P(), p0) <= xh &&
+	invpindex.Get(i) == 0)
+      {
+	invpindex.Elem(i) =
+	  locpoints.Append (points.Get(pi).P());
+      }
+  */
+
+  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.Get(baselineindex).LineClass();
+}
+
+
+
+void AdFront2 :: SetStartFront ()
+{
+  INDEX i;
+  int j;
+
+  for (i = 1; i <= lines.Size(); i++)
+    if (lines.Get(i).Valid())
+      for (j = 1; j <= 2; j++)
+        points.Elem(lines.Get(i).L().I(j)).DecFrontNr(0);
+}
+
+
+
+
+void AdFront2 :: Print (ostream & ost) const
+{
+  INDEX i;
+
+  ost << points.Size() << " Points: " << endl;
+  for (i = 1; i <= points.Size(); i++)
+    if (points.Get(i).Valid())
+      ost << i << "  " << points.Get(i).P() << endl;
+
+  ost << nfl << " Lines: " << endl;
+  for (i = 1; i <= lines.Size(); i++)
+    if (lines.Get(i).Valid())
+      ost << lines.Get(i).L().I1() << " - " << lines.Get(i).L().I2() << endl;
+
+  ost << flush;
+}
+}
diff --git a/contrib/Netgen/libsrc/meshing/adfront2.hpp b/contrib/Netgen/libsrc/meshing/adfront2.hpp
new file mode 100644
index 0000000000..4e72c087fb
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/adfront2.hpp
@@ -0,0 +1,337 @@
+#ifndef FILE_ADFRONT2
+#define FILE_ADFRONT2
+
+/**************************************************************************/
+/* File:   adfront2.hh                                                    */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Okt. 95                                                    */
+/**************************************************************************/
+
+/*
+    Advancing front class for surfaces
+*/
+
+
+/*
+#define FRONTLINE_GEOMINFO_SIZE 8
+#define FRONTPOINT_GEOMINFO_SIZE 4
+*/
+
+///
+class AdFront2
+{
+
+  ///
+  class FrontPoint2
+  {
+    /// coordinates
+    Point3d p;            
+    /// global node index
+    PointIndex globalindex;   
+    /// number of front lines connected to point 
+    int nlinetopoint;    
+    /// distance to original boundary
+    int frontnr;          
+    //   char geominfo[FRONTLINE_GEOMINFO_SIZE];    
+  public:
+    ///
+    MultiPointGeomInfo * mgi;
+
+    ///
+    FrontPoint2 ()
+    {
+      globalindex = -1;
+      nlinetopoint = 0;
+      frontnr = INT_MAX-10;    // attention: overflow on calculating  INT_MAX + 1
+      mgi = NULL;
+    }
+
+    ///
+    FrontPoint2 (const Point3d & ap, PointIndex agi,
+		 MultiPointGeomInfo * amgi);
+    ///
+    ~FrontPoint2 () { ; }
+
+    ///
+    const Point3d & P () 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; }
+
+    ///
+    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
+    //    char geominfo[FRONTLINE_GEOMINFO_SIZE];
+    PointGeomInfo geominfo[2];
+  public:
+
+    FrontLine ();
+    ///
+    FrontLine (const INDEX_2 & al);
+
+    ///
+    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;
+  };
+
+
+
+  ///
+  ARRAY<FrontPoint2> points;  /// front points
+  ARRAY<FrontLine> lines;     /// front lines
+
+  Box3d boundingbox;
+  Box3dTree linesearchtree;       /// search tree for lines
+  Point3dTree cpointsearchtree;   /// search tree for cone points
+
+  ARRAY<INDEX> delpointl;     /// list of deleted front points
+  ARRAY<INDEX> dellinel;      /// list of deleted front lines
+
+  INDEX nfl;                  /// number of front lines;
+  INDEX_2_HASHTABLE<int> * allflines; /// all front lines ever have been
+
+
+  int minval;
+  int starti;
+
+public:
+  ///
+  //  AdFront2 ();
+  AdFront2 (const Box3d & aboundingbox);
+  ///
+  ~AdFront2 ();
+
+  ///
+  void GetPoints (ARRAY<Point3d> & apoints) const;
+  ///
+  void Print (ostream & ost) const;
+
+  ///
+  bool Empty () const
+  {
+    return nfl == 0;
+  }
+  ///
+  int GetNFL () const { return nfl; }
+  ///
+  int SelectBaseLine (Point3d & p1, Point3d & 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<INDEX> & pindex,
+                 ARRAY<INDEX> & lindex,
+                 double xh);
+
+  ///
+  void DeleteLine (INDEX li);
+  ///
+  INDEX AddPoint (const Point3d & p, PointIndex globind, 
+		  MultiPointGeomInfo * mgi = NULL);
+  ///
+  INDEX AddLine (INDEX pi1, INDEX pi2, 
+		 const PointGeomInfo & gi1, const PointGeomInfo & gi2);
+  ///
+  int ExistsLine (int gpi1, int gpi2);
+  ///
+  void IncrementClass (INDEX li);
+  ///
+  void ResetClass (INDEX li);
+
+  ///
+  const PointGeomInfo & GetLineGeomInfo (int li, int lend) const
+    { return lines.Get(li).GetGeomInfo (lend); }
+  ///
+
+  PointIndex GetGlobalIndex (int pi) const
+  {
+    return points.Get(pi).GlobalIndex();
+  }
+  ///
+  void SetStartFront ();
+  ///
+  void PrintOpenSegments (ostream & ost) const;
+};
+
+
+
+
+
+
+/*
+inline int AdFront2::FrontPoint2 :: Valid () const
+{
+  return nlinetopoint >= 0;
+}
+*/
+/*
+inline const Point3d & AdFront2::FrontPoint2 :: P () const
+{
+  return p;
+}
+
+inline PointIndex AdFront2::FrontPoint2 :: GlobalIndex () const
+{
+  return globalindex;
+}
+
+inline void AdFront2::FrontPoint2 :: AddLine ()
+{
+  nlinetopoint++;
+}
+
+inline void AdFront2::FrontPoint2 :: RemoveLine ()
+{
+  nlinetopoint--;
+  if (nlinetopoint == 0)
+    nlinetopoint = -1;
+}
+
+inline int AdFront2::FrontPoint2 :: FrontNr () const
+{
+  return frontnr;
+}
+
+inline void AdFront2::FrontPoint2 :: DecFrontNr (int afrontnr)
+{
+  if (frontnr > afrontnr) frontnr = afrontnr;
+}
+*/
+
+
+
+
+
+
+
+
+/*
+inline int AdFront2::FrontLine :: Valid () const
+{
+  return l.I1() != 0;
+}
+
+inline void AdFront2::FrontLine :: Invalidate ()
+{
+  l.I1() = 0;
+  l.I2() = 0;
+  lineclass = 1000;
+}
+
+inline const INDEX_2 & AdFront2::FrontLine :: L () const
+{
+  return l;
+}
+
+inline int AdFront2::FrontLine :: LineClass () const
+{
+  return lineclass;
+}
+
+
+inline void AdFront2::FrontLine :: IncrementClass ()
+{
+  lineclass++;
+}
+
+inline void AdFront2::FrontLine :: ResetClass ()
+{
+  lineclass = 1;
+}
+
+
+inline int AdFront2 :: Empty () const
+{
+  return nfl == 0;
+}
+
+inline INDEX AdFront2 :: GetGlobalIndex (INDEX pi) const
+{
+  return points.Get(pi).GlobalIndex();
+}
+*/
+#endif
+
+
+
diff --git a/contrib/Netgen/libsrc/meshing/adfront3.cpp b/contrib/Netgen/libsrc/meshing/adfront3.cpp
new file mode 100644
index 0000000000..1bf686e103
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/adfront3.cpp
@@ -0,0 +1,883 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+
+/* ********************** FrontPoint ********************** */
+
+namespace netgen
+{
+
+FrontPoint3 :: FrontPoint3 () 
+{ 
+  globalindex = -1;
+  nfacetopoint = 0; 
+  frontnr = 1000; 
+  cluster = 0;
+}
+
+
+FrontPoint3 :: FrontPoint3 (const Point3d & 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 Element2d & 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<Point3d> & apoints) const
+{
+  for (PointIndex pi = PointIndex::BASE; 
+       pi < points.Size()+PointIndex::BASE; pi++)
+    
+    apoints.Append (points[pi].P());
+}
+
+
+PointIndex AdFront3 :: AddPoint (const Point3d & 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 Element2d & 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 Element2d & 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 :: IncrementClass (INDEX fi)
+{
+  faces.Elem(fi).IncrementQualClass();
+}
+
+
+void AdFront3 :: ResetClass (INDEX fi)
+{
+  faces.Elem(fi).ResetQualClass();
+}
+
+
+
+void AdFront3 :: CreateTrees ()
+{
+  int i, j;
+  PointIndex pi;
+  Point3d pmin, pmax;
+
+  for (pi = PointIndex::BASE; 
+       pi < GetNP()+PointIndex::BASE; pi++)
+    {
+      const Point3d & 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 Element2d & el = GetFace(i);
+      pmin = GetPoint (el[0]);
+      pmax = pmin;
+      for (j = 1; j < 3; j++)
+	{
+	  const Point3d & 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 Point3d & pmin, const Point3d & 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 ()
+{
+  int i, j, hi;
+
+  hi = 0;
+  for (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 (i = PointIndex::BASE; 
+       i < np+PointIndex::BASE; i++)
+    points[i].cluster = i;
+
+  int change;
+  do
+    {
+      change = 0;
+      for (i = 1; i <= faces.Size(); i++)
+	{
+	  const Element2d & el = faces.Get(i).Face();
+
+	  int mini = points[el.PNum(1)].cluster;
+	  int maxi = mini;
+	  
+	  for (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 (j = 1; j <= 3; j++)
+		points[el.PNum(j)].cluster = mini;
+	    }
+	}
+    }
+  while (change);
+
+  BitArrayChar<PointIndex::BASE> usecl(np);
+  usecl.Clear();
+  for (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 (i = PointIndex::BASE; 
+       i < np+PointIndex::BASE; i++)
+    if (usecl.Test(i))
+      cntcl++;
+
+  ARRAY<double, PointIndex::BASE> clvol (np);
+  clvol = 0.0;
+
+  for (i = 1; i <= faces.Size(); i++)
+    {
+      const Element2d & 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;
+    }
+
+
+  int negvol = 0;
+  for (i = PointIndex::BASE; 
+       i < clvol.Size()+PointIndex::BASE; i++)
+    {
+      if (clvol[i] < 0)
+	negvol = 1;
+    }
+
+  if (negvol)
+    {
+      for (i = 1; i <= faces.Size(); i++)
+	faces.Elem(i).cluster = 1;
+      for (i = PointIndex::BASE; 
+	   i < points.Size()+PointIndex::BASE; i++)
+	points[i].cluster = 1;
+    }
+
+  if (hashon) 
+    hashtable.Create();
+}
+
+
+
+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<Element2d> & locfaces,   // local index
+			   ARRAY<PointIndex> & pindex,
+			   ARRAY<INDEX> & findex,
+			   INDEX_2_HASHTABLE<int> & getconnectedpairs,
+			   float xh,
+			   float relh,
+			   INDEX& facesplit)
+{
+  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;
+  
+  static ARRAY<Element2d> locfaces2;           //all local faces in radius xh
+  static ARRAY<int> locfaces3;           // all faces in outer radius relh
+  static 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 Element2d & face = faces.Get(i).Face();
+	  if (faces.Get(i).cluster == cluster && faces.Get(i).Valid() && i != fstind)
+	    {
+	      const Point3d & p1 = points[face.PNum(1)].P();
+	      const Point3d & p2 = points[face.PNum(2)].P();
+	      const Point3d & p3 = points[face.PNum(3)].P();
+	      
+	      Box3d b2;
+	      b2.SetPoint (p1);
+	      b2.AddPoint (p2);
+	      b2.AddPoint (p3);
+
+	      /*
+	      midp = Center (p1, p2, p3);
+	      
+	      if (Dist2 (midp, p0) <= xh*xh)
+		{
+		  locfaces2.Append(faces.Get(i).Face());
+		  findex2.Append(i);
+		}
+		*/
+	      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 Element2d & face = locfaces2.Get(i);
+      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) <= 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 pi = pindex.Get(i);
+	  if (pi >= 1 && pi <= connectedpairs->Size ())
+	    {
+	      for (j = 1; j <= connectedpairs->EntrySize(pi); j++)
+		{
+		  int oi = connectedpairs->Get(pi, 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 (coned, 1);
+		    }
+		}
+	    }
+	}
+    }
+  
+
+
+
+  /*
+  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<Element2d> & groupelements,
+			   ARRAY<PointIndex> & pindex,
+			   ARRAY<INDEX> & findex
+			   ) const
+{
+  static ARRAY<char> pingroup;
+  INDEX i;
+  int j, changed, fused;
+
+  pingroup.SetSize(points.Size());
+
+  for (i = 1; i <= pingroup.Size(); i++)
+    pingroup.Elem(i) = 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 Element2d & face = faces.Get(i).Face();
+
+
+	    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())
+      {
+	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 Element2d & 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;
+    }
+    */
+}
+
+
+int AdFront3 :: Inside (const Point3d & p) const
+{
+  int i, 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 (i = 1; i <= faces.Size(); i++)
+    if (faces.Get(i).Valid())
+      {
+	const Point3d & p1 = points[faces.Get(i).Face().PNum(1)].P();
+	const Point3d & p2 = points[faces.Get(i).Face().PNum(2)].P();
+	const Point3d & p3 = points[faces.Get(i).Face().PNum(3)].P();
+
+	v1 = p2 - p1;
+	v2 = p3 - p1;
+
+	a.Elem(1, 1) = v1.X();
+	a.Elem(2, 1) = v1.Y();
+	a.Elem(3, 1) = v1.Z();
+	a.Elem(1, 2) = v2.X();
+	a.Elem(2, 2) = v2.Y();
+	a.Elem(3, 2) = v2.Z();
+	a.Elem(1, 3) = -n.X();
+	a.Elem(2, 3) = -n.Y();
+	a.Elem(3, 3) = -n.Z();
+
+	b.Elem(1) = p.X() - p1.X();
+	b.Elem(2) = p.Y() - p1.Y();
+	b.Elem(3) = p.Z() - p1.Z();
+
+	CalcInverse (a, ainv);
+	ainv.Mult (b, u);
+
+	if (u.Elem(1) >= 0 && u.Elem(2) >= 0 && u.Elem(1)+u.Elem(2) <= 1 &&
+	    u.Elem(3) > 0)
+	  {
+	    cnt++;
+	  }
+      }
+
+  return (cnt % 2);
+}
+
+
+
+
+
+int AdFront3 :: SameSide (const Point3d & lp1, const Point3d & lp2,
+			  const ARRAY<int> * testfaces) const
+{
+  int i, ii, cnt;
+
+  const Point3d *line[2];
+  line[0] = &lp1;
+  line[1] = &lp2;
+
+
+  cnt = 0;
+
+  Point3d pmin(lp1);
+  Point3d pmax(lp1);
+  pmin.SetToMin (lp2);
+  pmax.SetToMax (lp2);
+
+  static ARRAY<int> aprif;
+  aprif.SetSize(0);
+  
+  if (!testfaces)
+    facetree->GetIntersecting (pmin, pmax, aprif);
+  else
+    {
+      for (i = 1; i <= testfaces->Size(); i++)
+	aprif.Append (testfaces->Get(i));
+    }
+
+  //  (*testout) << "test ss, p1,p2 = " << lp1 << lp2 << ", inters = " << aprif.Size() << endl;
+  //  for (i = 1; i <= faces.Size(); i++)
+  for (ii = 1; ii <= aprif.Size(); ii++)
+    {
+      i = aprif.Get(ii);
+      
+      if (faces.Get(i).Valid())
+	{
+	  const Point3d *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 0000000000..2c43f9334b
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/adfront3.hpp
@@ -0,0 +1,276 @@
+#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
+  Point3d 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 Point3d & ap, PointIndex agi);
+  
+  ///
+  const Point3d & 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;
+};
+
+/// Face in advancing front
+class FrontFace
+{
+  ///
+  Element2d f;
+  ///
+  int qualclass;
+  ///
+  char oldfront;
+  ///
+  int hashvalue;
+  ///
+  int cluster;
+
+public:
+  ///
+  FrontFace ();
+  ///
+  FrontFace (const Element2d & af);
+  ///
+  const Element2d & 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;
+
+  ///
+  class Box3dTree * facetree;
+public:
+
+  ///
+  AdFront3 ();
+  ///
+  ~AdFront3 ();
+  ///
+  void GetPoints (ARRAY<Point3d> & apoints) const;
+  ///
+  int GetNP() const 
+    { return points.Size(); }
+  ///
+  const Point3d & GetPoint (PointIndex pi) const
+  { return points[pi].P(); }
+  ///
+  int GetNF() const
+    { return nff; }
+  ///
+  const Element2d & 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 Point3d & pmin, const Point3d & pmax, 
+			     ARRAY<int> & ifaces) const;
+
+  ///
+  void GetFaceBoundingBox (int i, Box3d & box) const;
+
+  ///
+  int GetLocals (int baseelement,
+		 ARRAY<Point3d> & locpoints,
+                 ARRAY<Element2d> & 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<Element2d> & groupelements,
+                 ARRAY<PointIndex> & pindex,
+                 ARRAY<INDEX> & findex
+                 ) const;
+
+  ///
+  void DeleteFace (INDEX fi);
+  ///
+  PointIndex AddPoint (const Point3d & p, PointIndex globind);
+  ///
+  INDEX AddFace (const Element2d & e);
+  ///
+  INDEX AddConnectedPair (const INDEX_2 & pair);
+  ///
+  void IncrementClass (INDEX fi);
+  ///
+  void ResetClass (INDEX fi);
+  ///
+  void SetStartFront (int baseelnp = 0);
+
+  /// is Point p inside Surface ?
+  int Inside (const Point3d & p) const;
+  /// both points on same side ?
+  int SameSide (const Point3d & lp1, const Point3d & 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/bisect.cpp b/contrib/Netgen/libsrc/meshing/bisect.cpp
new file mode 100644
index 0000000000..c95a2f1421
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/bisect.cpp
@@ -0,0 +1,2371 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+
+namespace netgen
+{
+//#include "../interface/writeuser.hpp"
+
+  class MarkedTet;
+  class MarkedPrism;
+  class MarkedTri;
+  class MarkedQuad;
+  
+  typedef MoveableArray<MarkedTet> T_MTETS;
+  typedef MoveableArray<MarkedPrism> T_MPRISMS;
+  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
+  unsigned char faceedges[4];
+
+  bool incorder;
+  unsigned int order:6;
+
+  friend ostream & operator<< (ostream & ost, const MarkedTet & mt);
+};
+
+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;
+};
+
+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;
+};
+
+class MarkedQuad
+{
+public:
+  /// point numbers
+  PointIndex pnums[4];
+  ///
+  PointGeomInfo pgeominfo[4];
+  /// marked for refinement
+  int marked;
+  /// surface id
+  int surfid;
+
+  bool incorder;
+  unsigned int order:6;
+};
+
+
+
+
+
+ostream & operator<< (ostream & ost, const MarkedTet & mt)
+{
+  int k;
+  ost << "MT: " << mt.pnums[0] << " - " << mt.pnums[1] << " - " 
+      << mt.pnums[2] << " - " << mt.pnums[3] << endl
+      << "marked edge: " << mt.tetedge1 << " - " << mt.tetedge2
+      << ", order = " << mt.order << endl;
+  for (k = 0; k < 4; k++)
+    ost << mt.faceedges[k] << "  ";
+  ost << endl;
+  return ost;
+}
+
+
+
+
+void BTSortEdges (const Mesh & mesh,
+		  INDEX_2_CLOSED_HASHTABLE<int> & edgenumber)
+{
+  cout << "sorting ... " << flush;
+
+  //  if (mesh.PureTetMesh())
+  if (1)
+    {
+      // new, fast version
+      
+      ARRAY<INDEX_2> edges;
+      ARRAY<int> eclasses;
+      
+      int i, j, k;
+      int cntedges = 0;
+      int goon;
+      int ned;
+      
+      // 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];
+	  
+	  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;
+	      }
+	    }
+	      
+	  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);
+		}
+	    }
+	}
+      
+      // 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];
+	  
+	  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
+	{
+	  goon = 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];
+	      switch (el.GetType())
+		{
+		case PRISM:
+		case PRISM12:
+		  {
+		    pairs = prismpairs;
+		    break;
+		  }
+		case PYRAMID:
+		  {
+		    pairs = pyramidpairs;
+		    break;
+		  }
+		}
+
+	      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);
+		      goon = 1;
+		    }
+		  if (eclasses.Get(eclass2) >
+		      eclasses.Get(eclass1))
+		    {
+		      eclasses.Elem(eclass2) = 
+			eclasses.Get(eclass1);
+		      goon = 1;
+		    }
+		}
+	    }
+	}
+      while (goon);
+
+      /*      
+      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);
+      
+      QickSort (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); 
+	    }
+	}
+    }
+
+  else
+    
+    {
+      // old version
+      
+      int i, j, k;
+      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;
+		  }
+		}
+	      
+	      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 goon = 0;
+	      do
+		{
+		  goon = 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;
+			  }
+			}
+
+		      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);
+			      goon = 1;
+			    }
+			  if (used2 && !used1)
+			    {
+			      cnt++;
+			      edgenumber.Set (e1, cnt);
+			      goon = 1;
+			    }
+			}
+		    }
+		}
+	      while (goon);
+	    }
+	}
+      while (found);
+    }
+}
+
+
+
+
+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;
+		  mt.faceedges[k] = 6 - k - i - j;
+		}
+	    }
+    }
+}
+
+
+
+
+void BTDefineMarkedPrism (const Element & el,
+			  INDEX_2_CLOSED_HASHTABLE<int> & edgenumber,
+			  MarkedPrism & mp)
+{
+  int i, j, k;
+
+  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;
+	  }
+      }
+}
+
+
+
+
+
+
+void BTDefineMarkedTri (const Element2d & el,
+			INDEX_2_CLOSED_HASHTABLE<int> & edgenumber,
+			MarkedTri & mt)
+{
+  int i, j, k;
+  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 BTDefineMarkedQuad (const Element2d & el,
+			 INDEX_2_CLOSED_HASHTABLE<int> & edgenumber,
+			 MarkedQuad & mq)
+{
+  int i, j, k;
+  for (i = 0; i < 4; i++)
+    mq.pnums[i] = el[i];
+  Swap (mq.pnums[2], mq.pnums[3]);
+
+  mq.marked = 0;
+  mq.surfid = el.GetIndex();
+}
+
+
+
+
+// mark elements due to local h
+int BTMarkTets (T_MTETS & mtets,
+		T_MPRISMS & mprisms,
+		const Mesh & mesh)
+{
+  int i, j, k;
+  int step;
+
+  int marked = 0;
+
+  int np = mesh.GetNP();
+  Vector hv(np);
+  for (i = 1; i <= np; i++)
+    hv.Elem(i) = mesh.GetH (mesh.Point(i));
+
+  double hfac = 1;
+  
+  for (step = 1; step <= 2; step++)
+    {
+      for (i = 1; i <= mtets.Size(); i++)
+	{
+	  double h = 0;
+	  
+	  for (j = 0; j < 3; j++)
+	    for (k = j+1; k < 4; k++)
+	      {
+		const Point3d & p1 = mesh.Point (mtets.Get(i).pnums[j]);
+		const Point3d & 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 (j = 0; j < 4; j++)
+	    {
+	      double hi = hv.Get (mtets.Get(i).pnums[j]);
+	      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 (i = 1; i <= mprisms.Size(); i++)
+	{
+	  double h = 0;
+	  
+	  for (j = 0; j < 2; j++)
+	    for (k = j+1; k < 3; k++)
+	      {
+		const Point3d & p1 = mesh.Point (mprisms.Get(i).pnums[j]);
+		const Point3d & 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 (j = 0; j < 6; j++)
+	    {
+	      double hi = hv.Get (mprisms.Get(i).pnums[j]);
+	      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)
+{
+  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;
+
+
+  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)
+	    newtet2.faceedges[oldtet.tetedge2] = 
+	      6 - oldtet.tetedge1 - j - k;
+	  else
+	    newtet2.faceedges[oldtet.tetedge2] = oldtet.tetedge1;
+	}
+
+      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)
+	    newtet1.faceedges[oldtet.tetedge1] = 
+	      6 - oldtet.tetedge2 - j - k;
+	  else
+	    newtet1.faceedges[oldtet.tetedge1] = oldtet.tetedge2;
+	}
+    }
+
+  newtet1.matindex = oldtet.matindex;
+  newtet2.matindex = oldtet.matindex;
+  newtet1.incorder = 0;
+  newtet1.order = oldtet.order;
+  newtet2.incorder = 0;
+  newtet2.order = oldtet.order;
+}
+
+
+
+
+void BTBisectPrism (const MarkedPrism & oldprism, int newp1, int newp2,
+		    MarkedPrism & newprism1, MarkedPrism & newprism2)
+{
+  int i, j, k;
+
+  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 BTBisectTri (const MarkedTri & oldtri, int newp, const PointGeomInfo & newpgi,
+		  MarkedTri & newtri1, MarkedTri & newtri2)
+{
+  int i, j, k;
+
+  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, j, k;
+
+  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];
+    }  
+
+  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;
+
+  newquad1.surfid = oldquad.surfid;
+  newquad2.surfid = oldquad.surfid;
+
+
+  int nm = oldquad.marked - 1;
+  if (nm < 0) nm = 0;
+
+  newquad1.marked = nm;
+  newquad2.marked = nm;
+}
+
+
+
+
+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;
+	  hanging = 1;
+	}
+    
+    }
+  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_MTRIS mtris;
+T_MQUADS mquads;
+
+
+
+void BisectTetsCopyMesh (Mesh & mesh, const class CSGeometry *,
+			 BisectionOptions & opt)
+{
+  mtets.SetName ("bisection, tets");
+  mprisms.SetName ("bisection, prisms");
+  mtris.SetName ("bisection, trigs");
+  mquads.SetName ("bisection, quads");
+
+  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);
+  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, 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;
+	  }
+	}
+    }
+
+  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);
+	}
+    }
+
+
+
+  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;
+
+
+  cout << "copied " << mtets.Size() << " tets, " << mtris.Size() << " trigs" << endl;
+}
+
+
+void Refinement :: Bisect (Mesh & mesh, 
+			   BisectionOptions & opt)
+{
+  cout << "Mesh bisection" << endl;
+
+  if (mesh.mglevels == 1)
+    BisectTetsCopyMesh(mesh, NULL, opt);
+
+  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 ii = 0;
+      for (ElementIndex ei = 0; ei < ne; ei++)
+        if (mesh[ei].TestRefinementFlag())
+	  mesh[ei].SetOrder (mesh[ei].GetOrder()+1);
+
+      for (SurfaceElementIndex sei = 0; sei < nse; sei++)
+        if (mesh[sei].TestRefinementFlag())
+	  mesh[sei].SetOrder (mesh[sei].GetOrder()+1);
+
+
+      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);
+
+      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()));
+
+  
+  for (l = 1; l <= 1; l++)
+    {
+      int marked = 0;
+      if (opt.refinementfilename)
+	{
+	  ifstream inf(opt.refinementfilename);
+	  cout << "load refinementinfo from file " << opt.refinementfilename << endl;
+	  char ch;
+	  for (i = 1; i <= mtets.Size(); i++)
+	    {
+	      inf >> ch;
+	      mtets.Elem(i).marked = (ch == '1');
+	    }
+	  marked = 1;
+	}
+
+      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;
+	    */
+
+	  cout << "marked elements: " << cntm << endl;
+
+	  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++;
+		  mquads.Elem(cntquad).marked =
+		    mesh.SurfaceElement(i).TestRefinementFlag();
+		  // mquads.Elem(cntquad).marked = 0;
+		  if (mquads.Elem(cntquad).marked)
+		    cntm++;
+		}
+	    }
+
+	  cout << "with surface-elements: " << cntm << endl;
+
+	  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;
+
+
+	  if (opt.refine_p)
+	    {
+	      cout << "refine p" << endl;
+
+	      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)
+	    {
+	      cout << "refine hp" << endl;
+	      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.p1);
+		    singv.Set (seg.p2);
+		  }
+	      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 (int j = 0; j < 2; j++)
+			{
+			  int pi = (j == 0) ? seg.p1 : seg.p2;
+			  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;
+
+      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);
+		INDEX_2 edge(oldtet.pnums[oldtet.tetedge1],
+			     oldtet.pnums[oldtet.tetedge2]);
+		edge.Sort();
+		if (cutedges.Used (edge))
+		  {
+		    newp = cutedges.Get(edge);
+		  }
+		else
+		  {
+		    Point3d np = Center (mesh.Point (edge.I1()),
+					 mesh.Point (edge.I2()));
+		    newp = mesh.AddPoint (np);
+		    cutedges.Set (edge, newp);
+		  }
+
+		BTBisectTet (oldtet, newp, newtet1, newtet2);
+		mtets.Elem(i) = newtet1;
+		mtets.Append (newtet2);
+	      }
+
+	  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
+		  {
+		    Point3d np = Center (mesh.Point (edge1.I1()),
+					 mesh.Point (edge1.I2()));
+		    newp1 = mesh.AddPoint (np);
+		    cutedges.Set (edge1, newp1);
+		  }
+		if (cutedges.Used (edge2))
+		  newp2 = cutedges.Get(edge2);
+		else
+		  {
+		    Point3d np = Center (mesh.Point (edge2.I1()),
+					 mesh.Point (edge2.I2()));
+		    newp2 = mesh.AddPoint (np);
+		    cutedges.Set (edge2, newp2);
+		  }
+		
+
+		BTBisectPrism (oldprism, newp1, newp2, newprism1, newprism2);
+		mprisms.Elem(i) = newprism1;
+		mprisms.Append (newprism2);
+	      }
+
+
+	  hangingvol = 
+	    MarkHangingTets (mtets, cutedges) +
+	    MarkHangingPrisms (mprisms, cutedges);
+
+
+	  int nsel = mtris.Size();
+
+	  for (i = 1; i <= nsel; i++)
+	    if (mtris.Elem(i).marked)
+	      {
+		MarkedTri oldtri;
+		MarkedTri newtri1, newtri2;
+		int 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
+		  {
+		    Point3d np = Center (mesh.Point (edge.I1()),
+					 mesh.Point (edge.I2()));
+		    newp = mesh.AddPoint (np);
+		    cutedges.Set (edge, newp);
+		  }
+		//		newp = cutedges.Get(edge);
+		
+		int si = mesh.GetFaceDescriptor (oldtri.surfid).SurfNr();
+		//  geom->GetSurface(si)->Project (mesh.Point(newp));
+		PointGeomInfo npgi;
+		
+		if (mesh.PointType(newp) != 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);
+		
+		BTBisectTri (oldtri, newp, npgi, newtri1, newtri2);
+		
+		
+		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]);
+		edge1.Sort();
+		edge2.Sort();
+
+		if (cutedges.Used (edge1))
+		  {
+		    newp1 = cutedges.Get(edge1);
+		  }
+		else
+		  {
+		    Point3d np = Center (mesh.Point (edge1.I1()),
+					 mesh.Point (edge1.I2()));
+		    newp1 = mesh.AddPoint (np);
+		    cutedges.Set (edge1, newp1);
+		  }
+
+		if (cutedges.Used (edge2))
+		  {
+		    newp2 = cutedges.Get(edge2);
+		  }
+		else
+		  {
+		    Point3d np = Center (mesh.Point (edge2.I1()),
+					 mesh.Point (edge2.I2()));
+		    newp2 = mesh.AddPoint (np);
+		    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) << "project point " << newp1 << " old: " << mesh.Point(newp1);
+		PointBetween (mesh.Point (edge1.I1()), mesh.Point (edge1.I2()),
+			      0.5, si,
+			      oldquad.pgeominfo[0],
+			      oldquad.pgeominfo[1],
+			      mesh.Point (newp1), npgi1);
+		(*testout) << " new: " << mesh.Point(newp1) << endl;
+
+		
+		PointBetween (mesh.Point (edge2.I1()), mesh.Point (edge2.I2()),
+			      0.5, si,
+			      oldquad.pgeominfo[2],
+			      oldquad.pgeominfo[3],
+			      mesh.Point (newp2), npgi2);
+		
+
+		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.p1, seg.p2);
+	      edge.Sort();
+	      if (cutedges.Used (edge))
+		{
+		  hangingedge = 1;
+		  Segment nseg1 = seg;
+		  Segment nseg2 = seg;
+		  
+		  int newpi = cutedges.Get(edge);
+		  
+		  nseg1.p2 = newpi;
+		  nseg2.p1 = newpi;
+		  
+		  EdgePointGeomInfo newepgi;
+		  
+		  //		  (*testout) << "move edgepoint " << newpi << " from " << mesh.Point(newpi);
+		  PointBetween (mesh.Point (seg.p1), mesh.Point (seg.p2),
+				0.5, seg.surfnr1, seg.surfnr2, 
+				seg.epgeominfo[0], seg.epgeominfo[1],
+				mesh.Point (newpi), newepgi);
+		  //		  (*testout) << " to " << mesh.Point (newpi) << endl;
+		  nseg1.epgeominfo[1] = newepgi;
+		  nseg2.epgeominfo[0] = newepgi;
+		  
+		  mesh.LineSegment (i) = nseg1;
+		  mesh.AddSegment (nseg2);
+		}
+	    }
+
+	}
+      while (hangingvol || hangingsurf || hangingedge);
+      
+      
+      cout << mtets.Size() << " tets" << endl;
+      cout << mtris.Size() << " trigs" << endl;
+      if (mprisms.Size())
+	{
+	  cout << mprisms.Size() << " prisms" << endl;
+	  cout << mquads.Size() << " quads" << endl;
+	}
+      cout << mesh.GetNP() << " points" << endl;
+    }
+
+  /*
+  cout << "mem in mtets: " << endl;
+  mtets.PrintMemInfo(cout);
+  cout << "cutedges" << endl;
+  cutedges.PrintMemInfo(cout);
+  */
+
+  // (*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 (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 (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 (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 (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());
+  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)
+    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;
+      }
+  */
+  for (i = 1; i <= cutedges.Size(); i++)
+    if (cutedges.UsedPos(i))
+      {
+	INDEX_2 edge;
+	int newpi;
+	cutedges.GetData (i, edge, 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.UpdateTopology();
+
+  // 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);
+	      }
+	  }
+
+    }
+  PrintMessage (5, "Bisection done");
+}
+
+
+
+
+BisectionOptions :: BisectionOptions ()
+{
+  outfilename = NULL;
+  mlfilename = NULL;
+  refinementfilename = NULL;
+  femcode = NULL;
+  maxlevel = 50;
+  usemarkedelements = 0;
+  refine_hp = 0;
+  refine_p = 0;
+}
+
+
+
+void Refinement :: PointBetween (const Point3d & p1, const Point3d & p2, double secpoint,
+				 int surfi, 
+				 const PointGeomInfo & gi1, 
+				 const PointGeomInfo & gi2,
+				 Point3d & newp, PointGeomInfo & newgi)
+{
+  newp = p1+secpoint*(p2-p1);
+}
+
+void Refinement :: PointBetween (const Point3d & p1, const Point3d & p2, double secpoint,
+				 int surfi1, int surfi2, 
+				 const EdgePointGeomInfo & ap1, 
+				 const EdgePointGeomInfo & ap2,
+				 Point3d & newp, EdgePointGeomInfo & newgi)
+{
+  newp = p1+secpoint*(p2-p1);
+}
+
+void Refinement :: ProjectToSurface (Point<3> & p, int surfi)
+{
+  cout << "Refinement :: ProjectToSurface    ERROR: no geometry set" << endl;
+}
+
+
+}
diff --git a/contrib/Netgen/libsrc/meshing/bisect.hpp b/contrib/Netgen/libsrc/meshing/bisect.hpp
new file mode 100644
index 0000000000..8a1e836116
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/bisect.hpp
@@ -0,0 +1,77 @@
+#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 CSGeometry *,
+			 ZRefinementOptions & opt);
+
+
+
+
+
+class Refinement
+{
+public:
+  Refinement ();
+  virtual ~Refinement ();
+  
+  void Refine (Mesh & mesh);
+  void Bisect (Mesh & mesh, class BisectionOptions & opt);
+  void MakeSecondOrder (Mesh & mesh);
+
+  virtual void PointBetween (const Point3d & p1, const Point3d & p2, double secpoint, 
+			     int surfi, 
+			     const PointGeomInfo & gi1, 
+			     const PointGeomInfo & gi2,
+			     Point3d & newp, PointGeomInfo & newgi);
+
+  virtual void PointBetween (const Point3d & p1, const Point3d & p2, double secpoint,
+			     int surfi1, int surfi2, 
+			     const EdgePointGeomInfo & ap1, 
+			     const EdgePointGeomInfo & ap2,
+			     Point3d & newp, EdgePointGeomInfo & newgi);
+
+  virtual void ProjectToSurface (Point<3> & p, int surfi);
+
+  virtual void ProjectToSurface (Point<3> & p, int surfi, PointGeomInfo & /* gi */)
+  {
+    ProjectToSurface (p, surfi);
+  }
+
+
+  void ValidateSecondOrder (Mesh & mesh);
+  void ValidateRefinedMesh (Mesh & mesh, 
+			    ARRAY<INDEX_2> & parents);
+
+};
+
+#endif
diff --git a/contrib/Netgen/libsrc/meshing/boundarylayer.cpp b/contrib/Netgen/libsrc/meshing/boundarylayer.cpp
new file mode 100644
index 0000000000..6f564586b6
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/boundarylayer.cpp
@@ -0,0 +1,91 @@
+#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).p1);
+	  bndnodes.Set (mesh.LineSegment(i).p2);
+	}
+    }
+  for (i = 1; i <= mesh.GetNSeg(); i++)
+    {
+      int snr = mesh.LineSegment(i).edgenr;
+      if (snr != surfid)
+	{
+	  bndnodes.Clear (mesh.LineSegment(i).p1);
+	  bndnodes.Clear (mesh.LineSegment(i).p2);
+	}
+    }
+  
+  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).p1;
+	  int p2 = mesh.LineSegment(i).p2;
+	  int p3 = mapto.Get (p1);
+	  if (!p3) p3 = p1;
+	  int p4 = mapto.Get (p2);
+	  if (!p4) p4 = p2;
+	  
+	  Element2d el(4);
+	  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;
+}
+
+}
diff --git a/contrib/Netgen/libsrc/meshing/boundarylayer.hpp b/contrib/Netgen/libsrc/meshing/boundarylayer.hpp
new file mode 100644
index 0000000000..e5a047b6bb
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/boundarylayer.hpp
@@ -0,0 +1,9 @@
+#ifndef FILE_BOUNDARYLAYER
+#define FILE_BOUNDARYLAYER
+
+
+///
+extern void InsertVirtualBoundaryLayer (Mesh & mesh);
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/meshing/clusters.cpp b/contrib/Netgen/libsrc/meshing/clusters.cpp
new file mode 100644
index 0000000000..5eef2078e7
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/clusters.cpp
@@ -0,0 +1,260 @@
+#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();
+  if (!top.HasEdges())
+    return;
+
+  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) = i;
+
+  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 0000000000..b0eea1b5e0
--- /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 0000000000..50fea32f55
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/curvedelems.cpp
@@ -0,0 +1,2037 @@
+#include <mystdlib.h>
+
+#include "meshing.hpp"
+
+namespace netgen
+{
+    
+    
+    // computes Gaussean integration formula on (0,1) with n points
+    // in: Numerical algs in C (or, was it the Fortran book ?)
+    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);
+	}
+    }
+
+
+
+// ----------------------------------------------------------------------------
+//      PolynomialBasis
+// ----------------------------------------------------------------------------
+
+
+    void PolynomialBasis :: CalcLegendrePolynomials (double x)
+    {
+	double p1 = 1.0, p2 = 0.0, p3;
+
+	lp[0] = 1.0;
+
+	for (int j=1; j<=order; j++)
+	{
+	    p3=p2; p2=p1;
+	    p1=((2.0*j-1.0)*(2*x-1)*p2-(j-1.0)*p3)/j;
+	    lp[j] = p1;
+	}
+    }
+
+
+    void PolynomialBasis :: CalcDLegendrePolynomials (double x)
+    {
+	double p1 = 0., p2 = 0., p3;
+
+	dlp[0] = 0.;
+
+	for (int j = 1; j <= order-1; j++)
+	{
+	    p3=p2; p2=p1;
+	    p1=((2.*j-1.)*(2*lp[j-1]+(2*x-1)*p2)-(j-1.)*p3)/j;
+	    dlp[j] = p1;
+	}
+    }
+
+
+    void PolynomialBasis :: CalcF (double x)
+    {
+	CalcLegendrePolynomials (x);
+
+	for (int j = 0; j<=order-2; j++)
+	    f[j] = (lp[j+2]-lp[j])/(2.0*(j+1)+1)/2.0;
+    }
+
+
+    void PolynomialBasis :: CalcDf (double x)
+    {
+	CalcLegendrePolynomials (x);
+
+	for (int j = 0; j <= order-2; j++)
+	    df[j] = lp[j+1];
+    }
+
+
+    void PolynomialBasis :: CalcFDf (double x)
+    {
+	CalcLegendrePolynomials (x);
+
+	for (int j = 0; j<=order-2; j++)
+	{
+	    f[j] = (lp[j+2]-lp[j])/(2.0*(j+1)+1)/2.0;
+	    df[j] = lp[j+1];
+	}
+    }
+
+
+    void PolynomialBasis :: CalcDDf (double x)
+    {
+        CalcLegendrePolynomials (x);
+	CalcDLegendrePolynomials (x);
+
+	for (int j = 0; j <= order-2; j++) ddf[j] = dlp[j+1];
+    }
+
+
+
+// ----------------------------------------------------------------------------
+//      BaseFiniteElement1D
+// ----------------------------------------------------------------------------
+
+
+    void BaseFiniteElement1D :: CalcVertexShapes ()
+    {
+	vshape[0] = xi(0);
+	vshape[1] = 1-xi(0);
+
+	vdshape[0] = 1;
+	vdshape[1] = -1;
+
+	/*
+	if (edgeorient == -1)
+	{
+	    Swap (vshape[0], vshape[1]);
+	    Swap (vdshape[0], vdshape[1]);
+	}
+	*/
+	
+    }
+
+
+    void BaseFiniteElement1D :: CalcEdgeShapes ()
+    {
+	b.SetOrder (edgeorder);
+	if (edgeorient == 1)
+	  b.CalcFDf( 1-xi(0) );
+	else
+	  b.CalcFDf( xi(0) );
+
+	for (int k = 2; k <= edgeorder; k++)
+	{
+	    eshape[k-2] = b.GetF(k);
+	    edshape[k-2] = -b.GetDf(k);
+	}
+    }
+
+
+    void BaseFiniteElement1D :: CalcEdgeLaplaceShapes ()
+    {
+        b.SetOrder (edgeorder);
+	if (edgeorient == 1)
+	  b.CalcDDf( 1-xi(0) );
+	else
+	  b.CalcDDf( xi(0) );
+
+	for (int k = 2; k <= edgeorder; k++)
+	    eddshape[k-2] = b.GetDDf(k);
+ }
+
+
+
+
+// ----------------------------------------------------------------------------
+//      BaseFiniteElement2D
+// ----------------------------------------------------------------------------
+
+
+    void BaseFiniteElement2D :: SetElementNumber (int aelnr)
+    {
+	int locmaxedgeorder = -1;
+	
+	BaseFiniteElement<2> :: SetElementNumber (aelnr);
+	const Element2d & elem = mesh[(SurfaceElementIndex) (elnr-1)]; 
+	top.GetSurfaceElementEdges (elnr, &(edgenr[0]), &(edgeorient[0]));
+	facenr = top.GetSurfaceElementFace (elnr);
+	faceorient = top.GetSurfaceElementFaceOrientation (elnr);
+	
+	for (int v = 0; v < nvertices; v++)
+	    vertexnr[v] = elem[v];
+	
+	for (int e = 0; e < nedges; e++)
+	{
+	    edgeorder[e] = curv.GetEdgeOrder (edgenr[e]-1); // 1-based
+	    locmaxedgeorder = max2 (edgeorder[e], locmaxedgeorder);
+	}
+	
+	faceorder = curv.GetFaceOrder (facenr-1); // 1-based
+	CalcNFaceShapes ();
+	
+	if (locmaxedgeorder > maxedgeorder)
+	{
+	    maxedgeorder = locmaxedgeorder;
+	    eshape.SetSize(nedges * (maxedgeorder-1));
+	    edshape.SetSize(nedges * (maxedgeorder-1));
+	}
+	
+	if (faceorder > maxfaceorder)
+	{
+	    maxfaceorder = faceorder;
+	    fshape.SetSize( nfaceshapes ); // number of face shape functions
+	    fdshape.SetSize( nfaceshapes );
+	    fddshape.SetSize ( nfaceshapes );
+	}
+    }
+    
+
+
+
+// ----------------------------------------------------------------------------
+//      BaseFiniteElement3D
+// ----------------------------------------------------------------------------
+
+
+    void BaseFiniteElement3D :: SetElementNumber (int aelnr)
+    {
+	int locmaxedgeorder = -1;
+	int locmaxfaceorder = -1;
+	int v, f, e;
+
+	BaseFiniteElement<3> :: SetElementNumber (aelnr);
+	Element elem = mesh[(ElementIndex) (elnr-1)];
+	top.GetElementEdges (elnr, &(edgenr[0]), &(edgeorient[0]));
+	top.GetElementFaces (elnr, &(facenr[0]), &(faceorient[0]));
+	
+	for (v = 0; v < nvertices; v++)
+	    vertexnr[v] = elem[v];
+	
+	for (f = 0; f < nfaces; f++)
+	{
+	    surfacenr[f] = top.GetFace2SurfaceElement (facenr[f]);
+	    // surfaceorient[f] = top.GetSurfaceElementFaceOrientation (surfacenr[f]);
+	}
+	
+	for (e = 0; e < nedges; e++)
+	{
+	    edgeorder[e] = curv.GetEdgeOrder (edgenr[e]-1); // 1-based
+	    locmaxedgeorder = max (edgeorder[e], locmaxedgeorder);
+	}
+	
+	for (f = 0; f < nfaces; f++)
+	{
+	    faceorder[f] = curv.GetFaceOrder (facenr[f]-1); // 1-based
+	    locmaxfaceorder = max (faceorder[f], locmaxfaceorder);
+	}
+	
+	CalcNFaceShapes ();
+	
+	if (locmaxedgeorder > maxedgeorder)
+	{
+	    maxedgeorder = locmaxedgeorder;
+	    eshape.SetSize(nedges * (maxedgeorder-1));
+	    edshape.SetSize(nedges * (maxedgeorder-1));
+	}
+	
+	if (locmaxfaceorder > maxfaceorder)
+	{
+	    maxfaceorder = locmaxfaceorder;
+	    fshape.SetSize( nfaces * (maxfaceorder-1) * (maxfaceorder-1)); // number of face shape functions
+	    fdshape.SetSize( nfaces * (maxfaceorder-1) * (maxfaceorder-1));
+	}
+    }
+    
+    
+
+
+// ----------------------------------------------------------------------------
+//      FETrig
+// ----------------------------------------------------------------------------
+
+
+    void FETrig :: SetReferencePoint (Point<2> axi)
+    {
+	BaseFiniteElement2D :: SetReferencePoint (axi);
+	lambda(0) = xi(0);
+	lambda(1) = xi(1);
+	lambda(2) = 1-xi(0)-xi(1);
+
+	dlambda(0,0) =  1; dlambda(0,1) =  0;
+	dlambda(1,0) =  0; dlambda(1,1) =  1;
+	dlambda(2,0) = -1; dlambda(2,1) = -1;
+    }
+
+
+    void FETrig :: SetVertexSingularity (int v, int exponent)
+    {
+      int i;
+	if (1-lambda(v) < EPSILON) return;
+
+	Point<3> lamold = lambda;
+
+	Vec<2> dfac;
+
+	double fac = pow(1-lambda(v),exponent-1);
+
+	for (i = 0; i < 2; i++)
+	{
+	    dfac(i) = -(exponent-1)*pow(1-lambda(v),exponent-2)*dlambda(v,i);
+	    dlambda(v,i) *= exponent * pow(1-lambda(v),exponent-1);
+	}
+
+	lambda(v) = 1-pow(1-lambda(v),exponent);
+
+	for (i = 0; i < nvertices; i++)
+	{
+	    if (i == v) continue;
+	    for (int j = 0; j < 2; j++)
+		dlambda(i,j) = dlambda(i,j) * fac + lamold(i) * dfac(j);
+
+	    lambda(i) *= fac;
+	}
+    }
+
+
+
+    void FETrig :: CalcVertexShapes ()
+    {
+	for (int v = 0; v < nvertices; v++)
+	{
+	    vshape[v] = lambda(v);
+	    vdshape[v](0) = dlambda(v,0);
+	    vdshape[v](1) = dlambda(v,1);
+	}
+    }
+
+
+    void FETrig :: CalcEdgeShapes ()
+    {
+	int index = 0;
+	for (int e = 0; e < nedges; e++)
+	{
+	  if (edgeorder[e] <= 1) continue;
+
+	    int i0 = eledge[e][0]-1;
+	    int i1 = eledge[e][1]-1;
+
+	    double arg = lambda(i0) + lambda(i1); // = 1-lambda[i2];
+
+	    if (fabs(arg) < EPSILON) // division by 0
+	    {
+		for (int k = 2; k <= edgeorder[e]; k++)
+		{
+		    eshape[index] = 0;
+		    edshape[index] = Vec<2>(0,0);
+		    index++;
+		}
+		continue;
+	    }
+
+	    if (edgeorient[e] == -1) Swap (i0, i1); // reverse orientation
+
+	    double xi = lambda(i1)/arg;
+
+	    b1.SetOrder (edgeorder[e]);
+	    b1.CalcFDf (xi);
+
+	    double decay = arg;
+	    double ddecay;
+	    
+	    double l0 = lambda(i0);
+	    double l0x = dlambda(i0,0);
+	    double l0y = dlambda(i0,1);
+
+	    double l1 = lambda(i1);
+	    double l1x = dlambda(i1,0);
+	    double l1y = dlambda(i1,1);
+
+	    for (int k = 2; k <= edgeorder[e]; k++)
+	    {        
+		ddecay = k*decay;
+		decay *= arg;
+		
+		eshape[index] = b1.GetF (k) * decay;
+		edshape[index] = Vec<2>
+		    (b1.GetDf(k) * (l1x*arg - l1*(l0x+l1x)) * 
+		     decay / (arg * arg) + b1.GetF(k) * ddecay * (l0x+l1x),
+		     b1.GetDf(k) * (l1y*arg - l1*(l0y+l1y)) *
+		     decay / (arg * arg) + b1.GetF(k) * ddecay * (l0y+l1y));
+		index++;
+	    }
+	}
+	// (*testout) << "eshape = " << eshape << ", edshape = " << edshape << endl;
+
+
+	/*
+	index = 0;
+	for (int e = 0; e < nedges; e++)
+	  {
+	    int i0 = eledge[e][0]-1;
+	    int i1 = eledge[e][1]-1;
+
+	    if (edgeorient[e] == -1) Swap (i0, i1); // reverse orientation
+	    
+	    double x = lambda(i1)-lambda(i0);
+	    double y = 1-lambda(i0)-lambda(i1);
+	    double fy = (1-y)*(1-y);
+
+	    // double p3 = 0, p3x = 0, p3y = 0;
+	    // double p2 = -1, p2x = 0, p2y = 0;
+	    // double p1 = x, p1x = 1, p1y = 0;
+
+	    double p3 = 0, p3x = 0, p3y = 0;
+	    double p2 = -0.5, p2x = 0, p2y = 0;
+	    double p1 = 0.5*x, p1x = 0.5, p1y = 0;
+
+	    for (int j=2; j<= edgeorder[e]; j++)
+	      {
+		p3=p2; p3x = p2x; p3y = p2y;
+		p2=p1; p2x = p1x; p2y = p1y;
+		double c1 = (2.0*j-3) / j;
+		double c2 = (j-3.0) / j;
+		
+		p1  = c1 * x * p2 - c2 * fy * p3;
+		p1x = c1 * p2 + c1 * x * p2x - c2 * fy * p3x;
+		p1y = c1 * x * p2y - (c2 * 2 * (y-1) * p3 + c2 * fy * p3y);
+		eshape[index] = p1;
+		// edshape[index] = Vec<2> (p1x, p1y);
+		edshape[index](0) = -2*p1x;
+		edshape[index](1) = p1y-p1x;
+		index++;
+	      }    
+
+	  }
+	  // (*testout) << "eshape = " << eshape << ", edshape = " << edshape << endl;
+	  */
+    }
+
+
+    void FETrig :: CalcFaceShapes ()
+    {
+	int index = 0;
+
+	int i0 = elface[0][0]-1;
+	int i1 = elface[0][1]-1;
+	int i2 = elface[0][2]-1;
+
+	// sort lambda_i's by the corresponing vertex numbers
+
+	if (vertexnr[i1] < vertexnr[i0]) Swap (i0, i1);
+	if (vertexnr[i2] < vertexnr[i0]) Swap (i0, i2);
+	if (vertexnr[i2] < vertexnr[i1]) Swap (i1, i2);
+
+	double arg = lambda(i1) + lambda(i2);
+
+	if (fabs(arg) < EPSILON) // division by 0
+	{
+	    for (int k = 0; k < nfaceshapes; k++)
+	    {
+		fshape[index] = 0;
+		fdshape[index] = Vec<2>(0,0);
+		index++;
+	    }
+	    return;
+	}
+
+	b1.SetOrder (faceorder);
+	b2.SetOrder (faceorder);
+
+	b1.CalcFDf (lambda(i0));
+	b2.CalcFDf (lambda(i2)/arg);
+
+	double decay = 1;
+	double ddecay;
+
+	double l0 = lambda(i0);
+	double l1 = lambda(i1);
+	double l2 = lambda(i2);
+	double dl0x = dlambda(i0,0);
+	double dl0y = dlambda(i0,1);
+	double dl1x = dlambda(i1,0);
+	double dl1y = dlambda(i1,1);
+	double dl2x = dlambda(i2,0);
+	double dl2y = dlambda(i2,1);
+
+	double dargx = dl1x + dl2x;
+	double dargy = dl1y + dl2y;
+
+	for (int n1 = 2; n1 < faceorder; n1++)
+	{
+	    ddecay = (n1-1)*decay;
+	    decay *= arg;
+	    
+	    for (int n0 = 2; n0 < faceorder-n1+2; n0++)
+	    {
+		fshape[index] = b1.GetF(n0) * b2.GetF(n1) * decay;
+		fdshape[index] = Vec<2>
+		    (b1.GetDf(n0) * dl0x * b2.GetF(n1) * decay +
+		     b1.GetF(n0) * b2.GetDf(n1) * (dl2x * arg - l2 * dargx)/(arg*arg) * decay +
+		     b1.GetF(n0) * b2.GetF(n1) * ddecay * dargx,
+		     b1.GetDf(n0) * dl0y * b2.GetF(n1) * decay +
+		     b1.GetF(n0) * b2.GetDf(n1) * (dl2y * arg - l2 * dargy)/(arg*arg) * decay +
+		     b1.GetF(n0) * b2.GetF(n1) * ddecay * dargy);
+		index++;
+	    }
+	}
+    }
+
+
+
+    void FETrig :: CalcFaceLaplaceShapes ()
+    {
+	int index = 0;
+
+	int i0 = elface[0][0]-1;
+	int i1 = elface[0][1]-1;
+	int i2 = elface[0][2]-1;
+
+	if (vertexnr[i1] < vertexnr[i0]) Swap (i0, i1);
+	if (vertexnr[i2] < vertexnr[i0]) Swap (i0, i2);
+	if (vertexnr[i2] < vertexnr[i1]) Swap (i1, i2);
+
+	double arg = lambda(i1) + lambda(i2);
+
+	if (fabs(arg) < EPSILON) // division by 0
+	{
+	    for (int k = 0; k < nfaceshapes; k++)
+		fddshape[k] = 0;
+	    return;
+	}
+
+	b1.SetOrder (faceorder);
+	b2.SetOrder (faceorder);
+
+	b1.CalcFDf (lambda(i0));
+	b1.CalcDDf (lambda(i0));
+	b2.CalcFDf (lambda(i2)/arg);
+	b2.CalcDDf (lambda(i2)/arg);
+
+	double decay = 1;
+	double ddecay = 0;
+	double dddecay;
+
+	double l0 = lambda(i0);
+	double l1 = lambda(i1);
+	double l2 = lambda(i2);
+	double dl0x = dlambda(i0,0);
+	double dl0y = dlambda(i0,1);
+	double dl1x = dlambda(i1,0);
+	double dl1y = dlambda(i1,1);
+	double dl2x = dlambda(i2,0);
+	double dl2y = dlambda(i2,1);
+
+	double dargx = dl1x + dl2x;
+	double dargy = dl1y + dl2y;
+
+	for (int n1 = 2; n1 < faceorder; n1++)
+	{
+	    dddecay = (n1-1)*ddecay;
+	    ddecay = (n1-1)*decay;
+	    decay *= arg;
+	    
+	    for (int n0 = 2; n0 < faceorder-n1+2; n0++)
+	    {
+		fddshape[index] = 
+
+		//  b1.GetDf(n0) * dl0x * b2.GetF(n1) * decay .... derived
+
+		    b1.GetDDf(n0) * dl0x * dl0x * b2.GetF(n1) * decay +
+		    b1.GetDf(n0) * dl0x * b2.GetDf(n1) * (dl2x * arg - l2 * dargx)/(arg*arg) * decay +
+		    b1.GetDf(n0) * dl0x * b2.GetF(n1) * ddecay * dargx +
+
+		    
+	        //  b1.GetF(n0) * b2.GetDf(n1) * (dl2x * arg - l2 * dargx)/(arg*arg) * decay ... derived
+
+		    b1.GetDf(n0) * dl0x * b2.GetDf(n1) * (dl2x * arg - l2 * dargx)/(arg*arg) * decay +
+		    b1.GetF(n0) * b2.GetDDf(n1) * pow((dl2x * arg - l2 * dargx)/(arg*arg),2) * decay +
+		    b1.GetF(n0) * b2.GetDf(n1) * (-2*dargx/arg) * (dl2x * arg - l2 * dargx)/(arg*arg) * decay +
+		    b1.GetF(n0) * b2.GetDf(n1) * (dl2x * arg - l2 * dargx)/(arg*arg) * ddecay * dargx +
+
+		    
+		//  b1.GetF(n0) * b2.GetF(n1) * ddecay * dargx ... derived
+
+		    b1.GetDf(n0) * dl0x * b2.GetF(n1) * ddecay * dargx +
+		    b1.GetF(n0) * b2.GetDf(n1) * (dl2x * arg - l2 * dargx)/(arg*arg) * ddecay * dargx +
+		    b1.GetF(n0) * b2.GetF(n1) * dddecay * dargx * dargx +
+
+		    
+		//  b1.GetDf(n0) * dl0y * b2.GetF(n1) * decay ... derived
+
+		    b1.GetDDf(n0) * dl0y * dl0y * b2.GetF(n1) * decay +
+		    b1.GetDf(n0) * dl0y * b2.GetDf(n1) * (dl2y * arg - l2 * dargy)/(arg*arg) * decay +
+		    b1.GetDf(n0) * dl0y * b2.GetF(n1) * ddecay * dargy +
+		    
+
+		//  b1.GetF(n0) * b2.GetDf(n1) * (dl2y * arg - l2 * dargy)/(arg*arg) * decay ... derived
+
+		    b1.GetDf(n0) * dl0y * b2.GetDf(n1) * (dl2y * arg - l2 * dargy)/(arg*arg) * decay +
+		    b1.GetF(n0) * b2.GetDDf(n1) * pow((dl2y * arg - l2 * dargy)/(arg*arg),2) * decay +
+		    b1.GetF(n0) * b2.GetDf(n1) * (-2*dargy/arg) * (dl2y * arg - l2 * dargy)/(arg*arg) * decay +
+		    b1.GetF(n0) * b2.GetDf(n1) * (dl2y * arg - l2 * dargy)/(arg*arg) * ddecay * dargy +
+		    
+
+		//  b1.GetF(n0) * b2.GetF(n1) * ddecay * dargy ... derived
+
+		    b1.GetDf(n0) * dl0y * b2.GetF(n1) * ddecay * dargy +
+		    b1.GetF(n0) * b2.GetDf(n1) * (dl2y * arg - l2 * dargy)/(arg*arg) * ddecay * dargy +
+		    b1.GetF(n0) * b2.GetF(n1) * dddecay * dargy * dargy;
+
+		index++;
+	    }
+	}
+    }
+
+
+
+// ----------------------------------------------------------------------------
+//      FEQuad
+// ----------------------------------------------------------------------------
+
+
+    void FEQuad :: CalcVertexShapes ()
+    {
+	vshape[0] = (1-xi(0)) * (1-xi(1));
+	vshape[1] = (  xi(0)) * (1-xi(1));
+	vshape[2] = (  xi(0)) * (  xi(1));
+	vshape[3] = (1-xi(0)) * (  xi(1));
+
+	vdshape[0] = Vec<2> ( -(1-xi(1)), -(1-xi(0)) );
+	vdshape[1] = Vec<2> (  (1-xi(1)), -(  xi(0)) );
+	vdshape[2] = Vec<2> (  (  xi(1)),  (  xi(0)) );
+	vdshape[3] = Vec<2> ( -(  xi(1)),  (1-xi(0)) );
+    }
+
+
+    void FEQuad :: CalcEdgeShapes ()
+    {
+	int index = 0;
+
+	double arg0[4] = { xi(0), 1-xi(0), 1-xi(1), xi(1) };
+	double arg1[4] = { 1-xi(1), xi(1), 1-xi(0), xi(0) };
+	double darg0[4] = {  1, -1, -1,  1 };
+	double darg1[4] = { -1,  1, -1,  1 };
+	
+	for (int e = 0; e < nedges; e++)
+	{
+	    b1.SetOrder (edgeorder[e]);
+	    b1.CalcFDf (edgeorient[e] == 1 ? arg0[e] : 1-arg0[e]);
+
+	    double decay = arg1[e];
+	    double ddecay;
+
+	    for (int k = 2; k <= edgeorder[e]; k++, index++)
+	    {
+		ddecay = k*decay;
+		decay *= arg1[e];
+// linear decay
+		eshape[index] = b1.GetF(k) * arg1[e];
+
+		if (e < 2)
+		    edshape[index] = Vec<2>
+			(darg0[e] * edgeorient[e] * b1.GetDf(k) * arg1[e],
+			 b1.GetF(k) * darg1[e]);
+		else
+		    edshape[index] = Vec<2>
+			(b1.GetF(k) * darg1[e],
+			 darg0[e] * edgeorient[e] * b1.GetDf(k) * arg1[e]);
+
+// exponential decay
+/*		eshape[index] = b1.GetF(k) * decay;
+
+		if (e < 2)
+		    edshape[index] = Vec<2>
+			(darg0[e] * edgeorient[e] * b1.GetDf(k) * decay,
+			 b1.GetF(k) * ddecay * darg1[e]);
+		else
+		    edshape[index] = Vec<2>
+			(b1.GetF(k) * ddecay * darg1[e],
+			 darg0[e] * edgeorient[e] * b1.GetDf(k) * decay);
+*/
+	    }
+	}
+    }
+
+
+    void FEQuad :: CalcFaceShapes ()
+    {
+	int index = 0;
+
+	// find index of point with smallest number
+
+	int i0 = 0;
+	for (int i = 1; i < 4; i++)
+	    if (vertexnr[elface[0][i]-1] < vertexnr[elface[0][i0]-1]) i0 = i;
+
+	double x;
+	double y;
+	double dxx;
+	double dxy;
+	double dyx;
+	double dyy;
+
+	switch (i0)
+	{
+	    case 0:
+		x = xi(0); y = xi(1);
+		dxx = 1; dxy = 0;
+		dyx = 0; dyy = 1;
+		break;
+	    case 1:
+		x = xi(1); y = 1-xi(0);
+		dxx = 0; dxy = 1;
+		dyx = -1; dyy = 0;
+		break;
+	    case 2: 
+		x = 1-xi(0); y = 1-xi(1);
+		dxx = -1; dxy = 0;
+		dyx = 0; dyy = -1;
+		break;
+	    case 3:
+		x = 1-xi(1); y = xi(0);
+		dxx = 0; dxy =-1;
+		dyx = 1; dyy = 0;
+		break;
+	}
+
+	if (vertexnr[elface[0][(i0+1) % 4]-1] > vertexnr[elface[0][(i0+3) % 4]-1]) 
+	{
+	    Swap (x,y);
+	    Swap (dxx, dyx);
+	    Swap (dxy, dyy);
+	}
+
+	b1.SetOrder (faceorder);
+	b2.SetOrder (faceorder);
+
+	b1.CalcFDf (x);
+	b2.CalcFDf (y);
+
+	for (int n0 = 2; n0 <= faceorder; n0++)
+	    for (int n1 = 2; n1 <= faceorder; n1++)
+	    {
+		fshape[index] = b1.GetF(n0) * b2.GetF(n1);
+		fdshape[index] = Vec<2> (b1.GetDf(n0) * dxx * b2.GetF(n1) + b1.GetF(n0) * b2.GetDf(n1) * dyx,
+					 b1.GetDf(n0) * dxy * b2.GetF(n1) + b1.GetF(n0) * b2.GetDf(n1) * dyy);
+		index++;
+	    }
+    }
+
+
+    void FEQuad :: CalcFaceLaplaceShapes ()
+    {
+	int index = 0;
+
+	// find index of point with smallest number
+
+	int i0 = 0;
+	for (int i = 1; i < 4; i++)
+	    if (vertexnr[elface[0][i]-1] < vertexnr[elface[0][i0]-1]) i0 = i;
+
+	double x;
+	double y;
+	double dxx;
+	double dxy;
+	double dyx;
+	double dyy;
+
+	switch (i0)
+	{
+	    case 0:
+		x = xi(0); y = xi(1);
+		dxx = 1; dxy = 0;
+		dyx = 0; dyy = 1;
+		break;
+	    case 1:
+		x = xi(1); y = 1-xi(0);
+		dxx = 0; dxy = 1;
+		dyx = -1; dyy = 0;
+		break;
+	    case 2: 
+		x = 1-xi(0); y = 1-xi(1);
+		dxx = -1; dxy = 0;
+		dyx = 0; dyy = -1;
+		break;
+	    case 3:
+		x = 1-xi(1); y = xi(0);
+		dxx = 0; dxy =-1;
+		dyx = 1; dyy = 0;
+		break;
+	}
+
+	if (vertexnr[elface[0][(i0+1) % 4]-1] > vertexnr[elface[0][(i0+3) % 4]-1]) 
+	{
+	    Swap (x,y);
+	    Swap (dxx, dyx);
+	    Swap (dxy, dyy);
+	}
+
+	b1.SetOrder (faceorder);
+	b2.SetOrder (faceorder);
+
+	b1.CalcFDf (x);
+	b1.CalcDDf (x);
+	b2.CalcFDf (y);
+	b2.CalcDDf (y);
+
+	for (int n0 = 2; n0 <= faceorder; n0++)
+	    for (int n1 = 2; n1 <= faceorder; n1++)
+	    {
+		fddshape[index] =
+		    b1.GetDDf(n0) * dxx * dxx * b2.GetF(n1) +
+		2*  b1.GetDf(n0) * dxx * b2.GetDf(n1) * dyx +
+		    b1.GetF(n0) * b2.GetDDf(n1) * dyx * dyx +
+
+		    b1.GetDDf(n0) * dxy * dxy * b2.GetF(n1) +
+		2*  b1.GetDf(n0) * dxy * b2.GetDf(n1) * dyy +
+		    b1.GetF(n0) * b2.GetDDf(n1) * dyy * dyy;
+
+		index++;
+	    }
+    }
+
+
+
+// ----------------------------------------------------------------------------
+//      FETet
+// ----------------------------------------------------------------------------
+
+
+    void FETet :: SetReferencePoint (Point<3> axi)
+    {
+	BaseFiniteElement3D :: SetReferencePoint (axi);
+	
+	lambda(0) = xi(0);
+	lambda(1) = xi(1);
+	lambda(2) = xi(2);
+	lambda(3) = 1-xi(0)-xi(1)-xi(2);
+
+	dlambda(0,0) =  1; dlambda(0,1) =  0; dlambda(0,2) =   0;
+	dlambda(1,0) =  0; dlambda(1,1) =  1; dlambda(1,2) =   0;
+	dlambda(2,0) =  0; dlambda(2,1) =  0; dlambda(2,2) =   1;
+	dlambda(3,0) = -1; dlambda(3,1) = -1; dlambda(3,2) =  -1;
+    }
+
+
+    void FETet :: CalcVertexShapes ()
+    {
+	for (int v = 0; v < nvertices; v++)
+	{
+	    vshape[v] = lambda(v);
+	    vdshape[v](0) = dlambda(v,0);
+	    vdshape[v](1) = dlambda(v,1);
+	    vdshape[v](2) = dlambda(v,2);
+	}
+    }
+
+
+    void FETet :: CalcEdgeShapes ()
+    {
+	int index = 0;
+
+	for (int e = 0; e < nedges; e++)
+	{
+	    int i0 = eledge[e][0]-1;
+	    int i1 = eledge[e][1]-1;
+
+	    double arg = lambda(i0)+lambda(i1);
+
+	    if (fabs(arg) < EPSILON) // division by 0
+	    {
+		for (int k = 2; k <= edgeorder[e]; k++)
+		{
+		    eshape[index] = 0;
+		    edshape[index] = Vec<3>(0,0,0);
+		    index++;
+		}
+		continue;
+	    }
+
+	    if (edgeorient[e] == -1) Swap (i0, i1);
+
+	    double xi = lambda[i1]/arg;
+
+	    b1.SetOrder (edgeorder[e]);
+	    b1.CalcFDf (xi);
+
+	    double decay = arg;
+	    double ddecay;
+	    
+	    double l0 = lambda(i0);
+	    double dl0x = dlambda(i0,0);
+	    double dl0y = dlambda(i0,1);
+	    double dl0z = dlambda(i0,2);
+
+	    double l1 = lambda(i1);
+	    double dl1x = dlambda(i1,0);
+	    double dl1y = dlambda(i1,1);
+	    double dl1z = dlambda(i1,2);
+
+	    double dargx = dl0x + dl1x;
+	    double dargy = dl0y + dl1y;
+	    double dargz = dl0z + dl1z;
+                           
+	    for (int k = 2; k <= edgeorder[e]; k++)
+	    {        
+		ddecay = k*decay;
+		decay *= arg;
+
+		eshape[index] = b1.GetF (k) * decay;
+		edshape[index] = Vec<3>
+		    (b1.GetDf(k) * (dl1x*arg - l1*dargx) * 
+		     decay / (arg * arg) + b1.GetF(k) * ddecay * dargx,
+		     b1.GetDf(k) * (dl1y*arg - l1*dargy) * 
+		     decay / (arg * arg) + b1.GetF(k) * ddecay * dargy,
+		     b1.GetDf(k) * (dl1z*arg - l1*dargz) * 
+		     decay / (arg * arg) + b1.GetF(k) * ddecay * dargz);
+
+		index++;
+	    }
+	}
+    }
+
+
+    void FETet :: CalcFaceShapes ()
+    {
+	int index = 0;
+
+	for (int f = 0; f < nfaces; f++)
+	{
+	  if (faceorder[f] <= 2) continue;
+
+	    int i0 = elface[f][0]-1;
+	    int i1 = elface[f][1]-1;
+	    int i2 = elface[f][2]-1;
+
+	    if (vertexnr[i1] < vertexnr[i0]) Swap (i0, i1);
+	    if (vertexnr[i2] < vertexnr[i0]) Swap (i0, i2);
+	    if (vertexnr[i2] < vertexnr[i1]) Swap (i1, i2);
+
+	    double arg = lambda(i1) + lambda(i2);
+	    double arg2 = lambda(i0) + lambda(i1) + lambda(i2);
+
+	    if (fabs(arg) < EPSILON || fabs(arg2) < EPSILON) // division by 0
+	    {
+		for (int k = 0; k < nfaceshapes[f]; k++)
+		{
+		    fshape[index] = 0;
+		    fdshape[index] = Vec<3>(0,0,0);
+		    index++;
+		}
+		continue;
+	    }
+
+	    b1.SetOrder (faceorder[f]);
+	    b2.SetOrder (faceorder[f]);
+	    
+	    b1.CalcFDf (lambda(i0)/arg2);
+	    b2.CalcFDf (lambda(i2)/arg);
+	    
+	    double decay = 1;
+	    double ddecay;
+	    
+	    double l0 = lambda(i0);
+	    double l1 = lambda(i1);
+	    double l2 = lambda(i2);
+	    double dl0x = dlambda(i0,0);
+	    double dl0y = dlambda(i0,1);
+	    double dl0z = dlambda(i0,2);
+	    double dl1x = dlambda(i1,0);
+	    double dl1y = dlambda(i1,1);
+	    double dl1z = dlambda(i1,2);
+	    double dl2x = dlambda(i2,0);
+	    double dl2y = dlambda(i2,1);
+	    double dl2z = dlambda(i2,2);
+	    
+	    double dargx = dl1x + dl2x;
+	    double dargy = dl1y + dl2y;
+	    double dargz = dl1z + dl2z;
+	    double darg2x = dl0x + dl1x + dl2x;
+	    double darg2y = dl0y + dl1y + dl2y;
+	    double darg2z = dl0z + dl1z + dl2z;
+
+	    for (int n1 = 2; n1 < faceorder[f]; n1++)
+	    {
+		ddecay = (n1-1)*decay;
+		decay *= arg;
+		
+		double decay2 = arg2;
+		double ddecay2;
+
+		for (int n0 = 2; n0 < faceorder[f]-n1+2; n0++)
+		{
+		    ddecay2 = n0*decay2;
+		    decay2 *= arg2;
+
+		    fshape[index] = b1.GetF(n0) * b2.GetF(n1) * decay * decay2;
+		    fdshape[index] = Vec<3>
+			(b1.GetDf(n0) * (dl0x * arg2 - l0 * darg2x)/(arg2*arg2) * b2.GetF(n1) * decay * decay2 +
+			 b1.GetF(n0) * b2.GetDf(n1) * (dl2x * arg - l2 * dargx)/(arg*arg) * decay * decay2 +
+			 b1.GetF(n0) * b2.GetF(n1) * ddecay * dargx * decay2 +
+			 b1.GetF(n0) * b2.GetF(n1) * decay * ddecay2 * darg2x,
+			
+			 b1.GetDf(n0) * (dl0y * arg2 - l0 * darg2y)/(arg2*arg2) * b2.GetF(n1) * decay * decay2 +
+			 b1.GetF(n0) * b2.GetDf(n1) * (dl2y * arg - l2 * dargy)/(arg*arg) * decay * decay2 +
+			 b1.GetF(n0) * b2.GetF(n1) * ddecay * dargy * decay2 +
+			 b1.GetF(n0) * b2.GetF(n1) * decay * ddecay2 * darg2y,
+
+			 b1.GetDf(n0) * (dl0z * arg2 - l0 * darg2z)/(arg2*arg2) * b2.GetF(n1) * decay * decay2 +
+			 b1.GetF(n0) * b2.GetDf(n1) * (dl2z * arg - l2 * dargz)/(arg*arg) * decay * decay2 +
+			 b1.GetF(n0) * b2.GetF(n1) * ddecay * dargz * decay2 +
+			 b1.GetF(n0) * b2.GetF(n1) * decay * ddecay2 * darg2z);
+
+		    index++;
+		}
+	    }
+	}
+    }
+
+
+
+
+// ----------------------------------------------------------------------------
+//      FEPrism
+// ----------------------------------------------------------------------------
+
+
+    void FEPrism :: SetReferencePoint (Point<3> axi)
+    {
+	BaseFiniteElement3D :: SetReferencePoint (axi);
+	
+	lambda(0) = xi(0);
+	lambda(1) = xi(1);
+	lambda(2) = 1-xi(0)-xi(1);
+	lambda(3) = xi(2);
+
+	dlambda(0,0) =  1; dlambda(0,1) =  0; dlambda(0,2) =   0;
+	dlambda(1,0) =  0; dlambda(1,1) =  1; dlambda(1,2) =   0;
+	dlambda(2,0) = -1; dlambda(2,1) = -1; dlambda(2,2) =   0;
+	dlambda(3,0) =  0; dlambda(3,1) =  0; dlambda(3,2) =   1;
+    }
+
+
+    void FEPrism :: CalcVertexShapes ()
+    {
+	double zcomp = 1-lambda(3);
+
+	for (int v = 0; v < nvertices; v++)
+	{
+	    if (v == 3) zcomp = 1-zcomp;
+
+	    vshape[v] = lambda(v % 3) * zcomp;
+	    vdshape[v](0) = dlambda(v % 3,0) * zcomp;
+	    vdshape[v](1) = dlambda(v % 3,1) * zcomp;
+	    vdshape[v](2) = lambda(v % 3) * (-dlambda(3,2));
+
+	    if (v >= 3) vdshape[v](2) *= -1;
+	}
+    }
+
+
+    void FEPrism :: CalcEdgeShapes ()
+    {
+	int index = 0;
+	int e;
+	// triangle edge shapes
+	
+	for (e = 0; e < 6; e++)
+	{
+	    int i0 = (eledge[e][0]-1) % 3;
+	    int i1 = (eledge[e][1]-1) % 3;
+
+	    double arg = lambda[i0]+lambda[i1];
+
+	    if (fabs(arg) < EPSILON) // division by 0
+	    {
+		for (int k = 2; k <= edgeorder[e]; k++)
+		{
+		    eshape[index] = 0;
+		    edshape[index] = Vec<3>(0,0,0);
+		    index++;
+		}
+		continue;
+	    }
+
+	    if (edgeorient[e] == -1) Swap (i0, i1);
+
+	    double xi = lambda[i1]/arg;
+
+	    b1.SetOrder (edgeorder[e]);
+	    b1.CalcFDf (xi);
+
+	    double decay = arg;
+	    double ddecay;
+
+	    double zarg = e < 3 ? (1-lambda(3)) : lambda(3);
+	    double zcomp = zarg;
+	    double dzcomp;
+	    
+	    double l0 = lambda(i0);
+	    double dl0x = dlambda(i0,0);
+	    double dl0y = dlambda(i0,1);
+
+	    double l1 = lambda(i1);
+	    double dl1x = dlambda(i1,0);
+	    double dl1y = dlambda(i1,1);
+
+	    double dargx = dl0x + dl1x;
+	    double dargy = dl0y + dl1y;
+                           
+	    for (int k = 2; k <= edgeorder[e]; k++)
+	    {        
+		ddecay = k*decay;
+		decay *= arg;
+
+		dzcomp = k*zcomp;
+		zcomp *= zarg;
+
+		eshape[index] = b1.GetF (k) * decay * zcomp;
+		edshape[index] = Vec<3>
+		    ((b1.GetDf(k) * (dl1x*arg - l1*dargx) * 
+		     decay / (arg * arg) + b1.GetF(k) * ddecay * dargx) * zcomp,
+		     (b1.GetDf(k) * (dl1y*arg - l1*dargy) * 
+		     decay / (arg * arg) + b1.GetF(k) * ddecay * dargy) * zcomp,
+		     b1.GetF(k) * decay * dzcomp * (e < 3 ? -1 : 1));
+		index++;
+	    }
+	}
+
+	// rectangle edge shapes
+	
+	for (e = 6; e < nedges; e++)
+	{
+	    int i0 = eledge[e][0]-1;
+
+	    double arg = lambda[i0]; 
+
+	    if (fabs(arg) < EPSILON) // division by 0
+	    {
+		for (int k = 2; k <= edgeorder[e]; k++)
+		{
+		    eshape[index] = 0.;
+		    edshape[index] = Vec<3>(0.,0.,0.);
+		    index++;
+		}
+		continue;
+	    }
+
+	    double xi = lambda[3];
+
+	    if (edgeorient[e] == -1) xi = 1-xi;
+
+	    b1.SetOrder (edgeorder[e]);
+	    b1.CalcFDf (xi);
+
+	    double decay = arg;
+	    double ddecay;
+	    
+	    double l0 = lambda(i0);
+	    double l0x = dlambda(i0,0);
+	    double l0y = dlambda(i0,1);
+
+	    for (int k = 2; k <= edgeorder[e]; k++)
+	    {        
+		ddecay = k*decay;
+		decay *= arg;
+		
+		eshape[index] = b1.GetF (k) * decay;
+		edshape[index] = Vec<3>
+		    (b1.GetF(k) * ddecay * l0x,
+		     b1.GetF(k) * ddecay * l0y,
+		     b1.GetDf(k) * edgeorient[e] * decay);
+		index++;
+	    }
+	}
+    }
+
+
+    void FEPrism :: CalcFaceShapes ()
+    {
+	int index = 0;
+	int f;
+
+	// triangle face parts
+
+	for (f = 0; f < 2; f++)
+	{
+	    int i0 = elface[f][0]-1;
+	    int i1 = elface[f][1]-1;
+	    int i2 = elface[f][2]-1;
+
+	    if (vertexnr[i1] < vertexnr[i0]) Swap (i0, i1);
+	    if (vertexnr[i2] < vertexnr[i0]) Swap (i0, i2);
+	    if (vertexnr[i2] < vertexnr[i1]) Swap (i1, i2);
+
+	    i0 = i0 % 3;
+	    i1 = i1 % 3;
+	    i2 = i2 % 3;
+
+	    double arg = lambda(i1) + lambda(i2);
+
+	    if (fabs(arg) < EPSILON) // division by 0
+	    {
+		for (int k = 0; k < nfaceshapes[f]; k++)
+		{
+		    fshape[index] = 0;
+		    fdshape[index] = Vec<3>(0,0,0);
+		    index++;
+		}
+		continue;
+	    }
+
+	    b1.SetOrder (faceorder[f]);
+	    b2.SetOrder (faceorder[f]);
+	    
+	    b1.CalcFDf (lambda(i0));
+	    b2.CalcFDf (lambda(i2)/arg);
+	    
+	    double decay = 1;
+	    double ddecay;
+	    
+	    double l0 = lambda(i0);
+	    double l1 = lambda(i1);
+	    double l2 = lambda(i2);
+	    double dl0x = dlambda(i0,0);
+	    double dl0y = dlambda(i0,1);
+	    double dl0z = dlambda(i0,2);
+	    double dl1x = dlambda(i1,0);
+	    double dl1y = dlambda(i1,1);
+	    double dl1z = dlambda(i1,2);
+	    double dl2x = dlambda(i2,0);
+	    double dl2y = dlambda(i2,1);
+	    double dl2z = dlambda(i2,2);
+	    
+	    double dargx = dl1x + dl2x;
+	    double dargy = dl1y + dl2y;
+	    double dargz = dl1z + dl2z;
+
+	    double arg2 = f == 0 ? 1-xi(2) : xi(2);
+	    double darg2z = f == 0 ? -1 : 1;
+	    
+	    for (int n1 = 2; n1 < faceorder[f]; n1++)
+	    {
+		ddecay = (n1-1)*decay;
+		decay *= arg;
+		
+		double decay2 = arg2;
+		double ddecay2;
+
+		for (int n0 = 2; n0 < faceorder[f]-n1+2; n0++)
+		{
+		    ddecay2 = n0*decay2;
+		    decay2 *= arg2;
+
+		    fshape[index] = b1.GetF(n0) * b2.GetF(n1) * decay * decay2;
+		    fdshape[index] = Vec<3>
+			(b1.GetDf(n0) * dl0x * b2.GetF(n1) * decay * decay2 +
+			 b1.GetF(n0) * b2.GetDf(n1) * (dl2x * arg - l2 * dargx)/(arg*arg) * decay * decay2 +
+			 b1.GetF(n0) * b2.GetF(n1) * ddecay * dargx * decay2,
+			
+			 b1.GetDf(n0) * dl0y * b2.GetF(n1) * decay * decay2 +
+			 b1.GetF(n0) * b2.GetDf(n1) * (dl2y * arg - l2 * dargy)/(arg*arg) * decay * decay2 +
+			 b1.GetF(n0) * b2.GetF(n1) * ddecay * dargy * decay2,
+
+			 b1.GetDf(n0) * dl0z * b2.GetF(n1) * decay * decay2 +
+			 b1.GetF(n0) * b2.GetDf(n1) * (dl2z * arg - l2 * dargz)/(arg*arg) * decay * decay2 +
+			 b1.GetF(n0) * b2.GetF(n1) * ddecay * dargz * decay2 +
+			 b1.GetF(n0) * b2.GetF(n1) * decay * ddecay2 * darg2z);
+
+		    index++;
+		}
+	    }
+	}
+
+
+	// quad parts
+
+	for (f = 2; f < nfaces; f++)
+	{
+	    // find index of point with smallest number
+	  
+	    int i, i0 = 0;
+	    for (i = 1; i < 4; i++)
+		if (vertexnr[elface[f][i]-1] < vertexnr[elface[f][i0]-1]) i0 = i;
+	    
+	    double arg = 0;
+	    double dargx = 0;
+	    double dargy = 0;
+	    double dargz = 0;
+	    for (i = 0; i < 4; i++)
+	    {
+		arg += lambda((elface[f][i]-1) % 3)/2.0;
+		dargx += dlambda((elface[f][i]-1) % 3,0)/2.0;
+		dargy += dlambda((elface[f][i]-1) % 3,1)/2.0;
+		dargz += dlambda((elface[f][i]-1) % 3,2)/2.0;
+	    }
+	    
+	    if (fabs(arg) < EPSILON) // division by 0
+	    {
+		for (int k = 0; k < nfaceshapes[f]; k++)
+		{
+		    fshape[index] = 0;
+		    fdshape[index] = Vec<3>(0,0,0);
+		    index++;
+		}
+		continue;
+	    }
+
+	    int i1 = (i0+3) % 4;
+	    int j = (elface[f][i0]-1) % 3;
+
+	    double lam = lambda(j)/arg;
+	    double dlamx = (dlambda(j,0)*arg-lambda(j)*dargx)/(arg*arg);
+	    double dlamy = (dlambda(j,1)*arg-lambda(j)*dargy)/(arg*arg);
+	    double dlamz = (dlambda(j,2)*arg-lambda(j)*dargz)/(arg*arg);
+			    
+	    double x;
+	    double z;
+	    double dxx;
+	    double dxy;
+	    double dxz;
+	    double dzx;
+	    double dzy;
+	    double dzz;
+
+	    int ratvar;
+	    /*
+	    switch (i0)
+	    {
+		case 0:
+		    x = xi(2); z = lam;
+
+		    dxx = 0;     dxy = 0;     dxz = 1;
+		    dzx = dlamx; dzy = dlamy; dzz = dlamz;
+		    ratvar = 1;
+		    break;
+		case 1:
+		    x = 1-lam; z = xi(2);
+		    dxx = -dlamx; dxy = -dlamy; dxz = -dlamz;
+		    dzx = 0;      dzy = 0;      dzz = 1;
+		    ratvar = 0;
+		    break;
+		case 2: 
+		    x = 1-xi(2); z = 1-lam;
+		    dxx = 0;      dxy = 0;      dxz = -1;
+		    dzx = -dlamx; dzy = -dlamy; dzz = -dlamz;
+		    ratvar = 1;
+		    break;
+		case 3:
+		    x = lam; z = 1-xi(2);
+		    dxx = dlamx; dxy = dlamy; dxz = dlamz;
+		    dzx = 0;     dzy = 0;     dzz = -1;
+		    ratvar = 0;
+		    break;
+	    }
+	    */
+
+	    ratvar = 0;
+	    x = 1-lam;
+	    dxx = -dlamx; dxy = -dlamy; dxz = -dlamz;
+	    if (i0 == 0 || i0 == 1)
+	    {
+		z = xi(2);
+		dzx = 0; dzy = 0; dzz = 1;
+	    }
+	    else
+	    {
+		z = 1-xi(2);
+		dzx = 0; dzy = 0; dzz = -1;
+	    }
+
+	    int ix = i0 ^ 1;
+	    int iz = 3-i0;
+
+	    if (vertexnr[elface[f][ix]-1] > vertexnr[elface[f][iz]-1])
+	    {
+	        Swap (x,z);
+	        Swap (dxx, dzx);
+	        Swap (dxy, dzy);
+		Swap (dxz, dzz);
+		ratvar = 1-ratvar;
+	    }
+
+	    b1.SetOrder (faceorder[f]);
+	    b2.SetOrder (faceorder[f]);
+	    
+	    b1.CalcFDf (x);
+	    b2.CalcFDf (z);
+	    
+	    double decay = arg;
+	    double ddecay;
+	    
+	    for (int n0 = 2; n0 <= faceorder[f]; n0++)
+	    {
+		ddecay = n0*decay;
+		decay *= arg;
+		
+		if (ratvar == 1) decay = arg;
+
+		for (int n1 = 2; n1 <= faceorder[f]; n1++)
+		{
+		    if (ratvar == 1)
+		    {
+			ddecay = n1*decay;
+			decay *= arg;
+		    }
+
+		    fshape[index] = b1.GetF(n0) * b2.GetF(n1) * decay;
+		    fdshape[index] = Vec<3>
+			(b1.GetDf(n0) * dxx * b2.GetF(n1) * decay +
+			 b1.GetF(n0) * b2.GetDf(n1) * dzx * decay +
+			 b1.GetF(n0) * b2.GetF(n1) * ddecay * dargx,
+
+			 b1.GetDf(n0) * dxy * b2.GetF(n1) * decay +
+			 b1.GetF(n0) * b2.GetDf(n1) * dzy * decay +
+			 b1.GetF(n0) * b2.GetF(n1) * ddecay * dargy,
+			
+			 b1.GetDf(n0) * dxz * b2.GetF(n1) * decay +
+			 b1.GetF(n0) * b2.GetDf(n1) * dzz * decay +
+			 b1.GetF(n0) * b2.GetF(n1) * ddecay * dargz);
+
+		    index++;
+		}
+	    }
+	}
+    }
+    
+
+
+// ----------------------------------------------------------------------------
+//      FEPyramid
+// ----------------------------------------------------------------------------
+
+
+    void FEPyramid :: SetReferencePoint (Point<3> axi)
+    {
+	BaseFiniteElement3D :: SetReferencePoint (axi);
+    }
+
+
+    void FEPyramid :: CalcVertexShapes ()
+    {
+	double x = xi(0);
+	double y = xi(1);
+	double z = xi(2);
+
+	if (z == 1.) z = 1-1e-10;
+	vshape[0] = (1-z-x)*(1-z-y) / (1-z);
+	vshape[1] = x*(1-z-y) / (1-z);
+	vshape[2] = x*y / (1-z);
+	vshape[3] = (1-z-x)*y / (1-z);
+	vshape[4] = z;
+
+	double z1 = 1-z;
+	double z2 = z1*z1;
+
+	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 );
+    }
+
+
+    void FEPyramid :: CalcEdgeShapes ()
+    {
+	int index = 0;
+
+	for (int e = 0; e < GetNEdges(); e++)
+	{
+	    for (int k = 2; k <= edgeorder[e]; k++)
+	    {        
+		eshape[index] = 0;
+		edshape[index] = Vec<3>(0,0,0);
+		index++;
+	    }
+	}
+    }
+
+
+    void FEPyramid :: CalcFaceShapes ()
+    {
+	int index = 0;
+
+	for (int f = 0; f < GetNFaces(); f++)
+	{
+	    for (int k = 0; k < nfaceshapes[f]; k++)
+	    {
+		fshape[index] = 0;
+		fdshape[index] = Vec<3>(0,0,0);
+		index++;
+	    }
+	}
+    }
+    
+
+
+
+
+// ----------------------------------------------------------------------------
+//      FEHex
+// ----------------------------------------------------------------------------
+
+
+    void FEHex :: SetReferencePoint (Point<3> axi)
+    {
+	BaseFiniteElement3D :: SetReferencePoint (axi);
+    }
+
+
+    void FEHex :: CalcVertexShapes ()
+    {
+	double x = xi(0);
+	double y = xi(1);
+	double z = xi(2);
+
+	vshape[0] = (1-x)*(1-y)*(1-z);
+	vshape[1] =    x *(1-y)*(1-z); 
+	vshape[2] =    x *   y *(1-z);
+	vshape[3] = (1-x)*   y *(1-z);
+	vshape[4] = (1-x)*(1-y)* z;
+	vshape[5] =    x *(1-y)* z;
+	vshape[6] =    x *   y * z;
+	vshape[7] = (1-x)*   y * z;
+
+	vdshape[0] = Vec<3>(-(1-y)*(1-z), -(1-x)*(1-z), -(1-x)*(1-y));
+	vdshape[1] = Vec<3>( (1-y)*(1-z),    -x *(1-z),    -x *(1-y));
+	vdshape[2] = Vec<3>(    y *(1-z),     x *(1-z),    -x * y);
+	vdshape[3] = Vec<3>(   -y *(1-z),  (1-x)*(1-z), -(1-x)*y);
+	vdshape[4] = Vec<3>(-(1-y)*   z, -(1-x)* z,  (1-x)*(1-y));
+	vdshape[5] = Vec<3>( (1-y)*   z,    -x * z,     x *(1-y));
+	vdshape[6] = Vec<3>(    y *   z,     x * z,     x * y);
+	vdshape[7] = Vec<3>(   -y *   z,  (1-x)* z,  (1-x)*y);
+
+    }
+
+
+    void FEHex :: CalcEdgeShapes ()
+    {
+	int index = 0;
+
+	for (int e = 0; e < GetNEdges(); e++)
+	{
+	    for (int k = 2; k <= edgeorder[e]; k++)
+	    {        
+		eshape[index] = 0;
+		edshape[index] = Vec<3>(0,0,0);
+		index++;
+	    }
+	}
+    }
+
+
+    void FEHex :: CalcFaceShapes ()
+    {
+	int index = 0;
+
+	for (int f = 0; f < GetNFaces(); f++)
+	{
+	    for (int k = 0; k < nfaceshapes[f]; k++)
+	    {
+		fshape[index] = 0;
+		fdshape[index] = Vec<3>(0,0,0);
+		index++;
+	    }
+	}
+    }
+    
+
+
+
+
+
+
+
+
+  int CurvedElements :: IsSurfaceElementCurved (int elnr) const
+  {
+    if (mesh.coarsemesh)
+      {
+	const HPRefElement & hpref_el =
+	  (*mesh.hpelements) [ mesh[(SurfaceElementIndex) elnr].hp_elnr];
+
+	return mesh.coarsemesh->GetCurvedElements().IsSurfaceElementCurved (hpref_el.coarse_elnr);
+      }
+
+
+
+
+    Element2d elem = mesh[(SurfaceElementIndex) elnr];
+
+    switch (elem.GetType())
+      {
+      case TRIG:
+	{
+	  FETrig fe2d(*this);
+	  fe2d.SetElementNumber (elnr+1);
+	  return (fe2d.IsCurved());
+	  break;
+	}
+
+      case QUAD:
+	{
+	  FEQuad fe2d(*this);
+	  fe2d.SetElementNumber (elnr+1);
+	  return (fe2d.IsCurved());
+	  break;
+	}
+
+      }
+    return 0;
+  }
+
+
+
+  int CurvedElements :: IsElementCurved (int elnr) const
+  {
+    if (mesh.coarsemesh)
+      {
+	const HPRefElement & hpref_el =
+	  (*mesh.hpelements) [ mesh[(ElementIndex) elnr].hp_elnr];
+
+	return mesh.coarsemesh->GetCurvedElements().IsElementCurved (hpref_el.coarse_elnr);
+      }
+
+
+
+    Element elem = mesh[(ElementIndex) elnr];
+
+    switch (elem.GetType())
+      {
+      case TET:
+	{
+	  FETet fe3d(*this);
+	  fe3d.SetElementNumber (elnr+1);
+	  return (fe3d.IsCurved());
+	  break;
+	}
+
+      case PRISM:
+	{
+	  FEPrism fe3d(*this);
+	  fe3d.SetElementNumber (elnr+1);
+	  return (fe3d.IsCurved());
+	  break;
+	}
+
+      case PYRAMID:
+	{
+	  FEPyramid fe3d(*this);
+	  fe3d.SetElementNumber (elnr+1);
+	  return (fe3d.IsCurved());
+	  break;
+	}
+
+      case HEX:
+	{
+	  FEHex fe3d(*this);
+	  fe3d.SetElementNumber (elnr+1);
+	  return (fe3d.IsCurved());
+	  break;
+	}
+
+      }
+
+    return 0;
+  }
+
+
+    void CurvedElements :: CalcSegmentTransformation (double xi, int segnr,
+						      Point<3> * x, Vec<3> * dxdxi)
+    {
+	FESegm segm (*this);
+	segm.SetElementNumber (segnr+1);
+	segm.SetReferencePoint (Point<1>(xi));
+
+//	segm.CalcVertexShapes (x != NULL, dxdxi != NULL);
+	segm.CalcVertexShapes ();
+
+	if (x)
+	{
+	    (*x) = Point<3>(0,0,0);
+	    for (int v = 0; v < 2; v++)
+		(*x) = (*x) + segm.GetVertexShape(v) * mesh.Point(segm.GetVertexNr(v));
+	}
+
+	if (dxdxi)
+	{
+	    (*dxdxi) = Vec<3>(0,0,0);
+	    for (int v = 0; v < 2; v++)
+		(*dxdxi) = (*dxdxi) + segm.GetVertexDShape(v) * mesh.Point(segm.GetVertexNr(v));
+	}
+
+	if (segm.GetEdgeOrder() > 1)
+	{
+//	    segm.CalcEdgeShapes (x != NULL, dxdxi != NULL);
+	    segm.CalcEdgeShapes ();
+
+	    if (x)
+	    {
+		int gindex = edgecoeffsindex[segm.GetEdgeNr()-1];
+		for (int k = 2; k <= segm.GetEdgeOrder(); k++, gindex++)
+		    (*x) = (*x) + segm.GetEdgeShape(k-2) * edgecoeffs[gindex];
+	    }
+
+	    if (dxdxi)
+	    {
+		int gindex = edgecoeffsindex[segm.GetEdgeNr()-1];
+		for (int k = 2; k <= segm.GetEdgeOrder(); k++, gindex++)
+		    (*dxdxi) = (*dxdxi) + segm.GetEdgeDShape(k-2) * edgecoeffs[gindex];
+	    }
+	}
+    }
+
+
+
+    void CurvedElements :: CalcSurfaceTransformation (Point<2> xi, int elnr,
+						      Point<3> * x, Mat<3,2> * dxdxi)
+    {
+
+      if (mesh.coarsemesh)
+	{
+	  const HPRefElement & hpref_el =
+	    (*mesh.hpelements) [ mesh[(SurfaceElementIndex) elnr].hp_elnr];
+	  
+	  // xi umrechnen
+	  double lami[4];
+	  switch (mesh[(SurfaceElementIndex) elnr].GetType())
+	    {
+	    case TRIG: 
+	      {
+		lami[0] = xi(0);
+		lami[1] = xi(1);
+		lami[2] = 1-xi(0)-xi(1);
+		lami[3] = 0;
+		break;
+	      }
+	    case QUAD: 
+	      {
+		lami[0] = (1-xi(0))*(1-xi(1));
+		lami[1] = xi(0) * (1-xi(1));
+		lami[2] = xi(0) * xi(1);
+		lami[3] = (1-xi(0))*xi(1);
+		break;
+	      }
+	    }
+	  Point<2> coarse_xi(0,0);
+	  for (int i = 0; i < 4; i++)
+	    {
+	      coarse_xi(0) += hpref_el.param[i][0] * lami[i];
+	      coarse_xi(1) += hpref_el.param[i][1] * lami[i];
+	    }
+	  mesh.coarsemesh->GetCurvedElements().CalcSurfaceTransformation (coarse_xi, hpref_el.coarse_elnr, x, dxdxi);
+
+	  return;
+	}
+
+
+
+
+      const Element2d & elem = mesh[(SurfaceElementIndex) elnr];
+
+      BaseFiniteElement2D * fe2d;
+
+	// char locmem[max2(sizeof(FEQuad), sizeof(FETrig))];
+	char locmemtrig[sizeof(FETrig)];
+	char locmemquad[sizeof(FEQuad)];
+	switch (elem.GetType())
+	  {
+	  case TRIG: fe2d = new (locmemtrig) FETrig (*this); break;
+	  case QUAD: fe2d = new (locmemquad) FEQuad (*this); break;
+	  }
+
+	fe2d->SetElementNumber (elnr+1);
+	fe2d->SetReferencePoint (xi);
+
+	/*
+	for (int v = 0; v < fe2d->GetNVertices(); v++)
+	{
+	  // if (fe2d->GetVertexNr(v) == 1)
+	  fe2d->SetVertexSingularity (v, 2);
+	}
+	*/
+	/*
+	int imin = 0, imax = 0;
+	if (fe2d->GetVertexNr(1) < fe2d->GetVertexNr(0)) imin = 1;
+	if (fe2d->GetVertexNr(2) < fe2d->GetVertexNr(imin)) imin = 2;
+	if (fe2d->GetVertexNr(1) > fe2d->GetVertexNr(0)) imax = 1;
+	if (fe2d->GetVertexNr(2) > fe2d->GetVertexNr(imax)) imax = 2;
+
+	fe2d->SetVertexSingularity (imin, 3);
+	fe2d->SetVertexSingularity (3-imin-imax, 3);
+	fe2d->SetVertexSingularity (imax, 3);
+	*/
+	fe2d->CalcVertexShapes ();
+
+	if (x)
+	{
+	    (*x) = Point<3>(0,0,0);
+	    for (int v = 0; v < fe2d->GetNVertices(); v++)
+		(*x) = (*x) + fe2d->GetVertexShape(v) * mesh.Point(fe2d->GetVertexNr(v));
+	}
+
+	if (dxdxi)
+	{
+	    for (int i = 0; i < 3; i++)
+                for (int j = 0; j < 2; j++)
+                  (*dxdxi)(i,j) = 0;
+                  
+	    for (int v = 0; v < elem.GetNV(); v++)
+		for (int i = 0; i < 3; i++)
+		    for (int j = 0; j < 2; j++)
+			(*dxdxi)(i,j) += fe2d->GetVertexDShape(v)(j) * mesh.Point(fe2d->GetVertexNr(v)).X(i+1);
+	}
+
+	if (IsHighOrder())
+	{
+//	    fe2d->CalcEdgeShapes (x != NULL, dxdxi != NULL);
+	    fe2d->CalcEdgeShapes ();
+	    if (x)
+	    {
+		int index = 0;
+		for (int e = 0; e < fe2d->GetNEdges(); e++)
+		{
+		    int gindex = edgecoeffsindex[fe2d->GetEdgeNr(e)-1];
+
+		    for (int k = 2; k <= fe2d->GetEdgeOrder(e); k++, index++, gindex++)
+			(*x) = (*x) + fe2d->GetEdgeShape(index) * edgecoeffs[gindex];
+ 		}
+	    }
+
+	    if (dxdxi)
+	    {
+		int index = 0;
+		for (int e = 0; e < fe2d->GetNEdges(); e++)
+		{
+		    int gindex = edgecoeffsindex[fe2d->GetEdgeNr(e)-1];
+		    for (int k = 2; k <= fe2d->GetEdgeOrder(e); k++, index++, gindex++)
+			for (int i = 0; i < 3; i++)
+			    for (int j = 0; j < 2; j++)
+				(*dxdxi)(i,j) += fe2d->GetEdgeDShape(index)(j) * edgecoeffs[gindex](i);
+		}
+	    }
+
+	    if (mesh.GetDimension() == 3)
+	    {
+//		fe2d->CalcFaceShapes (x != NULL, dxdxi != NULL);
+		fe2d->CalcFaceShapes ();
+
+		if (x)
+		{
+		    int gindex = facecoeffsindex[fe2d->GetFaceNr()-1];
+		    for (int index = 0; index < fe2d->GetNFaceShapes(); index++, gindex++)
+		    {
+			(*x) = (*x) + fe2d->GetFaceShape(index) * facecoeffs[gindex];
+		    }
+		}
+
+		if (dxdxi)
+		{
+		    int gindex = facecoeffsindex[fe2d->GetFaceNr()-1];
+		    for (int index = 0; index < fe2d->GetNFaceShapes(); index++, gindex++)
+			for (int i = 0; i < 3; i++)
+			    for (int j = 0; j < 2; j++)
+				(*dxdxi)(i,j) += fe2d->GetFaceDShape(index)(j) * facecoeffs[gindex](i);
+		}
+	    }
+	} 
+
+	fe2d -> ~BaseFiniteElement2D();
+    }	
+
+
+
+
+    void CurvedElements :: CalcElementTransformation (Point<3> xi, int elnr,
+						      Point<3> * x, Mat<3,3> * dxdxi)
+    {
+
+     if (mesh.coarsemesh)
+	{
+	  const HPRefElement & hpref_el =
+	    (*mesh.hpelements) [ mesh[(ElementIndex) elnr].hp_elnr];
+	  
+
+	  double lami[8];
+	  FlatVector vlami(8, lami);
+	  vlami = 0;
+	  mesh[(ElementIndex) elnr] . GetShapeNew (xi, vlami);
+
+	  Point<3> coarse_xi(0,0,0);
+	  for (int i = 0; i < 8; i++)
+	    for (int l = 0; l < 3; l++)
+	      coarse_xi(l) += hpref_el.param[i][l] * lami[i];
+	  
+	  Mat<3,3> trans, dxdxic;
+	  if (dxdxi)
+	    {
+	      MatrixFixWidth<3> dlami(8);
+	      dlami = 0;
+	      mesh[(ElementIndex) elnr] . GetDShapeNew (xi, dlami);	  
+	      
+	      trans = 0;
+	      for (int k = 0; k < 3; k++)
+		for (int l = 0; l < 3; l++)
+		  {
+		    double sum = 0;
+		    for (int i = 0; i < 8; i++)
+		      sum += hpref_el.param[i][l] * dlami(i, k);
+		    trans(l,k) = sum;
+		  }
+	    }
+	  
+	  mesh.coarsemesh->GetCurvedElements().CalcElementTransformation (coarse_xi, hpref_el.coarse_elnr, x, &dxdxic);
+
+	  if (dxdxi)
+	    *dxdxi = dxdxic * trans;
+	  return;
+	}
+
+
+
+
+
+
+
+
+
+	Element elem = mesh[(ElementIndex) elnr];
+	BaseFiniteElement3D * fe3d;
+
+	// char locmem[max2(sizeof(FETet), sizeof(FEPrism))];
+	char locmemtet[sizeof(FETet)];
+	char locmemprism[sizeof(FEPrism)];
+	char locmempyramid[sizeof(FEPyramid)];
+	char locmemhex[sizeof(FEHex)];
+	switch (elem.GetType())
+	{
+	    case TET: fe3d = new (locmemtet) FETet (*this); break;
+	    case PRISM: fe3d = new (locmemprism) FEPrism (*this); break;
+	    case PYRAMID: fe3d = new (locmempyramid) FEPyramid (*this); break;
+	    case HEX: fe3d = new (locmemhex) FEHex (*this); break;
+	}
+	
+	fe3d->SetElementNumber (elnr+1);
+	fe3d->SetReferencePoint (xi);
+
+	fe3d->CalcVertexShapes ();
+//	fe3d->CalcVertexShapes (x != NULL, dxdxi != NULL);
+
+	if (x)
+	{
+	    (*x) = Point<3>(0,0,0);
+	    for (int v = 0; v < fe3d->GetNVertices(); v++)
+		(*x) += fe3d->GetVertexShape(v) * Vec<3> (mesh.Point(fe3d->GetVertexNr(v)));
+	}
+
+	if (dxdxi)
+	{
+            for (int i = 0; i < 3; i++)
+                for (int j = 0; j < 3; j++)
+                    (*dxdxi)(i,j) = 0;
+                    
+	    for (int v = 0; v < fe3d->GetNVertices(); v++)
+		for (int i = 0; i < 3; i++)
+		    for (int j = 0; j < 3; j++)
+			(*dxdxi)(i,j) += fe3d->GetVertexDShape(v)(j) * mesh.Point(fe3d->GetVertexNr(v)).X(i+1);
+	}
+
+	if (IsHighOrder())
+	{
+//	    fe3d->CalcEdgeShapes (x != NULL, dxdxi != NULL);
+	    fe3d->CalcEdgeShapes ();
+
+	    if (x)
+	    {
+		int index = 0;
+		for (int e = 0; e < fe3d->GetNEdges(); e++)
+		{
+		    int gindex = edgecoeffsindex[fe3d->GetEdgeNr(e)-1];
+		    for (int k = 2; k <= fe3d->GetEdgeOrder(e); k++, index++, gindex++)
+			(*x) += fe3d->GetEdgeShape(index) * edgecoeffs[gindex];
+		}
+	    }
+
+	    if (dxdxi)
+	    {
+		int index = 0;
+		for (int e = 0; e < fe3d->GetNEdges(); e++)
+		{
+		    int gindex = edgecoeffsindex[fe3d->GetEdgeNr(e)-1];
+		    for (int k = 2; k <= fe3d->GetEdgeOrder(e); k++, index++, gindex++)
+			for (int i = 0; i < 3; i++)
+			    for (int j = 0; j < 3; j++)
+				(*dxdxi)(i,j) += fe3d->GetEdgeDShape(index)(j) * edgecoeffs[gindex](i);
+		}
+	    }
+
+	    if (mesh.GetDimension() == 3)
+	    {
+		fe3d->CalcFaceShapes ();
+//		fe3d->CalcFaceShapes (x != NULL, dxdxi != NULL);
+
+		if (x)
+		{
+		    int index = 0;
+		    for (int f = 0; f < fe3d->GetNFaces(); f++)
+		    {
+			int gindex = facecoeffsindex[fe3d->GetFaceNr(f)-1];
+			for (int k = 0; k < fe3d->GetNFaceShapes(f); k++, index++, gindex++)
+			    (*x) += fe3d->GetFaceShape(index) * facecoeffs[gindex];
+		    }
+		}
+
+		if (dxdxi)
+		{
+		    int index = 0;
+		    for (int f = 0; f < fe3d->GetNFaces(); f++)
+		    {
+			int gindex = facecoeffsindex[fe3d->GetFaceNr(f)-1];
+			for (int k = 0; k < fe3d->GetNFaceShapes(f); k++, index++, gindex++)
+			    for (int i = 0; i < 3; i++)
+				for (int j = 0; j < 3; j++)
+				    (*dxdxi)(i,j) += fe3d->GetFaceDShape(index)(j) * facecoeffs[gindex](i);
+		    }
+		}
+	    } 
+	}
+	
+	fe3d -> ~BaseFiniteElement3D();
+    }
+
+
+} // namespace netgen
+
+
diff --git a/contrib/Netgen/libsrc/meshing/curvedelems.hpp b/contrib/Netgen/libsrc/meshing/curvedelems.hpp
new file mode 100644
index 0000000000..dd11eef051
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/curvedelems.hpp
@@ -0,0 +1,837 @@
+#ifndef CURVEDELEMS
+#define CURVEDELEMS
+
+/**************************************************************************/
+/* File:   curvedelems.hpp                                                */
+/* Author: Robert Gaisbauer                                               */
+/* Date:   27. Sep. 02 (second version: 30. Jan. 03)                      */
+/**************************************************************************/
+
+#include "bisect.hpp"
+#include <iostream>
+
+#define EPSILON 1e-20
+
+
+
+void ComputeGaussRule (int n, ARRAY<double> & xi, ARRAY<double> & wi);
+
+
+
+
+
+// ----------------------------------------------------------------------------
+//      CurvedElements
+// ----------------------------------------------------------------------------
+
+class CurvedElements
+{
+  const Mesh & mesh;
+  const MeshTopology & top;
+
+  bool isHighOrder;
+  int nvisualsubsecs;
+  int nIntegrationPoints;
+
+  ARRAY<int> edgeorder;
+  ARRAY<int> faceorder;
+
+  /*
+
+  ARRAY< Vec<3> > edgecoeffs;
+  ARRAY< Vec<3> > facecoeffs;
+
+  ARRAY<int> edgecoeffsindex;
+  ARRAY<int> facecoeffsindex;
+
+  */
+
+  inline Vec<3> GetEdgeCoeff (int edgenr, int k);
+  inline Vec<3> GetFaceCoeff (int facenr, int k);
+
+  
+  void CalcSegmentTransformation (double xi, int segnr,
+				  Point<3> * x = NULL, Vec<3> * dxdxi = NULL);
+
+  void CalcSurfaceTransformation (Point<2> xi, int elnr,
+				  Point<3> * x = NULL, Mat<3,2> * dxdxi = NULL);
+
+  void CalcElementTransformation (Point<3> xi, int elnr,
+				  Point<3> * x = NULL, Mat<3,3> * dxdxi = NULL);
+
+public:
+
+  Refinement * refinement;
+
+  ARRAY< Vec<3> > edgecoeffs;
+  ARRAY< Vec<3> > facecoeffs;
+
+  ARRAY<int> edgecoeffsindex;
+  ARRAY<int> facecoeffsindex;
+
+
+
+
+
+  CurvedElements (const Mesh & amesh);
+  ~CurvedElements();
+
+  bool IsHighOrder() const
+  { return isHighOrder; };
+  void SetHighOrder () { isHighOrder = 1; }
+
+
+  int GetNVisualSubsecs() const
+  { return nvisualsubsecs; };
+
+  const class Mesh & GetMesh() const
+  { return mesh; };
+
+  void BuildCurvedElements(Refinement * ref, int polydeg);
+
+  int GetEdgeOrder (int edgenr) const
+  { return edgeorder[edgenr]; };
+
+  int GetFaceOrder (int facenr) const
+  { return faceorder[facenr]; };
+
+  int IsEdgeCurved (int edgenr) const;
+
+  int IsFaceCurved (int facenr) const;
+
+  int IsSurfaceElementCurved (int elnr) const;
+
+  int IsElementCurved (int elnr) const;
+
+
+  void CalcSegmentTransformation (double xi, int segnr,
+				  Point<3> & x)
+  { CalcSegmentTransformation (xi, segnr, &x, NULL); };
+
+  void CalcSegmentTransformation (double xi, int segnr,
+				  Vec<3> & dxdxi)
+  { CalcSegmentTransformation (xi, segnr, NULL, &dxdxi); };
+
+  void CalcSegmentTransformation (double xi, int segnr,
+				  Point<3> & x, Vec<3> & dxdxi)
+  { CalcSegmentTransformation (xi, segnr, &x, &dxdxi); };
+
+
+  void CalcSurfaceTransformation (Point<2> & xi, int elnr,
+				  Point<3> & x)
+  { CalcSurfaceTransformation (xi, elnr, &x, NULL); };
+
+  void CalcSurfaceTransformation (Point<2> & xi, int elnr,
+				  Mat<3,2> & dxdxi)
+  { CalcSurfaceTransformation (xi, elnr, NULL, &dxdxi); };
+
+  void CalcSurfaceTransformation (Point<2> & xi, int elnr,
+				  Point<3> & x, Mat<3,2> & dxdxi)
+  { CalcSurfaceTransformation (xi, elnr, &x, &dxdxi); };
+
+
+  void CalcElementTransformation (Point<3> xi, int elnr,
+				  Point<3> & x)
+  { CalcElementTransformation (xi, elnr, &x, NULL); };
+
+  void CalcElementTransformation (Point<3> xi, int elnr,
+				  Mat<3,3> & dxdxi)
+  { CalcElementTransformation (xi, elnr, NULL, &dxdxi); };
+
+  void CalcElementTransformation (Point<3> xi, int elnr,
+				  Point<3> & x, Mat<3,3> & dxdxi)
+  { CalcElementTransformation (xi, elnr, &x, &dxdxi); };
+
+};
+
+
+
+// ----------------------------------------------------------------------------
+//      PolynomialBasis
+// ----------------------------------------------------------------------------
+
+class PolynomialBasis
+{
+  int order;
+  int maxorder;
+  ArrayMem<double,20> f;
+  ArrayMem<double,20> df;
+  ArrayMem<double,20> ddf;
+
+  ArrayMem<double,20> lp;
+  ArrayMem<double,20> dlp;
+
+  inline void CalcLegendrePolynomials (double x);
+  inline void CalcDLegendrePolynomials (double x);
+
+public:
+
+  PolynomialBasis ()
+  { maxorder = -1; };
+
+  ~PolynomialBasis ()
+  {};
+
+  void SetOrder (int aorder)
+  {
+    order = aorder;
+    if (order > maxorder)
+      {
+	maxorder = order;
+	f.SetSize(order-1);
+	df.SetSize(order-1);
+	ddf.SetSize(order-1);
+	lp.SetSize(order+1);
+	dlp.SetSize(order);
+      };
+  };
+
+  inline void CalcF (double x);
+  inline void CalcDf (double x);
+  inline void CalcDDf (double x);
+
+  inline void CalcFDf (double x);
+
+  double GetF (int p) { return f[p-2]; };
+  double GetDf (int p) { return df[p-2]; };
+  double GetDDf (int p) { return ddf[p-2]; };
+};
+
+
+
+// ----------------------------------------------------------------------------
+//      BaseFiniteElement
+// ----------------------------------------------------------------------------
+
+template <int DIM>
+class BaseFiniteElement
+{
+protected:
+
+  Point<DIM> xi;
+  int elnr;
+  const CurvedElements & curv;
+  const Mesh & mesh;
+  const MeshTopology & top;
+
+public:
+
+  BaseFiniteElement(const CurvedElements & acurv)
+    : curv(acurv), mesh(curv.GetMesh()), top(mesh.GetTopology())
+  {};
+
+  virtual ~BaseFiniteElement()
+  {};
+
+  void SetElementNumber (int aelnr)
+  { elnr = aelnr; }; // 1-based arrays in netgen
+
+  virtual void SetReferencePoint (Point<DIM> axi)
+  { xi = axi; };
+};
+
+
+
+// ----------------------------------------------------------------------------
+//      BaseFiniteElement1D
+// ----------------------------------------------------------------------------
+
+class BaseFiniteElement1D : public BaseFiniteElement<1>
+{
+protected:
+  PolynomialBasis b;
+
+  int vertexnr[2];
+  int edgenr;
+  int edgeorient;
+  int edgeorder;
+
+  int maxedgeorder;
+
+  double vshape[2];
+  double vdshape[2];
+  ArrayMem<double,20> eshape;
+  ArrayMem<double,20> edshape;
+  ArrayMem<double,20> eddshape;
+
+public:
+
+  BaseFiniteElement1D (const CurvedElements & acurv) : BaseFiniteElement<1>(acurv)
+  { maxedgeorder = 1; };
+
+  virtual ~BaseFiniteElement1D()
+  {};
+
+  int GetVertexNr (int v)
+  { return vertexnr[v]; };
+
+  int GetEdgeNr ()
+  { return edgenr; };
+
+  int GetEdgeOrder ()
+  { return edgeorder; };
+
+  int GetEdgeOrientation ()
+  { return edgeorient; };
+
+  void CalcVertexShapes();
+  void CalcEdgeShapes();
+  void CalcEdgeLaplaceShapes();
+
+  double GetVertexShape (int v)
+  { return vshape[v]; };
+
+  double GetEdgeShape (int index)
+  { return eshape[index]; };
+
+  double GetVertexDShape (int v)
+  { return vdshape[v]; };
+
+  double GetEdgeDShape (int index)
+  { return edshape[index]; };
+
+  double GetEdgeLaplaceShape (int index)
+  { return eddshape[index]; };
+
+};
+
+
+
+
+// ----------------------------------------------------------------------------
+//      FESegm
+// ----------------------------------------------------------------------------
+
+class FESegm : public BaseFiniteElement1D
+{
+
+public:
+
+  FESegm(const CurvedElements & acurv) : BaseFiniteElement1D(acurv)
+  {};
+
+  virtual ~FESegm()
+  {};
+
+  void SetElementNumber (int aelnr)
+  { 
+    BaseFiniteElement<1> :: SetElementNumber (aelnr);
+    Segment s = mesh.LineSegment(elnr);
+    vertexnr[0] = s.p1;
+    vertexnr[1] = s.p2;
+    edgenr = top.GetSegmentEdge(elnr);
+    edgeorient = top.GetSegmentEdgeOrientation(elnr);
+    edgeorder = curv.GetEdgeOrder(edgenr-1); // 1-based arrays in netgen
+
+    if (edgeorder > maxedgeorder)
+      {
+	maxedgeorder = edgeorder;
+	eshape.SetSize(maxedgeorder-1);
+	edshape.SetSize(maxedgeorder-1);
+	eddshape.SetSize(maxedgeorder-1);
+      }
+  };
+
+};
+
+
+
+// ----------------------------------------------------------------------------
+//      FEEdge
+// ----------------------------------------------------------------------------
+
+class FEEdge : public BaseFiniteElement1D
+{
+
+public:
+
+  FEEdge(const CurvedElements & acurv) : BaseFiniteElement1D(acurv)
+  {};
+
+  virtual ~FEEdge()
+  {};
+
+  void SetElementNumber (int aelnr)
+  { 
+    BaseFiniteElement<1> :: SetElementNumber (aelnr);
+    top.GetEdgeVertices (elnr, vertexnr[0], vertexnr[1]);
+    edgenr = elnr;
+    edgeorient = 1;
+    edgeorder = curv.GetEdgeOrder(edgenr-1); // 1-based arrays in netgen
+
+    if (edgeorder > maxedgeorder)
+      {
+	maxedgeorder = edgeorder;
+	eshape.SetSize(maxedgeorder-1);
+	edshape.SetSize(maxedgeorder-1);
+	eddshape.SetSize(maxedgeorder-1);
+      }
+  };
+    
+};
+
+
+
+// ----------------------------------------------------------------------------
+//      BaseFiniteElement2D
+// ----------------------------------------------------------------------------
+
+class BaseFiniteElement2D : public BaseFiniteElement<2>
+{
+protected:
+
+  int nvertices;
+  int nedges;
+
+  int vertexnr[4];
+  int edgenr[4];
+  int edgeorient[4];
+  int edgeorder[4];
+  int facenr;
+  int faceorient;
+  int faceorder;
+ 
+  int nfaceshapes;
+
+  int maxedgeorder;
+  int maxfaceorder;
+
+  PolynomialBasis b1, b2;
+
+  double vshape[4];
+  Vec<2> vdshape[4];
+  ArrayMem<double,80> eshape;
+  ArrayMem< Vec<2>,80> edshape;
+  ArrayMem<double,400> fshape;
+  ArrayMem<Vec<2>,400> fdshape;
+  ArrayMem<double,400> fddshape;
+
+  virtual void CalcNFaceShapes () = 0;
+
+public:
+
+  BaseFiniteElement2D (const CurvedElements & acurv) : BaseFiniteElement<2>(acurv)
+  { maxedgeorder = maxfaceorder = -1; };
+
+    virtual ~BaseFiniteElement2D()
+	{};
+
+  void SetElementNumber (int aelnr);
+
+  virtual void SetVertexSingularity (int v, int exponent) = 0;
+
+  int GetVertexNr (int v)
+  { return vertexnr[v]; };
+
+  int GetEdgeNr (int e)
+  { return edgenr[e]; };
+
+  int GetFaceNr ()
+  { return facenr; };
+
+  int GetEdgeOrder (int e)
+  { return edgeorder[e]; };
+
+  int GetFaceOrder ()
+  { return faceorder; }
+
+  int GetNVertices ()
+  { return nvertices; };
+
+  int GetNEdges ()
+  { return nedges; };
+
+  int GetNFaceShapes ()
+  { return nfaceshapes; };
+
+  int IsCurved ()
+  {
+    bool iscurved = 0;
+    int e;
+
+    for (e = 0; e < GetNEdges(); e++)
+      iscurved = iscurved || (GetEdgeOrder(e) > 1);
+
+    return iscurved || (GetFaceOrder() > 1);
+  }
+
+  virtual void CalcVertexShapes() = 0;
+  virtual void CalcEdgeShapes() = 0; 
+  virtual void CalcFaceShapes() = 0;
+
+  virtual void CalcFaceLaplaceShapes() = 0;
+
+  double GetVertexShape (int v)
+  { return vshape[v]; };
+
+  double GetEdgeShape (int index)
+  { return eshape[index]; };
+
+  double GetFaceShape (int index)
+  { return fshape[index]; };
+
+  Vec<2> GetVertexDShape (int v)
+  { return vdshape[v]; };
+
+  Vec<2> GetEdgeDShape (int index)
+  { return edshape[index]; };
+
+  Vec<2> GetFaceDShape (int index)
+  { return fdshape[index]; };
+
+  double GetFaceLaplaceShape (int index)
+  { return fddshape[index]; };
+};
+
+
+
+// ----------------------------------------------------------------------------
+//      FETrig
+// ----------------------------------------------------------------------------
+
+class FETrig : public BaseFiniteElement2D
+{
+  Point<3> lambda;
+  Mat<3,2> dlambda;
+
+  const ELEMENT_EDGE * eledge;
+  const ELEMENT_FACE * elface;
+
+  virtual void CalcNFaceShapes ()
+  { nfaceshapes = ((faceorder-1)*(faceorder-2))/2; };
+
+public:
+
+  FETrig (const CurvedElements & acurv) : BaseFiniteElement2D(acurv)
+  {
+    nvertices = 3;
+    nedges = 3;
+    eledge = MeshTopology :: GetEdges (TRIG);
+    elface = MeshTopology :: GetFaces (TRIG);
+  };
+
+    virtual ~FETrig()
+	{};
+
+  virtual void SetReferencePoint (Point<2> axi);
+
+  virtual void SetVertexSingularity (int v, int exponent);
+
+  virtual void CalcVertexShapes();
+  virtual void CalcEdgeShapes();
+  virtual void CalcFaceShapes();
+
+  virtual void CalcFaceLaplaceShapes();
+};
+
+
+
+// ----------------------------------------------------------------------------
+//      FEQuad
+// ----------------------------------------------------------------------------
+
+class FEQuad : public BaseFiniteElement2D
+{
+  const ELEMENT_FACE * elface;
+
+  virtual void CalcNFaceShapes ()
+  { nfaceshapes = (faceorder-1)*(faceorder-1); };
+
+public:
+
+  FEQuad (const CurvedElements & acurv) : BaseFiniteElement2D(acurv)
+  {
+    nvertices = 4;
+    nedges = 4;
+    elface = MeshTopology :: GetFaces (QUAD);
+  };
+
+    virtual ~FEQuad()
+	{};
+
+  virtual void SetVertexSingularity (int /* v */, int /* exponent */)
+	{};
+
+  virtual void CalcVertexShapes();
+  virtual void CalcEdgeShapes();
+  virtual void CalcFaceShapes();
+
+  virtual void CalcFaceLaplaceShapes();
+};
+
+
+
+
+// ----------------------------------------------------------------------------
+//      BaseFiniteElement3D
+// ----------------------------------------------------------------------------
+
+class BaseFiniteElement3D : public BaseFiniteElement<3>
+{
+protected:
+
+  int nvertices;
+  int nedges;
+  int nfaces;
+
+  int vertexnr[8];
+  int edgenr[12];
+  int edgeorient[12];
+  int edgeorder[12];
+  int facenr[6];
+  int faceorient[6];
+  int faceorder[6];
+  int surfacenr[6];
+  // int surfaceorient[6];
+
+  int nfaceshapes[6];
+
+  int maxedgeorder;
+  int maxfaceorder;
+
+  PolynomialBasis b1, b2;
+
+  double vshape[8];
+  Vec<3> vdshape[8];
+  ArrayMem<double,120> eshape;
+  ArrayMem<Vec<3>,120> edshape;
+  ArrayMem<double,2000> fshape;
+  ArrayMem<Vec<3>,2000> fdshape;
+
+  virtual void CalcNFaceShapes () = 0;
+
+public:
+
+  int locmaxedgeorder;
+  int locmaxfaceorder;
+
+  BaseFiniteElement3D (const CurvedElements & acurv) : BaseFiniteElement<3>(acurv)
+  { maxedgeorder = maxfaceorder = -1; };
+
+  void SetElementNumber (int aelnr);
+
+  int GetVertexNr (int v)
+  { return vertexnr[v]; };
+
+  int GetEdgeNr (int e)
+  { return edgenr[e]; };
+
+  int GetFaceNr (int f)
+  { return facenr[f]; };
+
+  int GetNFaceShapes (int f)
+  { return nfaceshapes[f]; };
+
+  int GetEdgeOrder (int e)
+  { return edgeorder[e]; };
+
+  int GetFaceOrder (int f)
+  { return faceorder[f]; };
+
+  int GetNVertices ()
+  { return nvertices; };
+
+  int GetNEdges ()
+  { return nedges; };
+
+  int GetNFaces ()
+  { return nfaces; };
+
+  int IsCurved ()
+  {
+    bool iscurved = 0;
+    int e, f;
+
+    for (e = 0; e < GetNEdges(); e++)
+      iscurved = iscurved || (GetEdgeOrder(e) > 1);
+
+    for (f = 0; f < GetNFaces(); f++)
+      iscurved = iscurved || (GetFaceOrder(f) > 1);
+
+    return iscurved;
+  }
+
+  virtual void CalcVertexShapes() = 0;
+  virtual void CalcEdgeShapes() = 0;
+  virtual void CalcFaceShapes() = 0;
+
+  double GetVertexShape (int v)
+  { return vshape[v]; };
+
+  double GetEdgeShape (int index)
+  { return eshape[index]; };
+
+  double GetFaceShape (int index)
+  { return fshape[index]; };
+
+  Vec<3> GetVertexDShape (int v)
+  { return vdshape[v]; };
+
+  Vec<3> GetEdgeDShape (int index)
+  { return edshape[index]; };
+
+  Vec<3> GetFaceDShape (int index)
+  { return fdshape[index]; };
+};
+
+
+
+// ----------------------------------------------------------------------------
+//      FETet
+// ----------------------------------------------------------------------------
+
+class FETet : public BaseFiniteElement3D
+{
+  Point<4> lambda;
+  Mat<4,3> dlambda;
+
+  const ELEMENT_EDGE * eledge;
+  const ELEMENT_FACE * elface;
+
+  virtual void CalcNFaceShapes ()
+  {
+    for (int f = 0; f < nfaces; f++)
+      nfaceshapes[f] = ((faceorder[f]-1)*(faceorder[f]-2))/2;
+  };
+
+public:
+
+  FETet (const CurvedElements & acurv) : BaseFiniteElement3D(acurv)
+  {
+    nvertices = 4;
+    nedges = 6;
+    nfaces = 4;
+    eledge = MeshTopology :: GetEdges (TET);
+    elface = MeshTopology :: GetFaces (TET);
+  };
+
+  void SetReferencePoint (Point<3> axi);
+
+  virtual void CalcVertexShapes();
+  virtual void CalcEdgeShapes();
+  virtual void CalcFaceShapes();
+};
+
+
+
+// ----------------------------------------------------------------------------
+//      FEPrism
+// ----------------------------------------------------------------------------
+
+class FEPrism : public BaseFiniteElement3D
+{
+  Point<4> lambda;   // mixed barycentric coordinates
+  Mat<4,3> dlambda;
+
+  const ELEMENT_EDGE * eledge;
+  const ELEMENT_FACE * elface;
+
+  virtual void CalcNFaceShapes ()
+  {
+    int f;
+    for (f = 0; f < 2; f++)
+      nfaceshapes[f] = ((faceorder[f]-1)*(faceorder[f]-2))/2;
+    for (f = 2; f < nfaces; f++)
+      nfaceshapes[f] = (faceorder[f]-1)*(faceorder[f]-1);
+  };
+
+public:
+
+  FEPrism (const CurvedElements & acurv) : BaseFiniteElement3D(acurv)
+  {
+    nvertices = 6;
+    nedges = 9;
+    nfaces = 5;
+    eledge = MeshTopology :: GetEdges (PRISM);
+    elface = MeshTopology :: GetFaces (PRISM);
+  };
+
+  void SetReferencePoint (Point<3> axi);
+
+  virtual void CalcVertexShapes();
+  virtual void CalcEdgeShapes();
+  virtual void CalcFaceShapes();
+};
+
+
+
+
+// ----------------------------------------------------------------------------
+//      FEPyramid
+// ----------------------------------------------------------------------------
+
+class FEPyramid : public BaseFiniteElement3D
+{
+
+  const ELEMENT_EDGE * eledge;
+  const ELEMENT_FACE * elface;
+
+  virtual void CalcNFaceShapes ()
+  {
+    int f;
+    for (f = 0; f < 4; f++)
+      nfaceshapes[f] = ((faceorder[f]-1)*(faceorder[f]-2))/2;
+    for (f = 4; f < nfaces; f++)
+      nfaceshapes[f] = (faceorder[f]-1)*(faceorder[f]-1);
+  };
+
+public:
+
+  FEPyramid (const CurvedElements & acurv) : BaseFiniteElement3D(acurv)
+  {
+    nvertices = 5;
+    nedges = 8;
+    nfaces = 5;
+    eledge = MeshTopology :: GetEdges (PYRAMID);
+    elface = MeshTopology :: GetFaces (PYRAMID);
+  };
+
+  void SetReferencePoint (Point<3> axi);
+
+  virtual void CalcVertexShapes();
+  virtual void CalcEdgeShapes();
+  virtual void CalcFaceShapes();
+};
+
+
+
+
+// ----------------------------------------------------------------------------
+//      FEHex
+// ----------------------------------------------------------------------------
+
+class FEHex : public BaseFiniteElement3D
+{
+
+  const ELEMENT_EDGE * eledge;
+  const ELEMENT_FACE * elface;
+
+  virtual void CalcNFaceShapes ()
+  {
+    int f;
+    for (f = 0; f < 6; f++)
+      nfaceshapes[f] = (faceorder[f]-1)*(faceorder[f]-1);
+  };
+
+public:
+
+  FEHex (const CurvedElements & acurv) : BaseFiniteElement3D(acurv)
+  {
+    nvertices = 8;
+    nedges = 12;
+    nfaces = 6;
+    eledge = MeshTopology :: GetEdges (HEX);
+    elface = MeshTopology :: GetFaces (HEX);
+  };
+
+  void SetReferencePoint (Point<3> axi);
+
+  virtual void CalcVertexShapes();
+  virtual void CalcEdgeShapes();
+  virtual void CalcFaceShapes();
+};
+
+
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/meshing/curvedelems2.cpp b/contrib/Netgen/libsrc/meshing/curvedelems2.cpp
new file mode 100644
index 0000000000..6b79ee5fe9
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/curvedelems2.cpp
@@ -0,0 +1,752 @@
+#include <mystdlib.h>
+
+#include "meshing.hpp"
+
+namespace netgen
+{
+    
+
+// ----------------------------------------------------------------------------
+//      CurvedElements
+// ----------------------------------------------------------------------------
+
+    CurvedElements :: CurvedElements (const Mesh & amesh)
+	: mesh(amesh), top(mesh.GetTopology())
+    {
+	isHighOrder = 0;
+	nvisualsubsecs = 2;
+	nIntegrationPoints = 10;
+    }
+
+
+    CurvedElements :: ~CurvedElements ()
+    {
+      ;
+    }
+
+
+    void CurvedElements :: BuildCurvedElements(Refinement * ref, int polydeg)
+    {
+      if (mesh.coarsemesh)
+	{
+	  mesh.coarsemesh->GetCurvedElements().BuildCurvedElements (ref, polydeg);
+	  SetHighOrder();
+	  return;
+	}
+
+      PrintMessage (2, "Build curved elements, order = ", polydeg);
+
+      NgLock lock(const_cast<Mesh&>(mesh).Mutex(), 1);
+      isHighOrder = 0;
+      lock.UnLock();
+
+	const_cast<Mesh &>(mesh).UpdateTopology();
+
+	// set order of edges and faces
+
+	BaseFiniteElement2D * fe2d;
+
+	FEEdge edge (*this);
+	FESegm segm (*this);
+	FETrig trig (*this);
+	FEQuad quad (*this);
+
+	int i, k, e, f;
+
+	ARRAY<bool> edgedone;
+
+	edgedone.SetSize (top.GetNEdges());
+
+	edgeorder.SetSize (top.GetNEdges());
+	faceorder.SetSize (top.GetNFaces());
+
+	int nedgestocurve = mesh.GetNSeg();
+
+	edgedone = 0;
+	edgeorder = 1;
+	faceorder = 1;
+	
+	/*
+	for (e = 0; e < top.GetNEdges(); e++)
+	  {
+	    edgedone = 0;
+	    edgeorder[e] = 1;
+	  }
+
+	for (f = 0; f < top.GetNFaces(); f++)
+	    faceorder[f] = 1;
+	*/
+
+	for (i = 1; i <= mesh.GetNSeg(); i++) 
+	    edgeorder[top.GetSegmentEdge(i)-1] = polydeg;
+
+
+	if (mesh.GetDimension() == 3)
+	  {
+	    for (i = 1; i <= mesh.GetNSE(); i++)
+	      {
+		faceorder[top.GetSurfaceElementFace(i)-1] = polydeg;
+		
+		Element2d elem = mesh[(SurfaceElementIndex) (i-1)];
+		
+		ARRAY<int> edgenrs;
+		top.GetSurfaceElementEdges(i, edgenrs);
+		
+		nedgestocurve += top.GetNEdges(elem.GetType());
+		
+		for (int e = 0; e < top.GetNEdges(elem.GetType()); e++)
+		  edgeorder[edgenrs[e]-1] = polydeg;
+	      }
+	  }
+
+	if (polydeg == 1)
+	{
+	    isHighOrder = 0;
+	    return;
+	}
+
+	PrintMessage (1, "Building curved elements, order = ", polydeg);
+	PushStatusF ("curving edges");
+
+
+
+        // set edgecoeffs and facecoeffs arrays index and size
+
+	edgecoeffsindex.SetSize (top.GetNEdges()+1);
+	facecoeffsindex.SetSize (top.GetNFaces()+1);
+
+	edgecoeffsindex[0] = 0;
+	for (e = 2; e <= top.GetNEdges()+1; e++)
+	    edgecoeffsindex[e-1] = edgecoeffsindex[e-2] + edgeorder[e-2]-1;
+
+	facecoeffsindex[0] = 0;
+	for (f = 2; f <= top.GetNFaces()+1; f++)
+	{
+	    switch (top.GetFaceType (f-1))
+	    {
+		case TRIG:
+		    facecoeffsindex[f-1] = facecoeffsindex[f-2] + 
+			(faceorder[f-2]-1)*(faceorder[f-2]-2)/2;
+		    break;
+		case QUAD:
+		    facecoeffsindex[f-1] = facecoeffsindex[f-2] +
+			(faceorder[f-2]-1)*(faceorder[f-2]-1);
+		    break;
+	    }
+	}
+
+	edgecoeffs.SetSize(edgecoeffsindex[top.GetNEdges()]);
+	facecoeffs.SetSize(facecoeffsindex[top.GetNFaces()]);
+
+
+        
+	// evaluate edge points
+
+	PointGeomInfo newgi;          // dummy variable, only needed for function call
+	EdgePointGeomInfo newepgi;    // dummy variable, only needed for function call
+	Point3d xexact;               // new point to be stored in ARRAY edgepts
+
+	ARRAY<double> xi, wi;
+	ComputeGaussRule(nIntegrationPoints, xi, wi);
+
+	for (i=0; i<edgecoeffsindex[top.GetNEdges()]; i++)
+	    edgecoeffs[i] = Vec<3>(0.,0.,0.);
+
+
+
+
+	// all edges belonging to segments
+
+	for (i=0; i<mesh.GetNSeg(); i++) 
+	{
+	  if (multithread.terminate) return;
+
+	  SetThreadPercent( double(100*i/nedgestocurve) );
+
+	  int edgenr = top.GetSegmentEdge(i+1);
+
+	  if (edgedone[edgenr-1]) continue;
+
+	  edgedone[edgenr-1] = 1;
+
+            Segment s = mesh.LineSegment(i+1); 
+
+	    segm.SetElementNumber (i+1);
+	
+	    for (k = 2; k <= segm.GetEdgeOrder(); k++)
+	      edgecoeffs[edgecoeffsindex[edgenr-1]+k-2] = Vec<3>(0.,0.,0.);
+
+	    for (int l = 0; l < nIntegrationPoints; l++)
+	      {
+		segm.SetReferencePoint (Point<1>(xi[l]));
+		segm.CalcVertexShapes ();
+		segm.CalcEdgeLaplaceShapes ();
+		
+		Point<3> xv(0,0,0);
+
+		for (int v = 0; v < 2; v++)
+		  xv = xv + segm.GetVertexShape(v) * mesh.Point(segm.GetVertexNr(v));
+		  		
+		double secpoint = xi[l];
+
+		ref->PointBetween (mesh.Point(segm.GetVertexNr(1)),
+				   mesh.Point(segm.GetVertexNr(0)), secpoint,
+				   s.surfnr2, s.surfnr1,
+				   s.epgeominfo[1], s.epgeominfo[0],
+				   xexact, newepgi);
+		
+		for (int k = 2; k <= segm.GetEdgeOrder(); k++)
+		  edgecoeffs[edgecoeffsindex[edgenr-1]+k-2] -=
+		    wi[l] * segm.GetEdgeLaplaceShape(k-2) * Vec<3>(xexact - xv);
+		
+	      }
+	    
+	    for (k = 2; k <= segm.GetEdgeOrder(); k++)
+	      edgecoeffs[edgecoeffsindex[edgenr-1]+k-2] =
+		(2.0*(k-1.0)+1.0)*edgecoeffs[edgecoeffsindex[edgenr-1]+k-2];
+	
+	}
+
+
+
+
+
+	// all edges belonging to surface elements
+	
+	if (mesh.GetDimension() == 3)
+	  {
+	    int nedgescurved = mesh.GetNSeg();
+	    for (int i=0; i<mesh.GetNSE(); i++) 
+	      {
+		if (multithread.terminate) return;
+		
+		//		SetThreadPercent( double(100*(mesh.GetNSeg()+i)/nedgestocurve) );
+		Element2d elem = mesh[(SurfaceElementIndex) i];
+		const ELEMENT_EDGE * eledges = MeshTopology::GetEdges(elem.GetType());
+		
+		ARRAY<int> edgenrs;
+		ARRAY<int> orient;
+		top.GetSurfaceElementEdges(i+1, edgenrs);
+		top.GetSurfaceElementEdgeOrientations(i+1, orient);
+
+		for (int e = 0; e < top.GetNEdges(elem.GetType()); e++)
+		  {
+//		    cout << "e = " << e << "/" << top.GetNEdges(elem.GetType()) <<  endl;
+
+		    nedgescurved++;
+
+		    if (edgedone[edgenrs[e]-1]) continue;
+		    
+		    edgedone[edgenrs[e]-1] = 1;
+
+		    SetThreadPercent( double(100*(nedgescurved)/nedgestocurve) );
+
+		    edge.SetElementNumber (edgenrs[e]);
+
+		    for (k = 2; k <= edge.GetEdgeOrder(); k++)
+		      edgecoeffs[edgecoeffsindex[edgenrs[e]-1]+k-2] = Vec<3>(0.,0.,0.);
+
+		    for (int l = 0; l < nIntegrationPoints; l++)
+		      {
+//			cout << "." << flush;
+			edge.SetReferencePoint (Point<1>(xi[l]));
+			edge.CalcVertexShapes ();
+			edge.CalcEdgeLaplaceShapes ();
+			
+			Point<3> xv(0,0,0);
+			for (int v = 0; v < 2; v++)
+			  xv = xv + edge.GetVertexShape(v) * mesh.Point(edge.GetVertexNr(v));
+
+			double secpoint = xi[l];
+
+			if (orient[e] == 1)
+			  ref->PointBetween (mesh.Point(edge.GetVertexNr(1)),
+					     mesh.Point(edge.GetVertexNr(0)), secpoint,
+					     mesh.GetFaceDescriptor(elem.GetIndex()).SurfNr(),
+					     elem.GeomInfoPi(eledges[e][1]),
+					     elem.GeomInfoPi(eledges[e][0]),
+					     xexact, newgi);
+			else
+			  ref->PointBetween (mesh.Point(edge.GetVertexNr(1)),
+					     mesh.Point(edge.GetVertexNr(0)), secpoint,
+					     mesh.GetFaceDescriptor(elem.GetIndex()).SurfNr(),
+					     elem.GeomInfoPi(eledges[e][0]),
+					     elem.GeomInfoPi(eledges[e][1]),
+					     xexact, newgi);
+
+			for (k = 2; k <= edge.GetEdgeOrder(); k++)
+			  edgecoeffs[edgecoeffsindex[edgenrs[e]-1]+k-2] -=
+			    wi[l] * edge.GetEdgeLaplaceShape(k-2) * Vec<3>(xexact - xv);
+		      }	
+//		    cout << endl;
+		    for (k = 2; k <= edge.GetEdgeOrder(); k++)
+		      edgecoeffs[edgecoeffsindex[edgenrs[e]-1]+k-2] =
+			(2.0*(k-1.0)+1.0)*edgecoeffs[edgecoeffsindex[edgenrs[e]-1]+k-2];
+		    
+		}
+	      }
+	  }
+
+
+
+
+/*
+
+	// L2-Projection for edges
+
+
+	cout << "WARNING: L2-Projection for edges" << endl;
+
+	if (mesh.GetDimension() == 3)
+	{
+	    for (int i=0; i<mesh.GetNSE(); i++) 
+	    {
+		Element2d elem = mesh[(SurfaceElementIndex) i];
+		const ELEMENT_EDGE * eledges = MeshTopology::GetEdges(elem.GetType());
+		
+		ARRAY<int> edgenrs;
+		ARRAY<int> orient;
+		top.GetSurfaceElementEdges(i+1, edgenrs);
+		top.GetSurfaceElementEdgeOrientations(i+1, orient);
+		
+		for (int e = 0; e < top.GetNEdges(elem.GetType()); e++)
+		{
+		    edge.SetElementNumber (edgenrs[e]);
+
+		    int npoints = edge.GetEdgeOrder()-1;
+
+		    if (npoints == 0) continue;
+
+		    DenseMatrix mat(npoints);
+		    DenseMatrix inv(npoints);
+		    Vector vec[3];
+	    
+		    for (int k = 0; k < 3; k++)
+		    {
+			vec[k].SetSize(npoints);
+			for (int n = 1; n <= npoints; n++) vec[k].Set(n, 0.);
+		    }
+		    
+		    for (int l = 0; l < nIntegrationPoints; l++)
+		    {
+			double w = wi[l];
+			
+			edge.SetReferencePoint (Point<1>(xi[l]));
+			edge.CalcVertexShapes ();
+			edge.CalcEdgeShapes ();
+		
+			for (int n = 0; n < npoints; n++)
+			    for (int m = 0; m < npoints; m++)
+				mat.Set(n+1, m+1, mat.Get(n+1,m+1) +
+					edge.GetEdgeShape(n) * edge.GetEdgeShape(m) * w);
+		
+			Point<3> xv(0,0,0);
+			for (int v = 0; v < 2; v++)
+			    xv = xv + edge.GetVertexShape(v) * mesh.Point(edge.GetVertexNr(v));
+			
+			double secpoint = xi[l];
+			
+			ref->PointBetween (mesh.Point(edge.GetVertexNr(1)),
+					   mesh.Point(edge.GetVertexNr(0)), secpoint,
+					   mesh.GetFaceDescriptor(elem.GetIndex()).SurfNr(),
+					   elem.GeomInfoPi(eledges[e][1]),
+					   elem.GeomInfoPi(eledges[e][0]),
+					   xexact, newgi);
+		
+			for (int k = 2; k <= edge.GetEdgeOrder(); k++)
+			{
+			    vec[0].Set(k-1, vec[0].Get(k-1) + Vec<3>(xexact - xv)(0)*edge.GetEdgeShape(k-2)*w );
+			    vec[1].Set(k-1, vec[1].Get(k-1) + Vec<3>(xexact - xv)(1)*edge.GetEdgeShape(k-2)*w );
+			    vec[2].Set(k-1, vec[2].Get(k-1) + Vec<3>(xexact - xv)(2)*edge.GetEdgeShape(k-2)*w );
+			}
+		
+		    }
+
+
+		    CalcInverse(mat,inv);
+	    
+		    Vector a0, a1, a2;
+		    
+		    a0 = inv*vec[0];
+		    a1 = inv*vec[1];
+		    a2 = inv*vec[2];
+
+		    int index = edgecoeffsindex[edge.GetEdgeNr()-1];
+
+		    for (int n = 0; n < npoints; n++, index++)
+			edgecoeffs[index] =  Vec<3>(a0(n+1), a1(n+1), a2(n+1));
+		}
+	    }
+	}
+
+
+	for (int i=0; i<mesh.GetNSeg(); i++) 
+	{
+	    int edgenr = top.GetSegmentEdge(i+1);
+
+            Segment s = mesh.LineSegment(i+1); 
+
+	    segm.SetElementNumber (i+1);
+
+	    int npoints = segm.GetEdgeOrder()-1;
+
+	    if (npoints == 0) continue;
+
+	    DenseMatrix mat(npoints);
+	    DenseMatrix inv(npoints);
+	    Vector vec[3];
+
+	    for (int k = 0; k < 3; k++)
+	    {
+		vec[k].SetSize(npoints);
+		for (int n = 1; n <= npoints; n++) vec[k].Set(n, 0.);
+	    }
+	
+	    for (int l = 0; l < nIntegrationPoints; l++)
+	    {
+		double w = wi[l];
+
+		segm.SetReferencePoint (Point<1>(xi[l]));
+		segm.CalcVertexShapes ();
+		segm.CalcEdgeShapes ();
+		
+		for (int n = 0; n < npoints; n++)
+		    for (int m = 0; m < npoints; m++)
+			mat.Set(n+1, m+1, mat.Get(n+1,m+1) +
+				segm.GetEdgeShape(n) * segm.GetEdgeShape(m) * w);
+		
+		Point<3> xv(0,0,0);
+		for (int v = 0; v < 2; v++)
+		    xv = xv + segm.GetVertexShape(v) * mesh.Point(segm.GetVertexNr(v));
+		
+		double secpoint = xi[l];
+		
+		if (segm.GetEdgeOrientation() == -1) secpoint = 1. - secpoint; // reverse orientation
+		
+		ref->PointBetween (mesh.Point(segm.GetVertexNr(1)),
+				   mesh.Point(segm.GetVertexNr(0)), secpoint,
+				   s.surfnr2, s.surfnr1,
+				   s.epgeominfo[1], s.epgeominfo[0],
+				   xexact, newepgi);
+		
+		for (int k = 2; k <= segm.GetEdgeOrder(); k++)
+		{
+		    vec[0].Set(k-1, vec[0].Get(k-1) + Vec<3>(xexact - xv)(0)*segm.GetEdgeShape(k-2)*w );
+		    vec[1].Set(k-1, vec[1].Get(k-1) + Vec<3>(xexact - xv)(1)*segm.GetEdgeShape(k-2)*w );
+		    vec[2].Set(k-1, vec[2].Get(k-1) + Vec<3>(xexact - xv)(2)*segm.GetEdgeShape(k-2)*w );
+		}
+		
+	    }
+
+
+	    CalcInverse(mat,inv);
+	    
+	    Vector a0, a1, a2;
+
+	    a0 = inv*vec[0];
+	    a1 = inv*vec[1];
+	    a2 = inv*vec[2];
+
+	    int index = edgecoeffsindex[segm.GetEdgeNr()-1];
+
+	    for (int n = 0; n < npoints; n++, index++)
+		edgecoeffs[index] =  Vec<3>(a0(n+1), a1(n+1), a2(n+1));
+
+
+
+	}
+
+*/
+
+
+
+
+
+	// evaluate face points
+
+	if (mesh.GetDimension() == 3)
+	  {
+	    PopStatus ();
+	    PushStatusF ("curving faces");
+	    
+	    for (int j=0; j<facecoeffsindex[top.GetNFaces()]; j++)
+	      facecoeffs[j] = Vec<3>(0.,0.,0.);
+	    
+	    for (SurfaceElementIndex i = 0; i < mesh.GetNSE(); i++)   // for all surface elements
+	      {
+		if (multithread.terminate) return;
+
+	        SetThreadPercent( double(100*i/mesh.GetNSE()) );
+
+		Element2d elem = mesh[i];
+		
+		if (elem.GetType() == TRIG)
+		    fe2d = &trig;
+                else
+		    fe2d = &quad;
+
+		fe2d->SetElementNumber (i+1);
+
+		int npoints = fe2d->GetNFaceShapes();
+
+		if (npoints == 0) continue;
+
+		DenseMatrix mat(npoints);
+		DenseMatrix inv(npoints);
+		Vector vec[3];
+
+		for (int k = 0; k < 3; k++)
+		{
+		    vec[k].SetSize(npoints);
+		    for (int n = 1; n <= npoints; n++) vec[k].Set(n, 0.);
+		}
+
+		for (int j = 0; j < nIntegrationPoints; j++)
+		{
+		    for (int k = 0; k < nIntegrationPoints; k++)
+		    {
+			double w;
+			Point<2> xr;
+
+			if (elem.GetType() == TRIG)
+			  {
+			    w = wi[j]*wi[k]*(1-xi[j]);
+			    xr = Point<2> (xi[j], xi[k]*(1-xi[j]));
+			  }
+			else
+			  {
+			    w = wi[j]*wi[k];
+			    xr = Point<2> (xi[j], xi[k]);
+			  }
+
+			fe2d->SetReferencePoint (xr);
+			fe2d->CalcFaceShapes ();
+			fe2d->CalcVertexShapes ();
+			fe2d->CalcEdgeShapes ();
+			fe2d->CalcFaceLaplaceShapes ();
+
+			// integration over the product of the gradients of the face shapes
+
+			for (int n = 0; n < npoints; n++)
+			  for (int m = 0; m < npoints; m++)
+			    mat.Set(n+1, m+1,
+				    mat.Get(n+1,m+1) +
+				    fe2d->GetFaceDShape(n)*fe2d->GetFaceDShape(m)*w);
+
+			// integration over the difference between the exact geometry and the one
+			// defined by vertex and edge shape functions times face shape
+
+			// double giu = 0, giv = 0;
+			PointGeomInfo gi;
+			gi.trignum = elem.GeomInfoPi(1).trignum;
+			gi.u = 0.0;
+			gi.v = 0.0;
+			Point<3> xve(0.,0.,0.);
+
+			// vertex shape functions
+			for (int v = 0; v < fe2d->GetNVertices(); v++)
+			  {
+			    xve = xve + fe2d->GetVertexShape(v) * mesh.Point(fe2d->GetVertexNr(v));
+			    gi.u += fe2d->GetVertexShape(v) * elem.GeomInfoPi(v+1).u;
+			    gi.v += fe2d->GetVertexShape(v) * elem.GeomInfoPi(v+1).v;
+			  }
+
+			// edge shape functions
+			int index = 0;
+			for (int e = 0; e < fe2d->GetNEdges(); e++)
+			  {
+			    int gindex = edgecoeffsindex[fe2d->GetEdgeNr(e)-1];
+			    for (int k = 2; k <= fe2d->GetEdgeOrder(e); k++, index++, gindex++)
+			      xve = xve + fe2d->GetEdgeShape(index) * edgecoeffs[gindex];
+			  }
+
+			// exact point
+
+			Point<3> xexact = xve;
+			ref->ProjectToSurface (xexact, mesh.GetFaceDescriptor(elem.GetIndex()).SurfNr(), gi);
+
+			Vec<3> v2 = w*(Vec<3>(xexact)-Vec<3>(xve));
+
+			for (int k = 0; k < 3; k++)
+			  for (int n = 0; n < npoints; n++)
+			    vec[k].Set(n+1, vec[k].Get(n+1) - fe2d->GetFaceLaplaceShape(n)*v2(k));
+		    }
+		}
+
+		CalcInverse(mat,inv);
+		
+		Vector a0(npoints), a1(npoints), a2(npoints);
+		
+		/*
+		a0 = inv*vec[0];
+		a1 = inv*vec[1];
+		a2 = inv*vec[2];
+		*/
+		inv.Mult (vec[0], a0);
+		inv.Mult (vec[1], a1);
+		inv.Mult (vec[2], a2);
+
+		int index = facecoeffsindex[fe2d->GetFaceNr()-1];
+		
+		for (int n = 0; n < npoints; n++, index++)
+		  facecoeffs[index] =  Vec<3>(a0.Elem(n+1), a1.Elem(n+1), a2.Elem(n+1));
+	      }
+	  }
+	
+
+
+	
+/*
+	cout << "WARNING: L2-Projection for faces" << endl;
+
+	// evaluate face points
+
+	if (mesh.GetDimension() == 3)
+	{
+	    for (int i=0; i<facecoeffsindex[top.GetNFaces()]; i++)
+		facecoeffs[i] = Vec<3>(0.,0.,0.);
+
+	    for (SurfaceElementIndex i = 0; i < mesh.GetNSE(); i++)   // for all surface elements
+	    {
+		Element2d elem = mesh[i];
+		
+		if (elem.GetType() == TRIG)
+		    fe2d = &trig;
+                else
+		    fe2d = &quad;
+
+		fe2d->SetElementNumber (i+1);
+
+		int npoints = fe2d->GetNFaceShapes();
+
+		if (npoints == 0) continue;
+
+		DenseMatrix mat(npoints);
+		DenseMatrix inv(npoints);
+		Vector vec[3];
+
+		for (int k = 0; k < 3; k++)
+		{
+		    vec[k].SetSize(npoints);
+		    for (int n = 1; n <= npoints; n++) vec[k].Set(n, 0.);
+		}
+
+		for (int j = 0; j < nIntegrationPoints; j++)
+		{
+		    for (int k = 0; k < nIntegrationPoints; k++)
+		    {
+			double w;
+			Point<2> xr;
+
+			if (elem.GetType() == TRIG)
+			{
+			    w = wi[j]*wi[k]*(1-xi[j]);
+			    xr = Point<2> (xi[j], xi[k]*(1-xi[j]));
+			}
+			else
+			{
+			    w = wi[j]*wi[k];
+			    xr = Point<2> (xi[j], xi[k]);
+			}
+
+			fe2d->SetReferencePoint (xr);
+//			fe2d->CalcFaceDShape (false, true);
+			fe2d->CalcFaceShapes ();
+
+			// integration over the product of the gradients of the face shapes
+
+			for (int n = 0; n < npoints; n++)
+			    for (int m = 0; m < npoints; m++)
+				    mat.Set(n+1, m+1, mat.Get(n+1,m+1) +
+					    fe2d->GetFaceShape(n)*fe2d->GetFaceShape(m)*w);
+
+			// integration over the difference between the exact geometry and the one
+			// defined by vertex and edge shape functions times face shape
+
+			Point<3> xve(0.,0.,0.);
+
+			// vertex shape functions
+			fe2d->CalcVertexShapes ();
+//			fe2d->CalcVertexShape (true, false);
+			for (int v = 0; v < fe2d->GetNVertices(); v++)
+			    xve = xve + fe2d->GetVertexShape(v) * mesh.Point(fe2d->GetVertexNr(v));
+
+			// edge shape functions
+//			fe2d->CalcEdgeShape (true, false);
+			fe2d->CalcEdgeShapes ();
+
+			int index = 0;
+			for (int e = 0; e < fe2d->GetNEdges(); e++)
+			{
+			    int gindex = edgecoeffsindex[fe2d->GetEdgeNr(e)-1];
+
+			    for (int k = 2; k <= fe2d->GetEdgeOrder(e); k++, index++, gindex++)
+				xve = xve + fe2d->GetEdgeShape(index) * edgecoeffs[gindex];
+			}
+
+			// exact point
+
+			Point<3> xexact = xve;
+			ref->ProjectToSurface (xexact, mesh.GetFaceDescriptor(elem.GetIndex()).SurfNr());
+
+			Vec<3> v = w*(Vec<3>(xexact)-Vec<3>(xve));
+
+			fe2d->CalcFaceLaplaceShapes ();
+
+			for (int k = 0; k < 3; k++)
+			    for (int n = 0; n < npoints; n++)
+				vec[k].Set(n+1, vec[k].Get(n+1) + fe2d->GetFaceShape(n)*v(k));
+			}
+		    }
+
+ 		    CalcInverse(mat,inv);
+
+		    Vector a0, a1, a2;
+
+		    a0 = inv*vec[0];
+		    a1 = inv*vec[1];
+		    a2 = inv*vec[2];
+
+		    int index = facecoeffsindex[fe2d->GetFaceNr()-1];
+
+		    for (int n = 0; n < npoints; n++, index++)
+			facecoeffs[index] =  Vec<3>(a0(n+1), a1(n+1), a2(n+1));
+	    }
+	}
+*/
+
+
+    PrintMessage (5, "reducing order");
+   
+    for (e = 0; e < top.GetNEdges(); e++)
+      if (edgeorder[e] > 1)
+	{
+	  int i;
+	  double maxcoeff = 0.;
+
+	  for (i = edgecoeffsindex[e]; i < edgecoeffsindex[e+1]; i++)
+	    maxcoeff = max2 (maxcoeff, edgecoeffs[i].Length());
+
+	  if (maxcoeff < 1e-12) edgeorder[e] = 1;
+	}
+
+    for (f = 0; f < top.GetNFaces(); f++)
+      if (faceorder[f] > 1)
+	{
+	  int i;
+	  double maxcoeff = 0.;
+
+	  for (i = facecoeffsindex[f]; i < facecoeffsindex[f+1]; i++)
+	    maxcoeff = max (maxcoeff, facecoeffs[i].Length());
+
+	  if (maxcoeff < 1e-12) faceorder[f] = 1;
+	}
+
+    isHighOrder = 1;              // true
+
+    PrintMessage(1, "done");
+    PopStatus();
+    //	cout << "finished" << endl;
+    }
+
+} // namespace netgen
diff --git a/contrib/Netgen/libsrc/meshing/delaunay.cpp b/contrib/Netgen/libsrc/meshing/delaunay.cpp
new file mode 100644
index 0000000000..505a8c193f
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/delaunay.cpp
@@ -0,0 +1,1683 @@
+#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<Point3d> & centers, ARRAY<double> & radi2,
+			 ARRAY<int> & connected, ARRAY<int> & treesearch, 
+			 ARRAY<int> & freelist, SphereList & list,
+			 IndexSet & insphere, IndexSet & closesphere)
+  {
+    int i, j, k, l;
+  
+    /*
+      find any sphere, such that newp is contained in
+    */
+  
+    DelaunayTet el;
+    int cfelind = -1;
+
+    const Point3d * pp[4];
+    Point3d pc;
+    double r2;
+    Point3d tpmin, tpmax;
+
+    tettree.GetIntersecting (newp, newp, treesearch);
+    for (j = 0; j < treesearch.Size(); j++)
+      {
+	int jjj = treesearch[j];
+	if (Dist2 (centers.Get(jjj), newp) < radi2.Get(jjj))
+	  {
+	    el = tempels.Get(jjj);
+	    cfelind = jjj;
+	    break;
+	  }
+      }
+  
+    /*
+      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.Array().Size()+1;
+
+	// if point in sphere, then it is also closesphere
+	for (j = starti; j < nstarti; j++)
+	  {
+	    int helind = insphere.Array().Get(j);
+	    if (!closesphere.IsIn (helind))
+	      closesphere.Add (helind);
+	  }
+
+	// add connected spheres to insphere - list
+	for (j = starti; j < nstarti; j++)
+	  {
+	    list.GetList (insphere.Array().Get(j), connected);
+	    for (k = 1; k <= connected.Size(); k++)
+	      {
+		int celind = connected.Get(k);
+
+		if (tempels.Get(celind)[0] != -1 && 
+		    !insphere.IsIn (celind))
+		  {
+		    changed = 1;
+		    insphere.Add (celind);
+		  }
+	      }
+	  }
+
+	// check neighbour-tets
+	for (j = starti; j < nstarti; j++)
+	  for (k = 1; k <= 4; k++)
+	    {
+	      int helind = insphere.Array().Get(j);
+	      int nbind = meshnb.GetNB (helind, k);
+
+	      if (nbind && !insphere.IsIn (nbind) )
+		{
+		  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 (j = 1; j <= insphere.Array().Size(); j++)
+      for (k = 1; k <= 4; k++)
+	{
+	  //	    int elind = insphere.Array().Get(j);
+	  int celind = insphere.Array().Get(j);
+	  int nbind = meshnb.GetNB (celind, k);
+
+	  if (!nbind || !insphere.IsIn (nbind))
+	    {
+	      tempels.Get (celind).GetFace1 (k, face);
+		
+	      Element newel(TET);
+	      for (l = 1; l <= 3; l++)
+		newel.PNum(l) = face.PNum(l);
+	      newel[3] = newpi;
+
+	      newels.Append (newel);
+
+	      Vec3d v1(mesh.Point (face[0]), mesh.Point (face[1]));
+	      Vec3d v2(mesh.Point (face[0]), mesh.Point (face[2]));
+	      Vec3d n = Cross (v1, v2);
+	      n /= n.Length();
+	      if (n * Vec3d(mesh.Point (face[0]), 
+			    mesh.Point (tempels.Get(insphere.Array().Get(j))[k-1]))
+		  > 0)
+		n *= -1;
+
+	      double hval = n * Vec3d (mesh.Point (face[0]), newp);
+		
+	      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.Array().Size()+1);
+
+    for (j = 1; j <= insphere.Array().Size(); j++)
+      {
+	//	  int elind = 
+	int celind = insphere.Array().Get(j);
+
+	meshnb.Delete (celind); 
+	list.DeleteElement (celind);
+	  
+	for (k = 0; k < 4; k++)
+	  tempels.Elem(celind)[k] = -1;
+
+	((ADTree6&)tettree.Tree()).DeleteElement (celind);
+	freelist.Append (celind);
+      }
+
+
+    int hasclose = 0;
+    for (j = 1; j <= closesphere.Array().Size(); j++)
+      {
+	int ind = closesphere.Array().Get(j);
+	if (!insphere.IsIn(ind) &&
+	    fabs (Dist2 (centers.Get (ind), newp) - radi2.Get(ind)) < 1e-8 )
+	  hasclose = 1;
+      }
+
+    for (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 (k = 1; k <= 4; k++)
+	  pp[k-1] = &mesh.Point (newels.Get(j).PNum(k));
+
+	if (CalcSphereCenter (&pp[0], pc) )
+	  {
+	    PrintSysError ("Delaunay: New tet is flat");
+
+	    (*testout) << "new tet is flat" << endl;
+	    for (k = 1; k <= 4; k++)
+	      (*testout) << newels.Get(j).PNum(k) << " ";
+	    (*testout) << endl;
+	    for (k = 1; k <= 4; k++)
+	      (*testout) << *pp[k-1] << " ";
+	    (*testout) << endl;
+	  }
+
+	r2 = Dist2 (*pp[0], pc);
+	if (hasclose)
+	  for (k = 1; k <= closesphere.Array().Size(); k++)
+	    {
+	      int csameind = closesphere.Array().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 (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, l;
+    const Point3d * pp[4];
+
+    ARRAY<Point3d> 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 Element2d & 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 Element2d & 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);
+
+
+    Point3d 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());
+
+
+
+#ifdef MARK
+    MARK (delaunay1);
+#endif
+
+
+    // "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 % 100 == 0)
+	  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);
+      }
+
+#ifdef MARK
+    MARK (delaunay3);
+#endif  
+
+
+
+    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, const MeshingParameters & mp)
+  {
+    PointIndex pi;
+    int i, j, k, l;
+    int ii;
+    int np, ne;
+
+    PrintMessage (1, "Delaunay meshing");
+    PrintMessage (3, "number of points: ", mesh.GetNP());
+    PushStatus ("Delaunay meshing");
+
+
+
+    ARRAY<DelaunayTet> tempels;
+
+    Point3d pmin, pmax;
+    Point3d tpmin, tpmax;
+    const Point3d * pp[4];
+  
+  
+    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 (pi = PointIndex::BASE; pi < mesh.GetNP()+PointIndex::BASE; pi++)
+	tempmesh.AddPoint (mesh[pi]);
+      
+      for (i = 1; i <= tempels.Size(); i++)
+	{   
+	  Element el(4);
+	  for (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);
+    
+      for (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 (i = 1; i <= 4; i++)
+	{
+	  Element2d self(TRIG);
+	  self.SetIndex (1);
+	  startel.GetFace1 (i, self);
+	  tempmesh.AddSurfaceElement (self);
+	}
+
+      
+      tempmesh.AddFaceDescriptor (FaceDescriptor (1, 1, 0, 0));
+      tempmesh.AddFaceDescriptor (FaceDescriptor (2, 1, 0, 0));
+
+
+      //  for (i = mesh.GetNP() - 3; i <= mesh.GetNP(); i++)
+      //    tempmesh.AddLockedPoint (i);
+      for (pi = PointIndex::BASE; 
+	   pi < tempmesh.GetNP() + PointIndex::BASE; pi++)
+	tempmesh.AddLockedPoint (pi);
+
+      //    tempmesh.PrintMemInfo(cout);
+      // tempmesh.Save ("tempmesh.vol");
+
+      for (i = 1; i <= 2; i++)
+	{ 
+	  tempmesh.FindOpenElements ();
+
+	  PrintMessage (5, "Num open: ", tempmesh.GetNOpenElements());
+	  tempmesh.CalcSurfacesOfNode ();
+
+	  tempmesh.FreeOpenElementsEnvironment (1);
+
+	  MeshOptimize3d meshopt;
+	  meshopt.SwapImprove(tempmesh, OPT_CONFORM);
+	}
+    
+
+#ifdef STAT_STREAM
+      tempmesh.FindOpenElements ();
+      PrintMessage (5, "Num open: ", tempmesh.GetNOpenElements());
+      (*statout) << tempmesh.GetNOpenElements() << " & " << endl;
+#endif   
+
+      MeshQuality3d (tempmesh);
+    
+      tempels.SetSize(0);
+      for (i = 1; i <= tempmesh.GetNE(); i++)
+	tempels.Append (tempmesh.VolumeElement(i));
+    }
+
+
+
+    // remove degenerated
+
+    BitArray badnode(mesh.GetNP());
+    badnode.Clear();
+    int ndeg = 0;
+    for (i = 1; i <= tempels.Size(); i++)
+      {
+	Element el(4);
+	for (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 (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 (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 (i = 1; i <= tempels.Size(); i++)
+      {
+	for (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 (i = 1; i <= openeltab.GetNBags(); i++)
+      for (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 (ii = 1; ii <= openels.Size(); ii++)
+      {
+	i = openels.Get(ii);
+	const Element2d & el = mesh.OpenElement(i);
+	for (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 (i = 1; i <= tempels.Size(); i++)
+      {
+	const DelaunayTet & el = tempels.Get(i);
+	INDEX_2 i2;
+	for (j = 1; j <= 6; j++)
+	  {
+	    switch (j)
+	      {
+	      case 1: i2 = INDEX_2(el[0], el[1]); break;
+	      case 2: i2 = INDEX_2(el[0], el[2]); break;
+	      case 3: i2 = INDEX_2(el[0], el[3]); break;
+	      case 4: i2 = INDEX_2(el[1], el[2]); break;
+	      case 5: i2 = INDEX_2(el[1], el[3]); break;
+	      case 6: i2 = INDEX_2(el[2], el[3]); 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 (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 (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 (tpmin);
+	      
+		for (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 (i = 1; i <= tempels.Size(); i++)
+	  {
+	    //      const Point3d *pp[4];
+	    int tetpi[4];
+	    DelaunayTet & el = tempels.Elem(i);
+	  
+	    int intersect = 0;
+	  
+	    for (j = 0; j < 4; j++)
+	      {
+		pp[j] = &mesh.Point(el[j]);
+		tetpi[j] = el[j];
+	      }
+	  
+	    Point3d tetpmin(*pp[0]);
+	    Point3d tetpmax(tetpmin);
+	    for (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++)
+	      {
+		j = neartrias.Get(jj);
+	      
+		const Element2d & tri = mesh.OpenElement(j);
+		const Point3d *tripp[3];
+		int tripi[3];
+	      
+		for (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--;
+	      }
+	  }
+      }
+  
+
+#ifdef MARK
+    MARK (delaunay4);
+#endif  
+
+
+
+
+
+
+
+
+    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 (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 (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 (i = 0; i < tempels.Size(); i++)
+      for (j = 0; j < 4; j++)
+	tempels[i].NB(j) = 0;
+  
+    TABLE<int,PointIndex::BASE> elsonpoint(mesh.GetNP());
+    for (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 (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 (pi = PointIndex::BASE; 
+	 pi < mesh.GetNP()+PointIndex::BASE; pi++)
+      {
+	faceht.SetSize (4 * elsonpoint[pi].Size());
+	for (ii = 0; ii < elsonpoint[pi].Size(); ii++)
+	  {
+	    i = elsonpoint[pi][ii];
+	    const DelaunayTet & el = tempels.Get(i);
+
+	    for (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 i3(hel[0], hel[1], hel[2]);
+			    INDEX_2 i2(i, j);
+			    faceht.Set (i3, 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;
+
+	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 (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 (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 (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 (i = ne; i >= 1; i--)
+      {
+	if (outer.Test(i))
+	  tempels.DeleteElement(i);
+      }
+
+
+    //  (*mycout) << "done" << endl;
+
+
+    // mesh.points.SetSize(mesh.points.Size()-4);
+
+    for (i = 1; i <= tempels.Size(); i++)
+      {
+	Element el(4);
+	for (j = 0; j < 4; j++)
+	  el[j] = tempels.Elem(i)[j];
+	mesh.AddVolumeElement (el);
+      }
+
+    PrintMessage (5, "outer removed");
+
+    mesh.FindOpenElements();
+
+    mesh.Compress();
+
+    PopStatus ();
+  }
+}
diff --git a/contrib/Netgen/libsrc/meshing/findip.cpp b/contrib/Netgen/libsrc/meshing/findip.cpp
new file mode 100644
index 0000000000..7be0ee913d
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/findip.cpp
@@ -0,0 +1,115 @@
+// find inner point
+
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+namespace netgen
+{
+
+
+template <typename POINTARRAY, typename FACEARRAY>
+int FindInnerPoint (POINTARRAY & points,
+		    FACEARRAY & faces,
+		    Point3d & p)
+{
+  int i, j;
+  ARRAY<Vec3d> a;
+  ARRAY<double> c;
+  Point3d p1, pmin;
+  int i1, i2, i3, i4;
+  int nf;
+  DenseMatrix m(3), inv(3);
+  Vector rs(3), x(3);
+  double f, fmin, hd, hmax;
+
+  nf = faces.Size();
+
+  //  testout << "#faces  = " << faces.Size() << endl;
+  //  testout << "#points = " << points.Size() << endl;
+
+  a.SetSize (nf);
+  c.SetSize (nf);
+
+  for (i = 1; i <= nf; i++)
+    {
+      p1 = points.Get(faces.Get(i).PNum(1));
+      a.Elem(i) = Cross (points.Get(faces.Get(i).PNum(2)) - points.Get(faces.Get(i).PNum(1)),
+		    points.Get(faces.Get(i).PNum(3)) - points.Get(faces.Get(i).PNum(1)));
+      a.Elem(i) /= a.Get(i).Length();
+      c.Elem(i) = - (a.Get(i).X() * p1.X() + a.Get(i).Y() * p1.Y() + a.Get(i).Z() * p1.Z());
+    }
+
+
+  hmax = 0;
+  for (i = 1; i <= nf; i++)
+    {
+      const Element2d & el = faces.Get(i);
+      for (j = 1; j <= 3; j++)
+	{
+	  double hi = Dist (points.Get(el.PNumMod(j)),
+			    points.Get(el.PNumMod(j+1)));
+	  if (hi > hmax) hmax = hi;
+	}
+    }
+
+
+  fmin = 100;
+  pmin = Point3d (0, 0, 0);
+
+  for (i1 = 1; i1 <= nf; i1++)
+    for (i2 = i1+1; i2 <= nf; i2++)
+      for (i3 = i2+1; i3 <= nf; i3++)
+        for (i4 = i3+1; i4 <= nf; i4++)
+          {
+	    m.Elem(1, 1) = a.Get(i1).X() - a.Get(i2).X();
+	    m.Elem(1, 2) = a.Get(i1).Y() - a.Get(i2).Y();
+	    m.Elem(1, 3) = a.Get(i1).Z() - a.Get(i2).Z();
+	    rs.Elem(1) = c.Get(i2) - c.Get(i1);
+
+	    m.Elem(2, 1) = a.Get(i1).X() - a.Get(i3).X();
+	    m.Elem(2, 2) = a.Get(i1).Y() - a.Get(i3).Y();
+	    m.Elem(2, 3) = a.Get(i1).Z() - a.Get(i3).Z();
+	    rs.Elem(2) = c.Get(i3) - c.Get(i1);
+
+	    m.Elem(3, 1) = a.Get(i1).X() - a.Get(i4).X();
+	    m.Elem(3, 2) = a.Get(i1).Y() - a.Get(i4).Y();
+	    m.Elem(3, 3) = a.Get(i1).Z() - a.Get(i4).Z();
+	    rs.Elem(3) = c.Get(i4) - c.Get(i1);
+
+
+	    if (fabs (m.Det()) > 1e-10)
+	      {
+		CalcInverse (m, inv);
+		inv.Mult (rs, x);
+
+		//            testout << "x = " << x << endl;
+
+
+		f = -1e10;
+		for (i = 1; i <= nf; i++)
+		  {
+		    hd = x.Elem(1) * a.Get(i).X()
+		      + x.Elem(2) * a.Get(i).Y()
+		      + x.Elem(3) * a.Get(i).Z()
+		      + c.Get(i);
+		    if (hd > f) f = hd;
+		  }
+
+		if (f < fmin)
+		  {
+		    fmin = f;
+		    pmin.X() = x.Elem(1);
+		    pmin.Y() = x.Elem(2);
+		    pmin.Z() = x.Elem(3);
+		  }
+	      }
+          }
+
+  //  testout << "fmin = " << fmin << endl;
+  //  testout << "pmin = " << pmin << endl;
+
+  p = pmin;
+  return (fmin < -1e-3 * hmax) ? 1 : 0;
+  //  return (fmin < 0) ? 1 : 0;
+}
+}
diff --git a/contrib/Netgen/libsrc/meshing/findip.hpp b/contrib/Netgen/libsrc/meshing/findip.hpp
new file mode 100644
index 0000000000..a0aed91b6c
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/findip.hpp
@@ -0,0 +1,108 @@
+// find inner point
+
+template <typename POINTARRAY, typename FACEARRAY>
+inline int FindInnerPoint (POINTARRAY & points,
+		    FACEARRAY & faces,
+		    Point3d & p)
+{
+  int i, j;
+  ARRAY<Vec3d> a;
+  ARRAY<double> c;
+  Point3d p1, pmin;
+  int i1, i2, i3, i4;
+  int nf;
+  DenseMatrix m(3), inv(3);
+  Vector rs(3), x(3);
+  double f, fmin, hd, hmax;
+
+  nf = faces.Size();
+
+  //  testout << "#faces  = " << faces.Size() << endl;
+  //  testout << "#points = " << points.Size() << endl;
+
+  a.SetSize (nf);
+  c.SetSize (nf);
+
+  for (i = 1; i <= nf; i++)
+    {
+      p1 = points.Get(faces.Get(i).PNum(1));
+      a.Elem(i) = Cross (points.Get(faces.Get(i).PNum(2)) - points.Get(faces.Get(i).PNum(1)),
+		    points.Get(faces.Get(i).PNum(3)) - points.Get(faces.Get(i).PNum(1)));
+      a.Elem(i) /= a.Get(i).Length();
+      c.Elem(i) = - (a.Get(i).X() * p1.X() + a.Get(i).Y() * p1.Y() + a.Get(i).Z() * p1.Z());
+    }
+
+
+  hmax = 0;
+  for (i = 1; i <= nf; i++)
+    {
+      const Element2d & el = faces.Get(i);
+      for (j = 1; j <= 3; j++)
+	{
+	  double hi = Dist (points.Get(el.PNumMod(j)),
+			    points.Get(el.PNumMod(j+1)));
+	  if (hi > hmax) hmax = hi;
+	}
+    }
+
+
+  fmin = 100;
+  pmin = Point3d (0, 0, 0);
+
+  for (i1 = 1; i1 <= nf; i1++)
+    for (i2 = i1+1; i2 <= nf; i2++)
+      for (i3 = i2+1; i3 <= nf; i3++)
+        for (i4 = i3+1; i4 <= nf; i4++)
+          {
+	    m.Elem(1, 1) = a.Get(i1).X() - a.Get(i2).X();
+	    m.Elem(1, 2) = a.Get(i1).Y() - a.Get(i2).Y();
+	    m.Elem(1, 3) = a.Get(i1).Z() - a.Get(i2).Z();
+	    rs.Elem(1) = c.Get(i2) - c.Get(i1);
+
+	    m.Elem(2, 1) = a.Get(i1).X() - a.Get(i3).X();
+	    m.Elem(2, 2) = a.Get(i1).Y() - a.Get(i3).Y();
+	    m.Elem(2, 3) = a.Get(i1).Z() - a.Get(i3).Z();
+	    rs.Elem(2) = c.Get(i3) - c.Get(i1);
+
+	    m.Elem(3, 1) = a.Get(i1).X() - a.Get(i4).X();
+	    m.Elem(3, 2) = a.Get(i1).Y() - a.Get(i4).Y();
+	    m.Elem(3, 3) = a.Get(i1).Z() - a.Get(i4).Z();
+	    rs.Elem(3) = c.Get(i4) - c.Get(i1);
+
+
+	    if (fabs (m.Det()) > 1e-10)
+	      {
+		CalcInverse (m, inv);
+		inv.Mult (rs, x);
+
+		//            testout << "x = " << x << endl;
+
+
+		f = -1e10;
+		for (i = 1; i <= nf; i++)
+		  {
+		    hd = x.Elem(1) * a.Get(i).X()
+		      + x.Elem(2) * a.Get(i).Y()
+		      + x.Elem(3) * a.Get(i).Z()
+		      + c.Get(i);
+		    if (hd > f) f = hd;
+		  }
+
+		if (f < fmin)
+		  {
+		    fmin = f;
+		    pmin.X() = x.Elem(1);
+		    pmin.Y() = x.Elem(2);
+		    pmin.Z() = x.Elem(3);
+		  }
+	      }
+          }
+
+  //  testout << "fmin = " << fmin << endl;
+  //  testout << "pmin = " << pmin << endl;
+
+  p = pmin;
+  return (fmin < -1e-3 * hmax) ? 1 : 0;
+  //  return (fmin < 0) ? 1 : 0;
+}
+
diff --git a/contrib/Netgen/libsrc/meshing/geomsearch.cpp b/contrib/Netgen/libsrc/meshing/geomsearch.cpp
new file mode 100644
index 0000000000..1c37637816
--- /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 Element2d& elem)
+  {
+    maxp.X()=(*points)[elem.PNum(1)].P().X();
+    maxp.Y()=(*points)[elem.PNum(1)].P().Y();
+    maxp.Z()=(*points)[elem.PNum(1)].P().Z();
+    minp.X()=(*points)[elem.PNum(1)].P().X();
+    minp.Y()=(*points)[elem.PNum(1)].P().Y();
+    minp.Z()=(*points)[elem.PNum(1)].P().Z();
+  
+    for (int i=2; i <= 3; i++)
+      {
+	maxp.X()=max2((*points)[elem.PNum(i)].P().X(),maxp.X());
+	maxp.Y()=max2((*points)[elem.PNum(i)].P().Y(),maxp.Y());
+	maxp.Z()=max2((*points)[elem.PNum(i)].P().Z(),maxp.Z());
+	minp.X()=min2((*points)[elem.PNum(i)].P().X(),minp.X());
+	minp.Y()=min2((*points)[elem.PNum(i)].P().Y(),minp.Y());
+	minp.Z()=min2((*points)[elem.PNum(i)].P().Z(),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 Element2d& 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.);
+    int ix,iy,iz;
+  
+    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;
+		if (ind < 1 || ind > size.i1 * size.i2 * size.i3)
+		  {
+		    cerr << "Illegal hash-position";
+		    cerr << "Position: " << ix << "," << iy << "," << iz << endl;
+		  }
+		hashtable.Elem(ind)->Append(elemnum);		      
+	      }
+	  }
+      }
+  }
+
+  void GeomSearch3d :: GetLocals(ARRAY<Element2d> & 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 Element2d & 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)
+			  {
+			    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 0000000000..5364c24f8d
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/geomsearch.hpp
@@ -0,0 +1,116 @@
+#ifndef FILE_GEOMSEARCH
+#define FILE_GEOMSEARCH
+
+/**************************************************************************/
+/* File:   geomsearch.hh                                                  */
+/* Author: Johannes Gerstmayr                                             */
+/* Date:   19. Nov. 97                                                    */
+/**************************************************************************/
+
+class FrontPoint3;
+class FrontFace;
+
+  /// 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 Element2d& 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 Element2d& elem, INDEX elemnum);
+
+  ///GetLocal faces in sphere with radius xh and middlepoint p
+  void GetLocals(ARRAY<Element2d> & 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 0000000000..60a1fbe51d
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/global.cpp
@@ -0,0 +1,53 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+namespace netgen
+{
+  ostream * testout = &cout;
+
+  ostream * mycout = &cout;
+  ostream * myerr = &cerr;
+
+
+  // Flags globflags; // not used anymoure
+  Flags parameters;
+
+
+  int silentflag = 0;
+  int testmode = 0;
+
+  MeshingParameters mparam;
+  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 0000000000..843231d0c5
--- /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
+*/
+
+///
+extern double GetTime ();
+extern void ResetTime ();
+
+///
+extern int testmode;
+
+// extern ostream * testout;
+// extern AutoPtr<ostream> testout;
+
+/// calling parameters
+extern Flags parameters;
+
+extern 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;
+  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_prism.hpp b/contrib/Netgen/libsrc/meshing/hpref_prism.hpp
new file mode 100644
index 0000000000..ffbf2d2873
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/hpref_prism.hpp
@@ -0,0 +1,300 @@
+
+
+
+
+
+  // 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,
+      HP_PRISM,
+      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, 7 },
+      { 2, 5, 8 },
+      { 3, 6, 9 },
+      { 0, 0, 0 }
+    };
+  HPREF_ELEMENT_TYPE refprism_1fa_0e_0v_newelstypes[] =
+    {
+      HP_PRISM_1FA_0E_0V,
+      HP_PRISM,
+      HP_NONE,
+    };
+  int refprism_1fa_0e_0v_newels[][8] =
+    {
+      { 1, 2, 3, 7, 8, 9 },
+      { 7, 8, 9, 4, 5, 6 }
+    };
+  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_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
+    };
+
+
+
+
+
diff --git a/contrib/Netgen/libsrc/meshing/hpref_quad.hpp b/contrib/Netgen/libsrc/meshing/hpref_quad.hpp
new file mode 100644
index 0000000000..8802d4539e
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/hpref_quad.hpp
@@ -0,0 +1,2129 @@
+
+
+
+
+// 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 },
+};
+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 },
+};
+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,
+  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 },
+  { 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_TRIG_SINGEDGECORNER1,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+  HP_QUAD,
+  HP_NONE,
+};
+int refquad_2e_1vc_newels[][8] =
+{
+  { 1, 5, 10 },
+  { 6, 1, 10 },
+  { 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
+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_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 },
+  { 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_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 },
+  { 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
+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_SINGEDGE,
+  HP_QUAD_SINGEDGE,
+  HP_TRIG,
+  HP_QUAD,
+  HP_TRIG_SINGCORNER,
+  HP_TRIG_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
+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_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 },
+  { 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_tet.hpp b/contrib/Netgen/libsrc/meshing/hpref_tet.hpp
new file mode 100644
index 0000000000..b071269ec8
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/hpref_tet.hpp
@@ -0,0 +1,2842 @@
+
+
+// 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_HEX, 
+  HP_HEX, 
+  HP_HEX, 
+  HP_HEX, 
+  HP_HEX, 
+  HP_HEX, 
+  */
+  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 },
+  /*
+  { 5, 17, 29, 18, 8, 20, 30, 21 },
+  { 6, 19, 29, 17, 11, 24, 31, 23 },
+  { 7, 18, 29, 19, 14, 26, 32, 27 },
+  { 9, 20, 30, 22, 12, 23, 31, 25 },
+  { 10, 22, 30, 21, 15, 28, 32, 26 },
+  { 13, 24, 31, 25, 16, 27, 32, 28 },
+  */
+  { 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 },
+  /*
+  { 5, 17, 29, 18, 8, 20, 30, 21 },
+  { 6, 19, 29, 17, 11, 24, 31, 23 },
+  { 7, 18, 29, 19, 14, 26, 32, 27 },
+  { 9, 20, 30, 22, 12, 23, 31, 25 },
+  { 10, 22, 30, 21, 15, 28, 32, 26 },
+  { 13, 24, 31, 25, 16, 27, 32, 28 },
+  */
+  { 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_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_2VA,  // 2 opposite edges
+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, 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_2va =
+{
+  HP_TET,
+  reftet_2eb_2va_splitedges, 
+  0, 0,
+  reftet_2eb_2va_newelstypes, 
+  reftet_2eb_2va_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 0000000000..6e2ede0eb0
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/hpref_trig.hpp
@@ -0,0 +1,750 @@
+
+
+
+
+
+// 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
+};
+
+
diff --git a/contrib/Netgen/libsrc/meshing/hprefinement.cpp b/contrib/Netgen/libsrc/meshing/hprefinement.cpp
new file mode 100644
index 0000000000..6a63e87675
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/hprefinement.cpp
@@ -0,0 +1,2605 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+
+
+
+namespace netgen
+{
+
+
+  // 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
+    };
+
+
+
+
+
+
+#include "hpref_trig.hpp"
+#include "hpref_quad.hpp"
+#include "hpref_tet.hpp"
+#include "hpref_prism.hpp"
+
+
+
+
+
+  // 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, 6 }
+    };
+  HPRef_Struct refpyramid =
+    {
+      HP_PYRAMID,
+      refpyramid_splitedges, 
+      0, 0,
+      refpyramid_newelstypes, 
+      refpyramid_newels
+    };
+
+
+
+  // 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
+    };
+
+
+
+  // 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
+    };
+
+
+
+
+  // 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
+    };
+
+
+
+
+
+
+
+  // HP_HEX
+  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_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
+    };
+
+
+
+
+  // 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
+    };
+
+
+
+  // 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
+    };
+
+
+
+
+  // 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,
+      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
+    };
+
+
+
+
+
+  // HP_HEX_1F_0E_0V
+  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_1F_0E_0V,
+      HP_HEX,
+      HP_NONE,
+    };
+  int refhex_1f_0e_0v_newels[][8] =
+    {
+      { 1, 2, 3, 4, 9, 10, 11, 12 },
+      { 9, 10, 11, 12, 5, 6, 7, 8 }
+    };
+  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
+    };
+
+
+
+
+
+
+
+  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_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_2VA:
+	hps = &reftet_2eb_2va; 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_1FB_0E_0V:
+	hps = &refprism_1fb_0e_0v; break;
+      case HP_PRISM_1FB_1EA_0V:
+	hps = &refprism_1fb_1ea_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;
+      }
+
+    if (!hps)
+      {
+	PrintSysError ("hp-refinement not implemented for case ", type);
+      }
+
+    return hps;
+  }
+
+
+  /* *********************** PrepareElements ****************************** */
+  
+  // Elements (volume, surface, edges)  are classified by singular vertices, edges, faces
+
+
+  void PrepareElements (Mesh & mesh, ARRAY<HPRefElement> & elements)
+  {
+    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);
+    ARRAY<int, PointIndex::BASE> facepoint(mesh.GetNP());
+
+    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).IsSingular())
+	      cornerpoint.Set(i);
+	  }
+
+
+	for (int i = 1; i <= mesh.GetNSeg(); i++)
+	  if (mesh.LineSegment(i).singedge_left)
+	    {
+	      INDEX_2 i2 (mesh.LineSegment(i).p1, 
+			  mesh.LineSegment(i).p2);
+	      i2.Sort();
+	      
+	      edges.Set (i2, 1);
+	      INDEX_2 i2s(i2.I2(), i2.I1());
+	      edges.Set (i2s, 1);
+	      
+	      edgepoint.Set (i2.I1());
+	      edgepoint.Set (i2.I2());
+	    }
+
+
+	// 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::GetEdges (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());
+
+	    if (!fd.domin_singular && !fd.domout_singular) continue;
+	    int domnr;
+	    if (fd.domin_singular) domnr = fd.DomainIn();
+	    if (fd.domout_singular) domnr = fd.DomainOut();
+	    if (fd.domin_singular && fd.domout_singular) domnr = -1;
+	    
+
+	    faces.Set (INDEX_3::Sort (el[0], el[1], el[2]), domnr);
+	    face_edges.Set (INDEX_2::Sort (el[0], el[1]), domnr);
+	    face_edges.Set (INDEX_2::Sort (el[0], el[2]), domnr);
+	    face_edges.Set (INDEX_2::Sort (el[2], el[1]), domnr);
+	    facepoint[el[0]] = domnr;
+	    facepoint[el[1]] = domnr;
+	    facepoint[el[2]] = domnr;
+	  }
+	
+      }
+    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)
+	      {
+		INDEX_2 i2 (mesh.LineSegment(i).p1, 
+			    mesh.LineSegment(i).p2);
+		edges.Set (i2, 1);
+		edgepoint.Set(i2.I1());
+		edgepoint.Set(i2.I2());
+	      
+		edgepoint_dom.Set (INDEX_2(mesh.LineSegment(i).domin, i2.I1()), 1);
+		edgepoint_dom.Set (INDEX_2(mesh.LineSegment(i).domin, i2.I2()), 1);
+	      }
+	    if (seg.singedge_right)
+	      {
+		INDEX_2 i2 (mesh.LineSegment(i).p2, 
+			    mesh.LineSegment(i).p1);
+		edges.Set (i2, 1);
+		edgepoint.Set(i2.I1());
+		edgepoint.Set(i2.I2());
+
+		edgepoint_dom.Set (INDEX_2(mesh.LineSegment(i).domout, i2.I1()), 1);
+		edgepoint_dom.Set (INDEX_2(mesh.LineSegment(i).domout, i2.I2()), 1);
+	      }
+	    // (*testout) << "seg = " << ind << ", " << seg.p1 << "-" << seg.p2 << endl;
+
+
+	    if (seg.singedge_left || seg.singedge_right)
+	      {
+		for (int j = 0; j < 2; j++)
+		  {
+		    int pi = (j == 0) ? seg.p1 : seg.p2;
+		    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);
+		edgepoint.Set(i);
+	      }
+	
+	    // mark points for refinement that are explicity specified in input file
+	    if (mesh.Point(i).IsSingular())
+	      {
+		cornerpoint.Set(i);
+		edgepoint.Set(i);
+	      }
+	  }
+
+	edgepoint.Or (cornerpoint);
+
+	(*testout) << "cornerpoints: " << endl << cornerpoint << endl
+		   << "edgepoints: " << endl << edgepoint << endl;
+      }
+
+
+
+
+    int cnt_undef = 0, cnt_nonimplement = 0;
+    ARRAY<int> misses(10000);
+    misses = 0;
+
+    for (ElementIndex i = 0; i < mesh.GetNE(); i++)
+      {
+	Element & el = mesh[i];
+      
+	HPREF_ELEMENT_TYPE type = HP_NONE;
+	int pnums[8] = { 0 };
+      
+      
+	switch (el.GetType())
+	  {
+	  case TET:
+	    {
+	      int ep1, ep2, ep3, ep4, cp1, cp2, cp3, cp4, fp1, fp2, fp3, fp4;
+	      int isedge1, isedge2, isedge3, isedge4, isedge5, isedge6;
+	      int isfedge1, isfedge2, isfedge3, isfedge4, isfedge5, isfedge6;
+	      int isface1, isface2, isface3, isface4;
+
+	      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[j]);
+		    ep2 = edgepoint.Test (el[k]);
+		    ep3 = edgepoint.Test (el[pi3]);
+		    ep4 = edgepoint.Test (el[pi4]);
+
+		    cp1 = cornerpoint.Test (el[j]);
+		    cp2 = cornerpoint.Test (el[k]);
+		    cp3 = cornerpoint.Test (el[pi3]);
+		    cp4 = cornerpoint.Test (el[pi4]);
+	    
+		    isedge1 = edges.Used (INDEX_2::Sort (el[j], el[k]));
+		    isedge2 = edges.Used (INDEX_2::Sort (el[j], el[pi3]));
+		    isedge3 = edges.Used (INDEX_2::Sort (el[j], el[pi4]));
+		    isedge4 = edges.Used (INDEX_2::Sort (el[k], el[pi3]));
+		    isedge5 = edges.Used (INDEX_2::Sort (el[k], el[pi4]));
+		    isedge6 = edges.Used (INDEX_2::Sort (el[pi3], el[pi4]));
+
+		    isface1 = isface2 = isface3 = isface4 = 0;
+		    for (int l = 0; l < 4; l++)
+		      {
+			INDEX_3 i3;
+			switch (l)
+			  {
+			  case 0: i3 = INDEX_3 (el[k], el[pi3], el[pi4]); break;
+			  case 1: i3 = INDEX_3 (el[j], el[pi3], el[pi4]); break;
+			  case 2: i3 = INDEX_3 (el[j], el[k], el[pi4]); break;
+			  case 3: i3 = INDEX_3 (el[j], el[k], el[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[k], el[pi3], el[pi4]));
+		    isface2 = faces.Used (INDEX_3::Sort (el[j], el[pi3], el[pi4]));
+		    isface3 = faces.Used (INDEX_3::Sort (el[j], el[k], el[pi4]));
+		    isface4 = faces.Used (INDEX_3::Sort (el[j], el[k], el[pi3]));
+		    */
+
+		    isfedge1 = isfedge2 = isfedge3 = isfedge4 = isfedge5 = isfedge6 = 0;
+		    for (int l = 0; l < 6; l++)
+		      {
+			INDEX_2 i2;
+			switch (l)
+			  {
+			  case 0: i2 = INDEX_2 (el[j], el[k]); break;
+			  case 1: i2 = INDEX_2 (el[j], el[pi3]); break;
+			  case 2: i2 = INDEX_2 (el[j], el[pi4]); break;
+			  case 3: i2 = INDEX_2 (el[k], el[pi3]); break;
+			  case 4: i2 = INDEX_2 (el[k], el[pi4]); break;
+			  case 5: i2 = INDEX_2 (el[pi3], el[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[j], el[k]));
+		    isfedge2 = face_edges.Used (INDEX_2::Sort (el[j], el[pi3]));
+		    isfedge3 = face_edges.Used (INDEX_2::Sort (el[j], el[pi4]));
+		    isfedge4 = face_edges.Used (INDEX_2::Sort (el[k], el[pi3]));
+		    isfedge5 = face_edges.Used (INDEX_2::Sort (el[k], el[pi4]));
+		    isfedge6 = face_edges.Used (INDEX_2::Sort (el[pi3], el[pi4]));
+		    */
+
+		    fp1 = fp2 = fp3 = fp4 = 0;
+		    for (int l = 0; l < 4; l++)
+		      {
+			int pi;
+			switch (l)
+			  {
+			  case 0: pi = el[j]; break;
+			  case 1: pi = el[k]; break;
+			  case 2: pi = el[pi3]; break;
+			  case 3: pi = el[pi4]; break;
+			  }
+			int domnr = facepoint[pi];
+			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[j]] != 0;
+		    fp2 = facepoint[el[k]] != 0;
+		    fp3 = facepoint[el[pi3]] != 0;
+		    fp4 = facepoint[el[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;
+				  }
+			      }
+			    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:  // one singular face
+			{
+			  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)
+		      {
+			pnums[0] = el[j];
+			pnums[1] = el[k];
+			pnums[2] = el[pi3];
+			pnums[3] = el[pi4];
+			break;
+		      }
+		  }
+
+	      /*
+		if (type != HP_TET_2EB_2VA)
+		type = HP_NONE;
+	      */
+
+	      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;
+		  cout << "undefined element !!! " << endl;
+		}
+
+	      break;
+	    }
+
+	  case PRISM:
+	    {
+	      int pi1, pi2, pi3, pi4, pi5, pi6;
+	      int ep1, ep2, ep3, ep4, ep5, ep6, cp1, cp2, cp3, cp4, cp5, cp6;
+
+	      int ishedge1, ishedge2, ishedge3, ishedge4, ishedge5, ishedge6;
+	      int isvedge1, isvedge2, isvedge3;
+	    
+	      for (int j = 1; j <= 3; j++)
+		{
+		  if (type) break;
+
+		  pi1 = j;
+		  pi2 = pi1%3 + 1;
+		  pi3 = pi2%3 + 1;
+		  pi4 = pi1+3;
+		  pi5 = pi2+3;
+		  pi6 = pi3+3;
+
+		  ep1 = edgepoint.Test (el.PNum (pi1));
+		  ep2 = edgepoint.Test (el.PNum (pi2));
+		  ep3 = edgepoint.Test (el.PNum (pi3));
+		  ep4 = edgepoint.Test (el.PNum (pi4));
+		  ep5 = edgepoint.Test (el.PNum (pi5));
+		  ep6 = edgepoint.Test (el.PNum (pi6));
+
+		  cp1 = cornerpoint.Test (el.PNum (pi1));
+		  cp2 = cornerpoint.Test (el.PNum (pi2));
+		  cp3 = cornerpoint.Test (el.PNum (pi3));
+		  cp4 = cornerpoint.Test (el.PNum (pi4));
+		  cp5 = cornerpoint.Test (el.PNum (pi5));
+		  cp6 = cornerpoint.Test (el.PNum (pi6));
+	    
+		  INDEX_2 i2 = INDEX_2::Sort(el.PNum (pi1), el.PNum (pi4));
+		  isvedge1 = edges.Used (i2) || ep1 || ep4;
+
+		  i2 = INDEX_2::Sort(el.PNum (pi2), el.PNum (pi5));
+		  isvedge2 = edges.Used (i2) || ep2 || ep5;
+
+		  i2 = INDEX_2::Sort(el.PNum (pi3), el.PNum (pi6));
+		  isvedge3 = edges.Used (i2) || ep3 || ep6;
+
+
+		  ishedge1 = edges.Used (INDEX_2::Sort(el.PNum (pi1), el.PNum (pi2)));
+		  ishedge2 = edges.Used (INDEX_2::Sort(el.PNum (pi2), el.PNum (pi3)));
+		  ishedge3 = edges.Used (INDEX_2::Sort(el.PNum (pi3), el.PNum (pi1)));
+
+
+
+		  switch (ishedge1 + ishedge2 + ishedge3)
+		    {
+		    case 0:
+		      {
+			if (!isvedge1 && !isvedge2 && !isvedge3)
+			  type = HP_PRISM;
+			else if (isvedge1 && !isvedge2 && !isvedge3)
+			  type = HP_PRISM_SINGEDGE;
+			else if (isvedge1 && isvedge2 && !isvedge3)
+			  type = HP_PRISM_SINGEDGE_V12;
+			break;
+		      }
+		    case 1:
+		      {
+			if (ishedge1)
+			  type = HP_PRISM_SINGEDGE_H1;
+			break;
+		      }
+		    case 2:
+		      {
+			if (ishedge1 && ishedge2)
+			  type = HP_PRISM_SINGEDGE_H12;
+			break;
+		      }
+		    }
+
+		  if (type != HP_NONE)
+		    {
+		      cout << "classified element, type = " << type << ", el = "  << el << endl;
+		      pnums[0] = el.PNum (pi1);
+		      pnums[1] = el.PNum (pi2);
+		      pnums[2] = el.PNum (pi3);
+		      pnums[3] = el.PNum (pi4);
+		      pnums[4] = el.PNum (pi5);
+		      pnums[5] = el.PNum (pi6);
+		      break;
+		    }
+		}
+	      break;
+	    }
+	  default:
+	    {
+	      cerr << "hp-refinement not defined for element" << endl;
+	    }
+	  }
+
+
+	if (type == HP_NONE)
+	  {
+	    cout << "element is HP_NONE: " << el << endl;
+	  }
+
+	if (!Get_HPRef_Struct (type)) 
+	  {
+	    (*testout) << "case " << type << " not implemented " << endl;
+	    cnt_nonimplement++;
+	    misses[type]++;
+	  }
+      
+	HPRefElement hpel;
+	hpel.type = type;
+	for (int j = 0; j < 8; j++)
+	  hpel.pnums[j] = pnums[j];
+	hpel.index = el.GetIndex();
+	hpel.level = 1;
+	hpel.coarse_elnr = i;
+
+	static const double points[4][3] = 
+	  { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 }, { 0, 0, 0 } };
+	for (int j = 0; j < 4; j++)
+	  for (int k = 0; k < 4; k++)
+	    if (pnums[j] == el[k])
+	      {
+		hpel.param[j][0] = points[k][0];
+		hpel.param[j][1] = points[k][1];
+		hpel.param[j][2] = points[k][2];
+	      }
+
+	elements.Append (hpel);
+      }
+
+    cout << "undefined elements: " << cnt_undef << endl;
+    cout << "non-implemented: " << cnt_nonimplement << endl;
+
+    for (int i = 0; i < misses.Size(); i++)
+      if (misses[i])
+	cout << "missing case " << i << " occured " << misses[i] << " times" << endl;
+
+    for (int i = 1; i <= mesh.GetNSE(); i++)
+      {
+	int j, k, pi3, pi4;
+	Element2d & el = mesh.SurfaceElement(i);
+      
+	HPREF_ELEMENT_TYPE type = HP_NONE;
+	int pnums[8] = { 0 };
+
+	switch (el.GetType())
+	  {
+	  case TRIG:
+	    {
+	      for (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 (mesh.GetDimension() == 2)
+		    {
+		      ep1 = edgepoint_dom.Used (INDEX_2 (mesh.SurfaceElement(i).GetIndex(), el.PNumMod(j)));
+		      ep2 = edgepoint_dom.Used (INDEX_2 (mesh.SurfaceElement(i).GetIndex(), el.PNumMod(j+1)));
+		      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;
+		
+		  INDEX_2 i2;
+		  i2 = INDEX_2(el.PNumMod (j), el.PNumMod (j+1));
+		  // i2.Sort();
+		  int isedge1 = edges.Used (i2);
+		  i2 = INDEX_2(el.PNumMod (j+1), el.PNumMod (j+2));
+		  // i2.Sort();
+		  int isedge2 = edges.Used (i2);
+		  i2 = INDEX_2(el.PNumMod (j+2), el.PNumMod (j+3));
+		  // i2.Sort();
+		  int isedge3 = edges.Used (i2);
+		
+		  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 (mesh.GetDimension() == 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;
+		    }
+		}
+	      break;
+	    }
+	  case QUAD:
+	    {
+	      int ep1, ep2, ep3, ep4, cp1, cp2, cp3, cp4;
+	      int isedge1, isedge2, isedge3, isedge4;
+
+	      for (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 (mesh.GetDimension() == 2)
+		    {
+		      ep1 = edgepoint_dom.Used (INDEX_2 (mesh.SurfaceElement(i).GetIndex(), el.PNumMod(j)));
+		      ep2 = edgepoint_dom.Used (INDEX_2 (mesh.SurfaceElement(i).GetIndex(), el.PNumMod(j+1)));
+		      ep3 = edgepoint_dom.Used (INDEX_2 (mesh.SurfaceElement(i).GetIndex(), el.PNumMod(j+2)));
+		      ep4 = edgepoint_dom.Used (INDEX_2 (mesh.SurfaceElement(i).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;
+		
+
+		  INDEX_2 i2;
+		  i2 = INDEX_2(el.PNumMod (j), el.PNumMod (j+1));
+		  // i2.Sort();
+		  isedge1 = edges.Used (i2);
+		  i2 = INDEX_2(el.PNumMod (j+1), el.PNumMod (j+2));
+		  // i2.Sort();
+		  isedge2 = edges.Used (i2);
+		  i2 = INDEX_2(el.PNumMod (j+2), el.PNumMod (j+3));
+		  // i2.Sort();
+		  isedge3 = edges.Used (i2);
+		  i2 = INDEX_2(el.PNumMod (j+3), el.PNumMod (j+4));
+		  // i2.Sort();
+		  isedge4 = edges.Used (i2);
+		
+		  int sumcp = cp1 + cp2 + cp3 + cp4;
+		  int sumep = ep1 + ep2 + ep3 + ep4;
+		  int sumedge = isedge1 + isedge2 + isedge3 + isedge4;
+
+		  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)
+		    {
+		      pnums[0] = el.PNumMod (j);
+		      pnums[1] = el.PNumMod (j+1);
+		      pnums[2] = el.PNumMod (j+2);
+		      pnums[3] = el.PNumMod (j+3);
+		      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;
+		}
+	      break;
+	    }
+	  }
+
+	if (type == HP_NONE)
+	  {
+	    cerr << "undefined QUAD type" << endl;
+	    for (j = 0; j < 4; j++)
+	      pnums[j] = el[j];
+	  }
+
+	HPRefElement hpel;
+	hpel.type = type;
+	for (j = 0; j < 8; j++)
+	  hpel.pnums[j] = pnums[j];
+	hpel.index = el.GetIndex();
+	hpel.level = 1;
+	hpel.coarse_elnr = i-1;
+
+
+	static const double points[3][2] = 
+	  { { 1, 0 }, { 0, 1 }, { 0, 0 } };
+	for (j = 0; j < 3; j++)
+	  for (k = 0; k < 3; k++)
+	    if (pnums[j] == el[k])
+	      {
+		hpel.param[j][0] = points[k][0];
+		hpel.param[j][1] = points[k][1];
+	      }
+	elements.Append (hpel);
+      }
+  
+  
+  
+
+  
+
+    for (int i = 1; i <= mesh.GetNSeg(); i++)
+      {
+	Segment & seg = mesh.LineSegment(i);
+      
+	HPREF_ELEMENT_TYPE type = HP_NONE;
+
+	int cp1 = cornerpoint.Test (seg.p1);
+	int cp2 = cornerpoint.Test (seg.p2);
+
+	INDEX_2 i2;
+	i2 = INDEX_2(seg.p1, seg.p2);
+	i2.Sort();
+	if (!edges.Used (i2))
+	  {
+	    cp1 = edgepoint.Test (seg.p1);
+	    cp2 = edgepoint.Test (seg.p2);
+	  }
+
+	HPRefElement hpel;
+	hpel.index = seg.edgenr + 10000 * seg.si;
+	hpel.level = 1;
+      
+	hpel.pnums[0] = seg.p1;
+	hpel.pnums[1] = seg.p2;
+	hpel.param[0][0] = seg.epgeominfo[0].dist;
+	hpel.param[1][0] = seg.epgeominfo[1].dist;
+      
+	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;
+
+	(*testout) << "refine segment " << seg << ", hptype= " << hpel.type << endl;
+
+	elements.Append (hpel);
+
+	(*testout) << "add seg: " << endl << seg << endl;
+	(*testout) << "param = " << elements.Last().param[0][0] << ", " << elements.Last().param[1][0] << endl;
+      }
+  }
+
+  
+
+  /* *******************************  DoRefinement *************************************** */
+
+  // parameter "fine": false ... divide elements by a factor of 3/4 to 1/4
+  //                   true  ... divide by 7/8 to 1/8
+
+  void DoRefinement (Mesh & mesh, ARRAY<HPRefElement> & elements,
+		     Refinement * ref, bool fine) 
+  {
+    INDEX_2_HASHTABLE<int> newpts(elements.Size()+1);
+    INDEX_3_HASHTABLE<int> newfacepts(elements.Size()+1);
+  
+    // prepare new points
+  
+    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))
+	      {
+		Point3d np = Center (mesh.Point (i2.I1()),
+				     mesh.Point (i2.I2()));
+		np = Center (mesh.Point (i2.I1()),np);
+		if ( fine ) np = Center (mesh.Point (i2.I1()),np);
+
+		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))
+		{
+		  Point3d np = Center (mesh.Point (i3.I2()),
+				       mesh.Point (i3.I3()));
+		  np = Center (mesh.Point (i3.I1()),np);
+		  if ( fine ) np = Center (mesh.Point (i3.I1()),np);
+		  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.level + 1;
+
+	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)
+	  newlevel = el.level;
+
+	if (!hprs) continue;
+
+	int newpnums[64];
+	double newparam[64][3];
+
+	int j;
+	for (j = 0; j < 8; 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;
+
+	    double fac1, fac2;
+	    if ( fine ) { fac1 = 0.875; fac2 = 0.125; }
+	    else { fac1 = 0.75; fac2 = 0.25; }
+
+	    for (int l = 0; l < 3; l++)
+	      newparam[hprs->splitedges[j][2]-1][l] =
+		fac1 * el.param[hprs->splitedges[j][0]-1][l] + 
+		fac2 * 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;
+
+	      double fac1, fac2;
+	      if ( fine ) { fac1 = 0.75; fac2 = 0.125; }
+	      else { fac1 = 0.5; fac2 = 0.25; }
+
+	      for (int l = 0; l < 3; l++)
+		newparam[hprs->splitfaces[j][3]-1][l] =
+		  fac1 * el.param[hprs->splitfaces[j][0]-1][l] + 
+		  fac2 * el.param[hprs->splitfaces[j][1]-1][l] + 
+		  fac2 * 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];
+	      Point3d np = 
+		Center (Center (mesh.Point (pi1),
+				mesh.Point (el.pnums[hprs->splitelements[j][1]-1])),
+			Center (mesh.Point (el.pnums[hprs->splitelements[j][2]-1]),
+				mesh.Point (el.pnums[hprs->splitelements[j][3]-1])));
+	      // divide once more, if fine subdivision is wanted
+	      if ( fine ) np = Center (mesh.Point (pi1),np);
+	      int npi = mesh.AddPoint (np);
+	      newpnums[hprs->splitelements[j][4]-1] = npi;
+
+	      double fac1, fac2;
+	      if ( fine ) { fac1 = 0.625; fac2 = 0.125; }
+	      else { fac1 = 0.25; fac2 = 0.25; }
+
+	      for (int l = 0; l < 3; l++)
+		newparam[hprs->splitelements[j][4]-1][l] =
+		  fac1 * el.param[hprs->splitelements[j][0]-1][l] + 
+		  fac2 * el.param[hprs->splitelements[j][1]-1][l] + 
+		  fac2 * el.param[hprs->splitelements[j][2]-1][l] + 
+		  fac2 * el.param[hprs->splitelements[j][3]-1][l];
+
+	      j++;
+	    }
+
+	j = 0;
+	while (hprs->neweltypes[j])
+	  {
+	    HPRefElement newel;
+	    newel.type = hprs->neweltypes[j];
+	    for (int k = 0; k < 8; k++)
+	      newel.pnums[k] = newpnums[hprs->newels[j][k]-1];
+
+	    newel.index = elements[i].index;
+	    newel.coarse_elnr = elements[i].coarse_elnr;
+	    newel.level = 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++;
+	  }
+      }
+  }
+
+
+
+
+
+
+  /* ************************** 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.level;
+
+	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])
+	  {
+	    HPRefElement newel;
+	    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.level = 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)
+  {
+    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)
+	    {
+	      HPRefElement el = elements[i];
+
+	      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]]);
+		  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::GetFaces (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);
+
+		    }
+		  
+		  HPRefElement newel = el;
+		  if (pts.Size() == 3)
+		    {
+		      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;
+		    }
+		  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;
+		    }
+		  
+		  if (j == 0)
+		    elements[i] = newel;
+		  else
+		    elements.Append (newel);
+		}
+	    }
+	}
+  }
+
+
+  void CalcStatistics (ARRAY<HPRefElement> & elements)
+  {
+    return;
+    
+    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;
+  }
+
+
+
+
+
+
+
+  /* ***************************** HPRefinement ********************************** */
+
+  void HPRefinement (Mesh & mesh, Refinement * ref, int levels)
+  {
+    PrintMessage (1, "HP Refinement called, levels = ", levels);
+
+    // ARRAY<HPRefElement> hpelements;
+
+    delete mesh.hpelements;
+    mesh.hpelements = new ARRAY<HPRefElement>;
+
+    mesh.coarsemesh = new Mesh;
+    *mesh.coarsemesh = mesh;
+
+    ARRAY<HPRefElement> & hpelements = *mesh.hpelements;
+
+
+
+    PrepareElements (mesh, hpelements);
+
+    for (int j = 1; j <= levels; j++)
+      {
+	cout << "hp-refine level " << j << flush;
+	// last parameter: true for fine subdivision (7/8 : 1/8), false for 3/4 : 1/4
+	// Please check into CVS only with this parameter set to "true"!
+	DoRefinement (mesh, hpelements, ref, true); // FB 
+	DoRefineDummies (mesh, hpelements, ref);
+	CalcStatistics (hpelements);
+	cout << " done" << endl;
+      }
+
+    SubdivideDegeneratedHexes (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.p1 = hpel.pnums[0];
+		seg.p2 = 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]; 
+		seg.epgeominfo[1].dist = hpel.param[1][0];
+		seg.epgeominfo[0].edgenr = seg.edgenr;
+		seg.epgeominfo[1].edgenr = seg.edgenr;
+		seg.hp_elnr = i;
+		mesh.AddSegment (seg);
+		break;
+	      }
+	    
+
+	    case HP_TRIG:
+	      {
+		Element2d el(3);
+		el.PNum(1) = hpel.pnums[0];
+		el.PNum(2) = hpel.pnums[1];
+		el.PNum(3) = hpel.pnums[2];
+		el.SetIndex (hpel.index);
+		el.hp_elnr = i;
+		mesh.AddSurfaceElement (el);
+		break;
+	      }
+	    case HP_QUAD:
+	      {
+		Element2d el(4);
+		el.PNum(1) = hpel.pnums[0];
+		el.PNum(2) = hpel.pnums[1];
+		el.PNum(3) = hpel.pnums[2];
+		el.PNum(4) = hpel.pnums[3];
+		el.SetIndex (hpel.index);
+		el.hp_elnr = i;
+		mesh.AddSurfaceElement (el);
+		break;
+	      }
+	    case HP_TET:
+	      {
+		Element el(4);
+		el.PNum(1) = hpel.pnums[0];
+		el.PNum(2) = hpel.pnums[1];
+		el.PNum(3) = hpel.pnums[2];
+		el.PNum(4) = hpel.pnums[3];
+		el.SetIndex (hpel.index);
+		el.hp_elnr = i;
+		mesh.AddVolumeElement (el);
+		break;
+	      }
+	    case HP_PRISM:
+	      {
+		Element el(6);
+		el.PNum(1) = hpel.pnums[0];
+		el.PNum(2) = hpel.pnums[1];
+		el.PNum(3) = hpel.pnums[2];
+		el.PNum(4) = hpel.pnums[3];
+		el.PNum(5) = hpel.pnums[4];
+		el.PNum(6) = hpel.pnums[5];
+		el.SetIndex (hpel.index);
+		el.hp_elnr = i;
+		mesh.AddVolumeElement (el);
+		break;
+	      }
+	    
+	    case HP_PYRAMID:
+	      {
+		Element el(5);
+		el.PNum(1) = hpel.pnums[0];
+		el.PNum(2) = hpel.pnums[1];
+		el.PNum(3) = hpel.pnums[2];
+		el.PNum(4) = hpel.pnums[3];
+		el.PNum(5) = hpel.pnums[4];
+		el.SetIndex (hpel.index);
+		el.hp_elnr = i;
+		mesh.AddVolumeElement (el);
+		break;
+	      }
+	    case HP_HEX:
+	      {
+		Element el(8);
+		el.PNum(1) = hpel.pnums[0];
+		el.PNum(2) = hpel.pnums[1];
+		el.PNum(3) = hpel.pnums[2];
+		el.PNum(4) = hpel.pnums[3];
+		el.PNum(5) = hpel.pnums[4];
+		el.PNum(6) = hpel.pnums[5];
+		el.PNum(7) = hpel.pnums[6];
+		el.PNum(8) = hpel.pnums[7];
+		el.SetIndex (hpel.index);
+		el.hp_elnr = i;
+		mesh.AddVolumeElement (el);
+		break;
+	      }
+	    
+	    default:
+	      PrintSysError ("hpref, backconversion failed for element ", 
+			     int(Get_HPRef_Struct (hpel.type) -> geom));
+	    }
+      }
+  
+    mesh.UpdateTopology();
+  }
+
+
+}
diff --git a/contrib/Netgen/libsrc/meshing/hprefinement.hpp b/contrib/Netgen/libsrc/meshing/hprefinement.hpp
new file mode 100644
index 0000000000..df1cc01e8e
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/hprefinement.hpp
@@ -0,0 +1,227 @@
+#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_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_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
+};
+
+
+
+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
+{
+public:
+  HPRefElement () 
+  {
+    for (int i = 0; i < 8; i++)
+      {
+	pnums[i] = -1;
+	param[i][0] = param[i][1] = param[i][2] = 0;
+      }
+  }
+  HPREF_ELEMENT_TYPE type;
+  PointIndex pnums[8];
+  double param[8][3];
+  int index;
+  int level;
+  int coarse_elnr;
+  //  EdgePointGeomInfo epgeominfo[2];
+};
+
+
+
+extern void HPRefinement (Mesh & mesh, Refinement * ref, int levels);
+
+
+#endif
+
+
+
+
diff --git a/contrib/Netgen/libsrc/meshing/improve2.cpp b/contrib/Netgen/libsrc/meshing/improve2.cpp
new file mode 100644
index 0000000000..af0ea0ca45
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/improve2.cpp
@@ -0,0 +1,798 @@
+#include <mystdlib.h>
+
+#include "meshing.hpp"
+#include <opti.hpp>
+
+#ifndef SMALLLIB
+//#include <visual.hpp>
+#endif
+
+namespace netgen
+{
+
+class Neighbour
+{
+  int nr[3];
+  int orient[3];
+
+public:
+  Neighbour () { nr[0] = nr[1] = nr[2] = -1; orient[0] = orient[1] = orient[2] = 0; }
+
+  void SetNr (int side, int anr) { nr[side-1] = anr; }
+  int GetNr (int side) { return nr[side-1]; }
+
+  void SetOrientation (int side, int aorient) { orient[side-1] = aorient; }
+  int GetOrientation (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)
+{
+  //  return; 
+
+  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;
+    }
+
+
+  int i, i2, j, k, j2;
+  bool should;
+  PointIndex pi;
+
+  ARRAY<SurfaceElementIndex> seia;
+  mesh.GetSurfaceElementsOfFace (faceindex, seia);
+
+  for (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());
+
+  SurfaceElementIndex t1, t2;
+  int o1, o2;
+
+  PointIndex pi1, pi2, pi3, pi4;
+  PointGeomInfo gi1, gi2, gi3, gi4;
+
+
+  int nswaps = 0;
+  int e, done;
+  double d;
+  Vec3d nv1, nv2;
+  double horder;
+  double loch;
+  static const double minangle[] = { 0, 1.481, 2.565, 3.627, 4.683, 5.736, 7, 9 };
+
+  pangle = 0;
+
+  for (i = 0; i < seia.Size(); i++)
+    {
+      const Element2d & sel = mesh[seia[i]];
+      for (j = 0; j < 3; j++)
+	{
+	  POINTTYPE typ = mesh.PointType (sel[j]);
+	  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 (pi = PointIndex::BASE; 
+       pi < mesh.GetNP()+PointIndex::BASE; pi++)
+    {
+      if (mesh.PointType(pi) == INNERPOINT || mesh.PointType(pi) == SURFACEPOINT)
+	pdef[pi] = -6;
+      else
+	for (j = 0; j < 8; j++)
+	  if (pangle[pi] >= minangle[j])
+	    pdef[pi] = -1-j;
+    }
+
+  for (i = 0; i < seia.Size(); i++)
+    {
+      const Element2d & sel = mesh[seia[i]];
+      for (j = 0; j < 3; j++)
+	pdef[sel[j]]++;
+    }
+
+  for (i = 0; i < seia.Size(); i++)
+    {
+      const Element2d & sel = mesh[seia[i]];
+      for (j = 0; j < 3; j++)
+	{
+	  neighbors[seia[i]].SetNr (j+1, -1);
+	  neighbors[seia[i]].SetOrientation (j+1, 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 (i = 0; i < seia.Size(); i++)
+    {
+      const Element2d & sel = mesh[seia[i]];
+
+      for (j = 1; j <= 3; j++)
+	{
+	  pi1 = sel.PNumMod(j+1);
+	  pi2 = sel.PNumMod(j+2);
+	  
+	  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();
+	      
+	      i2 = other.Get(ii2).tnr;
+	      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 (i = 0; i < seia.Size(); i++)
+    swapped[seia[i]] = 0;
+
+
+  int t = 4;
+  done = 0;
+  while (!done && t >= 2)
+    {
+      for (i = 0; i < seia.Size(); i++)
+	{
+	  t1 = seia[i];
+
+	  if (mesh[t1].IsDeleted())
+	    continue;
+
+	  if (mesh[t1].GetIndex() != faceindex)
+	    continue;
+
+	  if (multithread.terminate)
+	    throw NgException ("Meshing stopped");
+
+	  for (o1 = 1; o1 <= 3; o1++)
+	    {
+	      t2 = neighbors[t1].GetNr (o1);
+	      o2 = neighbors[t1].GetOrientation (o1);
+
+	      if (t2 == -1) continue;
+	      if (swapped[t1] || swapped[t2]) continue;
+	      
+
+	      pi1 = mesh[t1].PNumMod(o1+1);
+	      pi2 = mesh[t1].PNumMod(o1+2);
+	      pi3 = mesh[t1].PNumMod(o1);
+	      pi4 = mesh[t2].PNumMod(o2);
+	      
+	      gi1 = mesh[t1].GeomInfoPiMod(o1+1);
+	      gi2 = mesh[t1].GeomInfoPiMod(o1+2);
+	      gi3 = mesh[t1].GeomInfoPiMod(o1);
+	      gi4 = mesh[t2].GeomInfoPiMod(o2);
+	      
+	      // normal of old   (new ?????)
+	      nv1 = Cross (mesh.Point(pi3)-mesh.Point(pi4), 
+			   mesh.Point(pi1)-mesh.Point(pi4));
+	      nv2 = Cross (mesh.Point(pi4)-mesh.Point(pi3), 
+			   mesh.Point(pi2)-mesh.Point(pi3));
+
+	      
+	      // normals of swapped  original (???JS)
+	      Vec3d nv3, nv4;
+	      nv3 = Cross (mesh.Point(pi1)-mesh.Point(pi4), 
+			   mesh.Point(pi2)-mesh.Point(pi4));
+	      nv4 = Cross (mesh.Point(pi2)-mesh.Point(pi3), 
+			   mesh.Point(pi1)-mesh.Point(pi3));
+	      
+	      nv3 *= -1;
+	      nv4 *= -1;
+	      nv3.Normalize();
+	      nv4.Normalize();
+
+	      nv1.Normalize();
+	      nv2.Normalize();
+	    
+	      Vec3d 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
+	      bool allowswap = 
+		(nv1 * nvp3 > critval) && 
+		(nv1 * nvp4 > critval) && 
+		(nv2 * nvp3 > critval) && 
+		(nv2 * nvp4 > critval) &&
+		(nvp3 * nv3 > critval) && 
+		(nvp4 * nv4 > critval);
+	      
+
+	      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)
+		    {
+		      e = pdef[pi1] + pdef[pi2] - pdef[pi3] - pdef[pi4];
+		      d = 
+			Dist2 (mesh.Point(pi1), mesh.Point(pi2)) - 
+			Dist2 (mesh.Point(pi3), mesh.Point(pi4));
+		      
+		      should = e >= t && (e > 2 || d > 0);
+		    }
+		  else
+		    {
+		      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 !
+		      
+		      // cout << "swap " << endl;
+
+		      nswaps ++;
+		      
+		    //            testout << "nv1 = " << nv1 << "   nv2 = " << nv2 << endl;
+		      
+		      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;
+    }
+
+
+  int i, j, k, l, i2, j2;
+  PointIndex pi;
+  SurfaceElementIndex sei;
+
+
+  ARRAY<SurfaceElementIndex> seia;
+  mesh.GetSurfaceElementsOfFace (faceindex, seia);
+
+
+  for (i = 0; i < seia.Size(); i++)
+    if (mesh[seia[i]].GetNP() != 3)
+      return;
+
+
+
+  int surfnr = 0;
+  if (faceindex)
+    surfnr = mesh.GetFaceDescriptor (faceindex).SurfNr();
+
+
+  int should;
+  PointIndex pi1, pi2;
+  MeshPoint p1, p2, pnew;
+  double bad1, bad2;
+  Vec3d nv;
+
+  int np = mesh.GetNP();
+  int nse = mesh.GetNSE();
+
+  TABLE<SurfaceElementIndex,PointIndex::BASE> elementsonnode(np); 
+  ARRAY<SurfaceElementIndex> hasonepi, hasbothpi;
+
+  for (i = 0; i < seia.Size(); i++)
+    {
+      Element2d & el = mesh[seia[i]];
+      for (j = 0; j < el.GetNP(); j++)
+	{
+	  elementsonnode.Add (el[j], seia[i]);
+	}      
+    }
+
+
+  ARRAY<int,PointIndex::BASE> fixed(np);
+  fixed = 0;
+
+  SegmentIndex si;
+  for (si = 0; si < mesh.GetNSeg(); si++)
+    {
+      INDEX_2 i2(mesh[si].p1, mesh[si].p2);
+      fixed[i2.I1()] = 1;
+      fixed[i2.I2()] = 1;
+    }
+
+
+  ARRAY<Vec3d,PointIndex::BASE> normals(np);
+
+  for (pi = PointIndex::BASE; 
+       pi < np + PointIndex::BASE; pi++)
+    {
+      if (elementsonnode[pi].Size())
+	{
+	  Element2d & hel = mesh[elementsonnode[pi][0]];
+	  for (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;
+	      }
+	  if (k == 3)
+	    {
+	      cerr << "Neuer Fehler von Joachim, code 17121" << endl;
+	    }
+	}
+    }
+
+
+  for (i = 0; i < seia.Size(); i++)
+    {
+
+      sei = seia[i];
+      Element2d & elem = mesh[sei];
+      if (elem.IsDeleted()) continue;
+
+      for (j = 0; j < 3; j++)
+	{
+	  pi1 = elem[j];
+	  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 (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 (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;
+	      }
+	  if (k == 3)
+	    {
+	      cerr << "Neuer Fehler von Joachim, code 32434" << endl;
+	    }
+
+
+	  //	  nv = normals.Get(pi1);
+
+
+
+	  for (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 (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 (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());
+
+	  p1 = mesh[pi1];
+	  p2 = mesh[pi2];
+
+	  pnew = p1;
+	  mesh[pi1] = pnew;
+	  mesh[pi2] = pnew;
+
+	  bad2 = 0;
+	  for (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;
+
+	      Vec3d hnv = Cross (Vec3d (mesh[el[0]],
+					mesh[el[1]]),
+				 Vec3d (mesh[el[0]],
+					mesh[el[2]]));
+	      if (hnv * nv < 0)
+		bad2 += 1e10;
+
+	      for (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;
+	      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 (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 (l = 0; l < el.GetNP(); l++)
+		    {
+		      if (el[l] == pi2)
+			{
+			  el[l] = pi1;
+			  el.GeomInfoPi (l+1) = gi;
+			}
+
+		      fixed[el[l]] = 1;
+		    }
+		}
+
+	      /*
+	      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 (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 0000000000..0770a93b05
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/improve2.hpp
@@ -0,0 +1,91 @@
+#ifndef FILE_IMPROVE2
+#define FILE_IMPROVE2
+
+
+
+///
+class MeshOptimize2d
+{
+  int faceindex;
+  int improveedges;
+  double metricweight;
+  int writestatus;
+
+public:
+  ///
+  MeshOptimize2d ();
+  ///
+  void ImproveMesh (Mesh & mesh2d);
+  void ImproveMeshJacobian (Mesh & mesh2d);
+    
+  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 Point3d & p,
+				     const PointGeomInfo & gi);
+  ///
+  virtual void ProjectPoint (INDEX /* surfind */, Point3d & /* p */) const { };
+  ///
+  virtual void ProjectPoint2 (INDEX /* surfind */, INDEX /* surfind2 */, Point3d & /* 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 Point3d& /*p3*/) const
+    { gi.trignum = 1; return 1;};
+
+  virtual int CalcPointGeomInfo(int /* surfind */, PointGeomInfo& gi, const Point3d& p3) const
+    { return CalcPointGeomInfo (gi, p3); }
+
+  ///
+  virtual void GetNormalVector(INDEX surfind, const Point3d & p, PointGeomInfo & gi, Vec3d & n) const;
+  virtual void GetNormalVector(INDEX surfind, const Point3d & p, Vec3d & 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 0000000000..69f0e23b27
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/improve2gen.cpp
@@ -0,0 +1,441 @@
+#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;
+
+    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);
+	  }
+	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];
+		    int used = 0;
+		    for (int l = 0; l < nbels[sei].Size(); l++)
+		      if (nbels[sei][l] == nbel)
+			used = 1;
+		    if (!used)
+		      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;
+		Vec3d 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 0000000000..1b9c8c52f6
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/improve3.cpp
@@ -0,0 +1,1947 @@
+#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 ();
+  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 = CalcTetBadness (mesh[elem[0]], mesh[elem[1]],  
+					   mesh[elem[2]], mesh[elem[3]], 0);
+	      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;
+      int ne = mesh.GetNE();
+      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();
+
+  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 (int 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, -1);
+	  OptiParameters par;
+	  par.maxit_linsearch = 50;
+	  par.maxit_bfgs = 20;
+
+	  pnew = Center (p1, p2);
+	  Vector px(3);
+	  px.Elem(1) = pnew.X();
+	  px.Elem(2) = pnew.Y();
+	  px.Elem(3) = pnew.Z();
+
+	  if (elerrs[ei] > 0.1 * badmax)
+	    BFGS (px, pf, par);
+
+	  bad2 = pf.Func (px);
+
+	  pnew.X() = px.Get(1);
+	  pnew.Y() = px.Get(2);
+	  pnew.Z() = px.Get(3);
+
+
+	  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)
+{
+  int j, k, l;
+
+  ElementIndex ei;
+  SurfaceElementIndex sei;
+
+  PointIndex pi1, pi2, pi3, pi4, pi5, pi6;
+  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();
+  int nse = mesh.GetNSE();
+
+  
+  // contains at least all elements at node
+  TABLE<ElementIndex,PointIndex::BASE> elementsonnode(np);
+
+  ARRAY<ElementIndex> hasbothpoints;
+
+  PrintMessage (3, "SwapImprove ");
+  (*testout) << "\n" << "Start SwapImprove" << endl;
+
+  char * savetask = multithread.task;
+  multithread.task = "Swap Improve";
+  
+  //  mesh.CalcSurfacesOfNode ();
+  /*
+    for (i = 1; i <= GetNE(); i++)
+    if (volelements.Get(i).PNum(1))
+    if (!LegalTet (volelements.Get(i)))
+    {
+    cout << "detected illegal tet, 1" << endl;
+    (*testout) << "detected illegal tet1: " << i << endl;
+    }
+  */	
+  
+    
+  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 (ei = 0; ei < ne; ei++)
+    for (j = 0; j < mesh[ei].GetNP(); j++)
+      elementsonnode.Add (mesh[ei][j], ei);
+
+  /*
+    BitArray illegaltet(GetNE());
+    MarkIllegalElements();
+    if (goal == OPT_QUALITY || goal == OPT_LEGAL)
+    {
+    int cntill = 0;
+    for (i = 1; i <= GetNE(); i++)
+    {
+    //	  if (!LegalTet (volelements.Get(i)))
+    if (VolumeElement(i).flags.illegal)
+    {
+    cntill++;
+    illegaltet.Set (i);
+    }
+    }
+    //      (*mycout) << cntill << " illegal tets" << endl;
+    }
+  */
+
+  INDEX_2_HASHTABLE<int> edgeused(2 * ne + 5);
+
+  for (ei = 0; ei < ne; ei++)
+    {
+      if (multithread.terminate)
+	break;
+      
+      multithread.percent = 100.0 * (ei+1) / ne;
+
+      if (mesh.ElementType(ei) == FIXEDELEMENT)
+	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 (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 (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 (l = 0; l < elem.GetNP(); l++)
+		{
+		  if (elem[l] == pi1) has1 = 1;
+		  if (elem[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);
+		}
+	    }
+	  
+	  bool puretet = 1;
+	  for (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 (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 (k = 1; k < 3; k++)
+		{
+		  const Element & elem = mesh[hasbothpoints[k]];
+		  bool has1 = 0;
+		  for (l = 0; l < 4; l++)
+		    if (elem[l] == pi4)
+		      has1 = 1;
+		  if (has1)
+		    {
+		      for (l = 0; l < 4; l++)
+			if (elem[l] != pi1 && elem[l] != pi2 && elem[l] != pi4)
+			  pi5 = elem[l];
+		    }
+		}
+	      
+	      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 (l = 0; l < 4; l++)
+		    mesh[hasbothpoints[2]][l] = 0;
+		  mesh[hasbothpoints[2]].Delete();
+
+		  for (k = 0; k < 2; k++)
+		    for (l = 0; l < 4; l++)
+		      elementsonnode.Add (mesh[hasbothpoints[k]][l], hasbothpoints[k]);
+		}
+	    }
+	  
+
+	  if (nsuround == 4)
+	    {
+	      const Element & elem1 = mesh[hasbothpoints[0]];
+	      for (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 (k = 1; k < 4; k++)
+		{
+		  const Element & elem = mesh[hasbothpoints[k]];
+		  bool has1 = 0;
+		  for (l = 0; l < 4; l++)
+		    if (elem[l] == pi4)
+		      has1 = 1;
+		  if (has1)
+		    {
+		      for (l = 0; l < 4; l++)
+			if (elem[l] != pi1 && elem[l] != pi2 && elem[l] != pi4)
+			  pi5 = elem[l];
+		    }
+		}
+	      
+	      pi6 = 0;
+	      for (k = 1; k < 4; k++)
+		{
+		  const Element & elem = mesh[hasbothpoints[k]];
+		  bool has1 = 0;
+		  for (l = 0; l < 4; l++)
+		    if (elem[l] == pi3)
+		      has1 = 1;
+		  if (has1)
+		    {
+		      for (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 (k = 0; k < 4; k++)
+		    for (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 (k = 0; k < 4; k++)
+		    for (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 (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 (l = 2; l < nsuround; l++)
+		{
+		  int oldpi = suroundpts[l-1];
+		  int newpi = 0;
+
+		  for (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;
+			    }			
+		      }
+		}
+
+	      
+	      double bad1 = 0, bad2;
+	      for (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 (l = 0; l < nsuround; l++)
+		{
+		  bad2 = 0;
+
+		  for (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 (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 (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 (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 (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;
+}
+  
+
+
+
+
+
+
+
+/*
+  2 -> 3 conversion
+*/
+
+
+
+void MeshOptimize3d :: SwapImprove2 (Mesh & mesh, OPTIMIZEGOAL goal)
+{
+  int j, k, l;
+  ElementIndex ei, eli1, eli2, elnr;
+  SurfaceElementIndex sei;
+  PointIndex pi1, pi2, pi3, pi4, pi5;
+
+  int cnt = 0;
+
+  Element el21(TET), el22(TET), el31(TET), el32(TET), el33(TET);
+
+  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 (ei = 0; ei < ne; ei++)
+    for (j = 0; j < mesh[ei].GetNP(); j++)
+      elementsonnode.Add (mesh[ei][j], ei);
+
+  for (sei = 0; sei < nse; sei++)
+    for (j = 0; j < 3; j++)
+      belementsonnode.Add (mesh[sei][j], sei);
+
+  for (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 (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 (k = 0; k < belementsonnode[pi1].Size(); k++)
+	    {
+	      const Element2d & bel = 
+		mesh[belementsonnode[pi1][k]];
+
+	      bool bface1 = 1;
+	      for (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 (k = 0; k < row.Size(); k++)
+	    {
+	      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 (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 (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 0000000000..9b5b2c9bec
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/improve3.hpp
@@ -0,0 +1,50 @@
+#ifndef FILE_IMPROVE3
+#define FILE_IMPROVE3
+
+
+
+
+///
+class MeshOptimize3d
+{
+public:
+  void CombineImprove (Mesh & mesh, OPTIMIZEGOAL goal = OPT_QUALITY);
+  void SplitImprove (Mesh & mesh, OPTIMIZEGOAL goal = OPT_QUALITY);
+  void SwapImprove (Mesh & mesh, OPTIMIZEGOAL goal = OPT_QUALITY);
+  void SwapImprove2 (Mesh & mesh, OPTIMIZEGOAL goal = OPT_QUALITY);
+};
+
+
+
+extern double CalcBad (const Mesh::T_POINTS & points, const Element & elem,
+		       double h);
+
+extern double CalcTotalBad (const Mesh::T_POINTS & points, 
+			    const Mesh::T_VOLELEMENTS & elements);
+
+extern int WrongOrientation (const Mesh::T_POINTS & points, const Element & el);
+
+
+/* Functional depending of inner point inside triangular surface */
+
+
+
+class PointFunction1 : public MinFunction
+{
+  Mesh::T_POINTS & points;
+  const ARRAY<INDEX_3> & faces;
+  double h;
+public:
+  PointFunction1 (Mesh::T_POINTS & apoints, 
+		  const ARRAY<INDEX_3> & afaces,
+		  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;
+};
+
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/meshing/localh.cpp b/contrib/Netgen/libsrc/meshing/localh.cpp
new file mode 100644
index 0000000000..62025737f3
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/localh.cpp
@@ -0,0 +1,682 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+
+namespace netgen
+{
+
+GradingBox :: GradingBox (const double * ax1, const double * ax2)
+{
+  int i;
+
+  h2 = 0.5 * (ax2[0] - ax1[0]);
+  for (i = 0; i <= 2; i++)
+    {
+      /*
+      x1[i] = ax1[i];
+      x2[i] = ax2[i];
+      */
+      xmid[i] = 0.5 * (ax1[i] + ax2[i]);
+    }
+
+  /*
+  (*testout) << "new box: " << xmid[0] << "-" << xmid[1] << "-" << xmid[2] 
+	     << " h = " << (x2[0] - x1[0]) << endl;
+  */
+
+  for (i = 0; i < 8; i++)
+    childs[i] = NULL;
+  father = NULL;
+
+  flags.cutboundary = 0;
+  flags.isinner = 0;
+  flags.oldcell = 0;
+  flags.pinner = 0;
+
+  //  hopt = x2[0] - x1[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()
+{
+  int i;
+  for (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;
+  int i;
+
+  boundingbox = Box3d (pmin, pmax);
+  grading = agrading;
+
+  // a small enlargement, non-regular points 
+  double val = 0.0879;
+  for (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 (i = 1; i <= 2; i++)
+    if (x2[i] - x1[i] > hmax)
+      hmax = x2[i] - x1[i];
+
+  for (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;
+  int i;
+  for (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);
+    }
+  /*
+  Point3d np;
+  int i1, i2, i3;
+  for (i1 = -1; i1 <= 1; i1++)
+    for (i2 = -1; i2 <= 1; i2++)
+      for (i3 = -1; i3 <= 1; i3++)
+	{
+	  np.X() = p.X() + hbox * i1;
+	  np.Y() = p.Y() + hbox * i2;
+	  np.Z() = p.Z() + hbox * i3;
+
+	  SetH (np, hnp);
+	}
+  */
+}
+
+
+
+double LocalH :: GetH (const Point3d & x) const
+{
+  const GradingBox * box = root;
+  const GradingBox * nbox;
+  int childnr;
+
+  while (1)
+    {
+      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;
+      nbox = box->childs[childnr];
+      if (nbox)
+	box = nbox;
+      else
+	{
+	  //	  (*testout) << "diam = " << (box->x2[0] - box->x1[0])
+	  //		     << " h = " << box->hopt << endl;
+	  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;
+  /*
+  if (pmax.X() < box->x1[0] || pmin.X() > box->x2[0] ||
+      pmax.Y() < box->x1[1] || pmin.Y() > box->x2[1] ||
+      pmax.Z() < box->x1[2] || pmin.Z() > box->x2[2])
+    return 1e8;
+  */
+
+      
+  double hmin = 2 * box->h2; // box->x2[0] - box->x1[0];
+  int i;
+  
+  for (i = 0; i <= 7; i++)
+    {
+      if (box->childs[i])
+	{
+	  double hi = GetMinHRec (pmin, pmax, box->childs[i]);
+	  if (hi < hmin)
+	    hmin = hi;
+	}	  
+    }
+
+  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;
+  /*
+  if (pmax.X() < box->x1[0] || pmin.X() > box->x2[0] ||
+      pmax.Y() < box->x1[1] || pmin.Y() > box->x2[1] ||
+      pmax.Z() < box->x1[2] || pmin.Z() > box->x2[2])
+    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 ( // int (*sameside)(const Point3d & p1, const Point3d & p2),
+			       AdFront3 * adfront,
+			       int (*testinner)(const Point3d & p1))
+{
+  int i, j;
+
+  int nf = adfront->GetNF();
+
+  for (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 (i = 1; i <= nf; i++)
+    {
+      faceinds.Elem(i) = i;
+      adfront->GetFaceBoundingBox(i, faceboxes.Elem(i));
+    }
+  
+  for (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;
+  
+  int i, j;
+  
+  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);
+
+
+  static ARRAY<int> faceused;
+  static ARRAY<int> faceused2;
+  static ARRAY<int> facenotused;
+
+  faceused.SetSize(0);
+  facenotused.SetSize(0);
+  faceused2.SetSize(0);
+
+  for (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 (j = 1; j <= faceused.Size(); j++)
+    faceinds.Elem(j) = faceused.Get(j);
+  for (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;
+    }
+
+  int nf = faceused.Size();
+  for (i = 0; i < 8; i++)
+    FindInnerBoxesRec2 (box->childs[i], adfront, faceboxes, faceinds, nf);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+/*
+void LocalH :: FindInnerBoxes ( // int (*sameside)(const Point3d & p1, const Point3d & p2),
+			       AdFront3 * adfront,
+			       int (*testinner)(const Point3d & p1))
+{
+  int i;
+  for (i = 1; i <= boxes.Size(); i++)
+    boxes.Elem(i)->flags.isinner = 0;
+
+  root->flags.isinner = 0;
+
+  Point3d rpmid(root->xmid[0], root->xmid[1], root->xmid[2]);
+  Point3d rx2 = rpmid + Vec3d (root->h2, root->h2, root->h2);
+
+  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;
+  
+
+  for (i = 2; i <= boxes.Size(); i++)
+    {
+      GradingBox * box = boxes.Elem(i);
+      GradingBox * father = box -> father;
+
+      Point3d c(box->xmid[0], box->xmid[1], box->xmid[2]);
+      Vec3d v(box->h2, box->h2, box->h2);
+      Point3d x1 = c-v;
+      Point3d x2 = c+v;
+
+
+      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))
+		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;
+	}
+    }
+  //  FindInnerBoxesRec (inner, root);
+}
+*/
+
+
+void LocalH :: FindInnerBoxesRec ( int (*inner)(const Point3d & p),
+				   GradingBox * box)
+{
+  int i;
+  if (box->flags.cutboundary)
+    {
+      for (i = 0; i < 8; i++)
+	if (box->childs[i])
+	  FindInnerBoxesRec (inner, box->childs[i]);
+    }
+  else
+    {
+      if (inner (Point3d (box->xmid[0], box->xmid[1], box->xmid[2])))
+	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 ()
+{
+  int nb = boxes.Size(); 
+  int i;
+  //  (*testout) << "old boxes: " << nb << endl;
+  for (i = 1; i <= nb; i++)
+    {
+      GradingBox * box = boxes.Get(i);
+      //      double h = box->x2[0] - box->x1[0];
+      double h = box->hopt;
+      Point3d c(box->xmid[0], box->xmid[1], box->xmid[2]);
+      //      (*testout) << " i = " << i 
+      //		 << " c = " << c << " h = " << h << endl;
+
+      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<Point3d> & points)
+{
+  int i, nb = boxes.Size(); 
+
+  for (i = 1; i <= nb; i++)
+    {
+      GradingBox * box = boxes.Get(i);
+      /*
+      if (box->flags.pinner)
+	points.Append (box->randomip);
+      */
+      //      if (box->flags.pinner)
+      if (box->flags.isinner)
+	{
+	  Point3d c(box->xmid[0], box->xmid[1], box->xmid[2]);
+	  points.Append (c);
+	  /*
+	  cout << "add point " << c << "; h = " << box->hopt
+	       << "; max-min = " << (box->x2[0]-box->x1[0]) << endl;
+	  */
+	}
+    }
+}
+
+
+
+void LocalH :: GetOuterPoints (ARRAY<Point3d> & points)
+{
+  int i, nb = boxes.Size(); 
+
+  for (i = 1; i <= nb; i++)
+    {
+      GradingBox * box = boxes.Get(i);
+      if (!box->flags.isinner &&
+	  !box->flags.cutboundary)
+	{
+	  Point3d c(box->xmid[0], box->xmid[1], box->xmid[2]);
+	  points.Append (c);
+	}
+    }
+}
+
+
+
+void LocalH :: Convexify ()
+{
+  ConvexifyRec (root);
+}
+
+void LocalH :: ConvexifyRec (GradingBox * box)
+{
+  Point3d center(box->xmid[0], box->xmid[1], box->xmid[2]);
+  Point3d hp;
+
+  double size = 2 * box->h2; // box->x2[0] - box->x1[0];
+  double dx = 0.6 * size;
+
+  double maxh = box->hopt;
+  int i;
+
+  
+  
+  for (i = 1; i <= 6; i++)
+    {
+      hp = center;
+      switch (i)
+	{
+	case 1: hp.X() += dx; break;
+	case 2: hp.X() -= dx; break;
+	case 3: hp.Y() += dx; break;
+	case 4: hp.Y() -= dx; break;
+	case 5: hp.Z() += dx; break;
+	case 6: hp.Z() -= dx; break;
+	}
+      
+      double hh = GetH (hp);
+      if (hh > maxh) maxh = hh;
+    }
+
+  if (maxh < 0.95 * box->hopt)
+    SetH (center, maxh);
+
+  for (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 0000000000..7531bc7faf
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/localh.hpp
@@ -0,0 +1,145 @@
+#ifndef LOCALH
+#define LOCALH
+
+/**************************************************************************/
+/* File:   localh.hh                                                      */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   29. Jan. 97                                                    */
+/**************************************************************************/
+
+
+
+
+/// box for grading
+class GradingBox
+{
+  /*
+  /// xmin
+  float x1[3];
+  /// xmax
+  float x2[3];
+  */
+  /// xmid
+  float xmid[3];
+  /// half edgelength
+  float h2;
+  ///
+  GradingBox * childs[8];
+  ///
+  GradingBox * father;
+  ///
+  double hopt;
+  ///
+  struct 
+  {
+    unsigned int cutboundary:1;
+    unsigned int isinner:1;
+    unsigned int oldcell:1;
+    unsigned int pinner:1;
+  } flags;
+public:
+  ///
+  GradingBox (const double * ax1, const double * ax2);
+  ///
+  void DeleteChilds();
+  ///
+  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();
+  ///
+  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); }
+
+  /// find inner boxes
+  void FindInnerBoxes ( // int (*sameside)(const Point3d & p1, const Point3d & p2),
+		       class AdFront3 * adfront,
+		       int (*testinner)(const Point3d & p1));
+
+  /// clears all flags 
+  void ClearFlags ()
+    { ClearFlagsRec(root); }
+
+  /// widen refinement zone
+  void WidenRefinement ();
+
+  /// get points in inner elements
+  void GetInnerPoints (ARRAY<Point3d> & points);
+
+  /// get points in outer closure
+  void GetOuterPoints (ARRAY<Point3d> & 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 SetInnerBoxesRec (GradingBox * box);
+
+  ///
+  void ClearFlagsRec (GradingBox * box);
+  
+  ///
+  void ConvexifyRec (GradingBox * box);
+};
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/meshing/meshclass.cpp b/contrib/Netgen/libsrc/meshing/meshclass.cpp
new file mode 100644
index 0000000000..25c44f337e
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/meshclass.cpp
@@ -0,0 +1,4744 @@
+#include <mystdlib.h>
+
+#include "meshing.hpp"
+
+namespace netgen
+{
+
+  Mesh :: Mesh ()
+  {
+    boundaryedges = NULL;
+    surfelementht = NULL; 
+    segmentht = NULL;
+
+    lochfunc = NULL;
+    mglevels = 1;
+    elementsearchtree = NULL;
+    elementsearchtreets = NextTimeStamp();
+    majortimestamp = timestamp = NextTimeStamp();
+    hglob = 1e10;
+    numvertices = -1;
+    dimension = 3;
+    topology = new MeshTopology (*this);
+    curvedelems = new CurvedElements (*this);
+    clusters = new AnisotropicClusters (*this);
+    ident = new Identifications (*this);
+
+    hpelements = NULL;
+    coarsemesh = NULL;
+
+  }
+
+  Mesh :: ~Mesh()
+  {
+    delete lochfunc;
+    delete boundaryedges;
+    delete surfelementht;
+    delete segmentht;
+    delete curvedelems;
+    delete clusters;
+    delete topology;
+    delete ident;
+
+    delete coarsemesh;
+    delete hpelements;
+    
+    for (int i = 0; i < materials.Size(); i++)
+      delete [] materials[i];
+  }
+
+
+  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;
+    return *this;
+  }
+
+  void Mesh :: DeleteMesh()
+  {
+    points.SetSize(0);
+    // ptyps.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);
+
+    // ident -> Delete();
+    delete ident;
+    ident = new Identifications (*this);
+    delete topology;
+    topology = new MeshTopology (*this);
+    delete curvedelems;
+    curvedelems = new CurvedElements (*this);
+    delete clusters;
+    clusters = new AnisotropicClusters (*this);
+
+    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) ); 
+
+    lock.UnLock();
+
+    return pi;
+  }
+
+
+  SegmentIndex Mesh :: AddSegment (const Segment & s)
+  { 
+    NgLock lock(mutex);	
+    lock.Lock();
+    timestamp = NextTimeStamp();
+
+    int maxn = max2 (s.p1, s.p2);
+    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.p1] > EDGEPOINT) ptyps[s.p1] = EDGEPOINT;
+      if (ptyps[s.p2] > EDGEPOINT) ptyps[s.p2] = EDGEPOINT;
+    */
+
+    if (maxn <= points.Size())
+      {
+	if (points[s.p1].Type() > EDGEPOINT)
+	  points[s.p1].SetType (EDGEPOINT);
+	if (points[s.p2].Type() > EDGEPOINT)
+	  points[s.p2].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); 
+
+    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;
+
+    while (volelements.Size() > eltyps.Size())
+      eltyps.Append (FREEELEMENT);
+  
+    timestamp = NextTimeStamp();
+
+    lock.UnLock();
+    return ve;
+  }
+
+
+
+
+
+
+  void Mesh :: Save (const string & filename) const
+  {
+    int i, j;
+
+    double scale = 1;  // globflags.GetNumFlag ("scale", 1);
+    int inverttets = 0;  // globflags.GetDefineFlag ("inverttets");
+    int invertsurf = 0;  // globflags.GetDefineFlag ("invertsurfacemesh");
+
+    ofstream outfile(filename.c_str());
+
+
+    outfile << "mesh3d" << "\n";
+
+    outfile << "dimension\n" << GetDimension() << "\n";
+
+    outfile << "\n";
+    outfile << "# surfnr    bcnr   domin  domout      np      p1      p2      p3"
+	    << "\n";
+
+    outfile << "surfaceelementsgi" << "\n";
+    //  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];
+	  }
+
+	for (j = 1; j <= sel.GetNP(); j++)
+	  {
+	    outfile.width(7);	  
+	    outfile << " " << sel.GeomInfoPi(j).trignum;
+	  }
+	outfile << endl;
+      }
+
+    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++)
+	  {
+	    outfile.width(8);
+	    outfile << el[j];
+	  }
+	outfile << "\n";
+      }
+
+
+    outfile << "\n" << "\n";
+    outfile << "   surf1   surf2      p1      p2" << "\n";
+    outfile << "edgesegmentsgi2" << "\n";
+    outfile << GetNSeg() << "\n";
+
+    for (i = 1; i <= GetNSeg(); i++)
+      {
+	const Segment & seg = LineSegment (i);
+	outfile.width(8);
+	outfile << seg.si;
+	outfile.width(8);
+	outfile << 0;
+	outfile.width(8);
+	outfile << seg.p1;
+	outfile.width(8);
+	outfile << seg.p2;
+	outfile << " ";
+	outfile.width(8);
+	outfile << seg.geominfo[0].trignum;
+	outfile << " ";
+	outfile.width(8);
+	outfile << seg.geominfo[1].trignum << endl;
+
+	outfile << " ";
+	outfile.width(8);
+	outfile << seg.surfnr1+1;
+	outfile << " ";
+	outfile.width(8);
+	outfile << seg.surfnr2+1;
+	outfile << " ";
+	outfile.width(8);
+	outfile << seg.edgenr;
+	//      outfile << seg.epgeominfo[0].edgenr;
+	/*
+	  outfile.width(8);
+	  outfile << seg.epgeominfo[0].lefttrig;
+	  outfile.width(8);
+	  outfile << seg.epgeominfo[0].righttrig;
+	*/
+	outfile << " ";
+	outfile.width(12);
+	outfile << seg.epgeominfo[0].dist;
+	outfile << " ";
+	outfile.width(8);
+	outfile << seg.epgeominfo[1].edgenr;
+	/*
+	  outfile.width(8);
+	  outfile << seg.epgeominfo[1].lefttrig;
+	  outfile.width(8);
+	  outfile << seg.epgeominfo[1].righttrig;
+	*/
+	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].X()/scale << "  ";
+	outfile.width(22);
+	outfile << (*this)[pi].Y()/scale << "  ";
+	outfile.width(22);
+	outfile << (*this)[pi].Z()/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";
+	      }
+	  }
+      }
+
+    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 cnt_sing = 0;
+    for (PointIndex pi = PointIndex::BASE; pi < GetNP()+PointIndex::BASE; pi++)
+      if ((*this)[pi].IsSingular()) 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].IsSingular()) 
+	    outfile << int(pi) << 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) << 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) << 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) << 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) << endl;
+      }
+
+
+  }
+
+
+  void Mesh :: Load (const string & filename)
+  {
+    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");
+
+    ifstream infile(filename.c_str());
+    if (!infile.good())
+      throw NgException ("mesh file not found");
+
+    facedecoding.SetSize(0);
+
+    while (infile.good())
+      {
+	infile >> str;
+
+	if (strcmp (str, "dimension") == 0)
+	  {
+	    infile >> dimension;
+	  }
+
+	if (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--;
+
+		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));
+		    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);
+
+		if (invertsurf)
+		  tri.Invert();
+
+		AddSurfaceElement (tri);
+	      }
+	  }
+      
+	if (strcmp (str, "surfaceelementsgi") == 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--;
+
+		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));
+		    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);
+
+		for (j = 1; j <= nep; j++)
+		  infile >> tri.GeomInfoPi(j).trignum;
+
+		if (invertsurf)
+		  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.p1 >> seg.p2;
+		AddSegment (seg);
+	      }
+	  }
+      
+
+
+	if (strcmp (str, "edgesegmentsgi") == 0)
+	  {
+	    infile >> n;
+	    for (i = 1; i <= n; i++)
+	      {
+		Segment seg;
+		int hi;
+		infile >> seg.si >> hi >> seg.p1 >> seg.p2
+		       >> seg.geominfo[0].trignum
+		       >> seg.geominfo[1].trignum;
+		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.p1 >> seg.p2
+		       >> seg.geominfo[0].trignum
+		       >> seg.geominfo[1].trignum
+		       >> seg.surfnr1 >> seg.surfnr2
+		       >> seg.edgenr
+		  // >> seg.epgeominfo[0].edgenr
+		  //		     >> seg.epgeominfo[0].lefttrig
+		  //		     >> seg.epgeominfo[0].righttrig
+		       >> seg.epgeominfo[0].dist
+		       >> seg.epgeominfo[1].edgenr
+		  //		     >> seg.epgeominfo[1].lefttrig
+		  //		     >> seg.epgeominfo[1].righttrig
+		       >> seg.epgeominfo[1].dist;
+		// seg.edgenr = seg.epgeominfo[0].edgenr;
+		seg.epgeominfo[0].edgenr = seg.epgeominfo[1].edgenr;
+
+		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, "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, "singular_points") == 0)
+	  {
+	    infile >> n;
+	    for (i = 1; i <= n; i++)
+	      {
+		PointIndex pi;
+		infile >> pi;
+		(*this)[pi].SetSingular (1);
+	      }
+	  }
+
+	if (strcmp (str, "singular_edge_left") == 0)
+	  {
+	    infile >> n;
+	    for (i = 1; i <= n; i++)
+	      {
+		SegmentIndex si;
+		infile >> si;
+		(*this)[si].singedge_left = 1;
+	      }
+	  }
+	if (strcmp (str, "singular_edge_right") == 0)
+	  {
+	    infile >> n;
+	    for (i = 1; i <= n; i++)
+	      {
+		SegmentIndex si;
+		infile >> si;
+		(*this)[si].singedge_right = 1;
+	      }
+	  }
+
+	if (strcmp (str, "singular_face_inside") == 0)
+	  {
+	    infile >> n;
+	    for (i = 1; i <= n; i++)
+	      {
+		SurfaceElementIndex sei;
+		infile >> sei;
+		GetFaceDescriptor((*this)[sei].GetIndex()).domin_singular = 1;
+	      }
+	  }
+
+	if (strcmp (str, "singular_face_outside") == 0)
+	  {
+	    infile >> n;
+	    for (i = 1; i <= n; i++)
+	      {
+		SurfaceElementIndex sei;
+		infile >> sei;
+		GetFaceDescriptor((*this)[sei].GetIndex()).domout_singular = 1;
+	      }
+	  }
+
+
+
+	strcpy (str, "");
+      }
+  
+    CalcSurfacesOfNode ();
+    //  BuildConnectedNodes ();
+    topology -> Update();
+    clusters -> Update();
+  
+    SetNextMajorTimeStamp();
+    //  PrintMemInfo (cout);
+  }
+  
+
+
+
+
+
+  void Mesh :: Merge (const string & filename)
+  {
+    char str[100];
+    int i, n;
+
+    ifstream infile(filename.c_str());
+    if (!infile.good())
+      throw NgException ("mesh file not found");
+
+    int oldnp = GetNP();
+
+    while (infile.good())
+      {
+	infile >> str;
+
+	if (strcmp (str, "surfaceelementsgi") == 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--;
+
+		// surfnr = 0;
+		// bcp = 1;
+		domin = 2;
+		domout = 1;
+
+		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));
+		    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;
+		  }
+
+		for (j = 1; j <= nep; j++)
+		  infile >> tri.GeomInfoPi(j).trignum;
+
+		AddSurfaceElement (tri);
+	      }
+	  }
+
+	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);
+	      }
+	  }
+
+	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 :: CalcSurfacesOfNode ()
+  {
+    int i, j, k;
+    SurfaceElementIndex sei;
+
+    surfacesonnode.SetSize (GetNP());
+    if (boundaryedges)
+      delete boundaryedges;
+    if (surfelementht)
+      delete surfelementht;
+    if (segmentht)
+      delete segmentht;
+
+    boundaryedges = new INDEX_2_CLOSED_HASHTABLE<int>
+      (3 * (GetNSE() + GetNOpenElements()) + GetNSeg() + 1);
+
+    /*
+      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);
+
+	    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 (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, sel.GetIndex());
+      }
+
+    int np = GetNP();
+    // ptyps.SetSize(np);
+    // ptyps = INNERPOINT;
+    for (PointIndex pi = PointIndex::BASE; 
+	 pi < np+PointIndex::BASE; pi++)
+      points[pi].SetType (INNERPOINT);
+
+    if (GetNFD() == 0) //  || GetFaceDescriptor(1).SurfNr() == 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.p1 : seg.p2;
+	  
+	    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.p1, seg.p2);
+	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)
+  {
+    int i, ii, j, k, l;
+    PointIndex pi;
+    SurfaceElementIndex sei;
+    Element2d hel;
+
+
+    if (1)   
+      { // nodebased 
+
+	int np = GetNP();
+	int ne = GetNE();
+	int nse = GetNSE();
+
+	ARRAY<int,PointIndex::BASE> numonpoint(np);
+
+	Element2d hel;
+
+	numonpoint = 0;
+	ElementIndex ei;
+	for (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 (j = 0; j < el.GetNP(); j++)
+		    numonpoint[el[j]]++;
+	      }
+	  }
+
+	TABLE<ElementIndex,PointIndex::BASE> elsonpoint(numonpoint);
+	for (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 (j = 0; j < el.GetNP(); j++)
+		    elsonpoint.Add (el[j], ei);
+	      }
+	  }
+
+	numonpoint = 0;
+	for (i = 0; i < nse; i++)
+	  {
+	    int ind = surfelements[i].GetIndex();
+	    if (
+		GetFaceDescriptor(ind).DomainIn() && 
+		(dom == 0 || dom == GetFaceDescriptor(ind).DomainIn())
+		||
+		GetFaceDescriptor(ind).DomainOut() && 
+		(dom == 0 || dom == GetFaceDescriptor(ind).DomainOut())
+		)
+	      {
+		hel = surfelements[i];
+		hel.NormalizeNumbering();	  
+		numonpoint[hel[0]]++;
+	      }
+	  }
+
+	TABLE<SurfaceElementIndex,PointIndex::BASE> selsonpoint(numonpoint);
+	for (i = 0; i < nse; i++)
+	  {
+	    int ind = surfelements[i].GetIndex();
+	    if (
+		GetFaceDescriptor(ind).DomainIn() && 
+		(dom == 0 || dom == GetFaceDescriptor(ind).DomainIn())
+		||
+		GetFaceDescriptor(ind).DomainOut() && 
+		(dom == 0 || dom == GetFaceDescriptor(ind).DomainOut())
+		)
+	      {
+		hel = surfelements[i];
+		hel.NormalizeNumbering();	  
+		selsonpoint.Add (hel[0], i);
+	      }
+	  }
+
+
+	INDEX_3_CLOSED_HASHTABLE<INDEX_2> faceht(100);   
+	openelements.SetSize(0);
+      
+	for (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 (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 (i = 1; 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 (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);
+		      }
+		  }
+	    }
+      }
+
+
+#ifdef OLD
+
+    else if (GetNE() || 1)
+      {
+	// new version, general elemetns
+	// hash index: pnum1-3
+	// hash data : domain nr, pnum4
+
+	openelements.SetSize(0);
+      
+
+	const int steps = 4;
+
+	for (k = 0; k < steps; k++)
+	  {
+	  
+	    INDEX_3_CLOSED_HASHTABLE<INDEX_2> faceht( (5 * GetNE() + 2 * GetNSE() ) / steps +1);   
+	  
+	    for (i = 1; i <= GetNSE(); i++)
+	      {
+		hel = SurfaceElement(i);
+		int ind = hel.GetIndex();	  
+	      
+	      
+		if (GetFaceDescriptor(ind).DomainIn() && 
+		    (dom == 0 || dom == GetFaceDescriptor(ind).DomainIn()) )
+		  {
+		    hel.Invert();
+		    hel.NormalizeNumbering();
+		    if (hel.PNum(1) % steps == k)
+		      {
+			INDEX_3 i3(hel.PNum(1), hel.PNum(2), hel.PNum(3));
+			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.NormalizeNumbering();
+		    if (hel.PNum(1) % steps == k)
+		      {
+			INDEX_3 i3(hel.PNum(1), hel.PNum(2), hel.PNum(3));
+			INDEX_2 i2 (GetFaceDescriptor(ind).DomainOut(), 
+				    (hel.GetNP() == 3) 
+				    ? PointIndex(0)
+				    : hel.PNum(4));
+			faceht.Set (i3, i2);
+		      }
+		  }
+	      }
+
+	    for (i = 1; i <= GetNE(); i++)
+	      {
+		const Element & el = VolumeElement(i);
+	      
+		if (dom == 0 || el.GetIndex() == dom)
+		  {
+		    for (j = 1; j <= el.GetNFaces(); j++)
+		      {
+			el.GetFace (j, hel);
+		      
+			hel.NormalizeNumbering();
+			if (hel.PNum(1) % steps != k)
+			  continue;
+		      
+			INDEX_3 i3(hel.PNum(1), hel.PNum(2), hel.PNum(3));
+
+			int pos = faceht.Position (i3);
+			if (pos)
+			  {
+			    INDEX_2 i2;
+			    faceht.GetData (pos, i2);
+			    if (i2.I1() == el.GetIndex())
+			      {
+				i2.I1() = 0;
+				faceht.SetData (pos, i2);
+			      }
+			    else
+			      {
+				if (i2.I1() == 0)
+				  PrintSysError ("more elements on face");
+			      }
+			  }
+			else
+			  {
+			    hel.Invert();
+			    hel.NormalizeNumbering();
+			    INDEX_3 i3(hel.PNum(1), hel.PNum(2), hel.PNum(3));
+			    INDEX_2 i2(el.GetIndex(), 
+				       (hel.GetNP() == 3) 
+				       ? PointIndex(0)
+				       : hel.PNum(4));
+			    faceht.Set (i3, i2);
+			  }
+		      }
+		  }
+	      }  
+	  
+	    for (i = 1; i <= faceht.Size(); i++)
+	      if (faceht.UsedPos (i))
+		{
+		  INDEX_3 i3;
+		  INDEX_2 i2;
+		  faceht.GetData (i, i3, i2);
+		  if (i2.I1() != 0)
+		    {
+		      Element2d tri;
+		      tri.SetType ( (i2.I2() == 0) ? TRIG : QUAD);
+		      for (l = 1; l <= 3; l++)
+			tri.PNum(l) = i3.I(l);
+		      tri.PNum(4) = i2.I2();
+		      tri.SetIndex (i2.I1());
+
+		      tri.Invert();
+		    
+		      openelements.Append (tri);
+		    }
+		}
+	    /*
+	      cout << "FindOpenElements, mem = ";
+	      faceht.PrintMemInfo (cout);
+	    */
+	  }
+
+
+
+
+	/*
+	// open hashing version:
+
+	INDEX_3_HASHTABLE<INDEX_2> faceht(4 * GetNE()+GetNSE()+1);   
+ 
+	for (i = 1; i <= GetNSE(); i++)
+	{
+	Element2d hel = SurfaceElement(i);
+	int ind = hel.GetIndex();	  
+	  
+
+	if (GetFaceDescriptor(ind).DomainIn() && 
+	(dom == 0 || dom == GetFaceDescriptor(ind).DomainIn()) )
+	{
+	hel.NormalizeNumbering();
+	INDEX_3 i3(hel.PNum(1), hel.PNum(2), hel.PNum(3));
+	INDEX_2 i2 (GetFaceDescriptor(ind).DomainIn(), 
+	(hel.GetNP() == 3) ? 0 : hel.PNum(4));
+	faceht.Set (i3, i2);
+	}
+	if (GetFaceDescriptor(ind).DomainOut() &&
+	(dom == 0 || dom == GetFaceDescriptor(ind).DomainOut()) )
+	{
+	hel.Invert();
+	hel.NormalizeNumbering();
+	INDEX_3 i3(hel.PNum(1), hel.PNum(2), hel.PNum(3));
+	INDEX_2 i2 (GetFaceDescriptor(ind).DomainOut(), 
+	(hel.GetNP() == 3) ? 0 : hel.PNum(4));
+	faceht.Set (i3, i2);
+	}
+	}
+
+	for (i = 1; i <= GetNE(); i++)
+	{
+	const Element & el = VolumeElement(i);
+	//	  INDEX_3 i3;
+
+	if (dom == 0 || el.GetIndex() == dom)
+	{
+	for (j = 1; j <= el.GetNFaces(); j++)
+	{
+	Element2d hel;
+	el.GetFace (j, hel);
+	hel.Invert();
+		  
+	hel.NormalizeNumbering();
+	INDEX_3 i3(hel.PNum(1), hel.PNum(2), hel.PNum(3));
+		  
+	if (faceht.Used (i3))
+	{
+	INDEX_2 i2 = faceht.Get(i3);
+	if (i2.I1() == el.GetIndex())
+	{
+	i2.I1() = 0;
+	faceht.Set (i3, i2);
+	}
+	else
+	{
+	if (i2.I1() == 0)
+	PrintSysError ("more elements on face");
+	}
+	}
+	else
+	{
+	hel.Invert();
+	hel.NormalizeNumbering();
+	INDEX_3 i3(hel.PNum(1), hel.PNum(2), hel.PNum(3));
+	INDEX_2 i2(el.GetIndex(), 
+	(hel.GetNP() == 3) ? 0 : hel.PNum(4));
+	faceht.Set (i3, i2);
+	}
+	}
+	}
+	}  
+
+      
+	openelements.SetSize(0);
+	for (i = 1; i <= faceht.GetNBags(); i++)
+	for (j = 1; j <= faceht.GetBagSize(i); j++)
+	{
+	INDEX_3 i3;
+	INDEX_2 i2;
+	faceht.GetData (i, j, i3, i2);
+	if (i2.I1() != 0)
+	{
+	Element2d tri;
+	tri.SetNP ( (i2.I2() == 0) ? 3 : 4);
+	for (k = 1; k <= 3; k++)
+	tri.PNum(k) = i3.I(k);
+	tri.PNum(4) = i2.I2();
+	tri.SetIndex (i2.I1());
+
+	openelements.Append (tri);
+	}
+	}
+	*/
+
+      }
+    else 
+      {
+	for (i = 1; i <= GetNSE(); i++)
+	  {
+	    Element2d hel = SurfaceElement(i);
+	    int ind = SurfaceElement(i).GetIndex();
+
+	    if (GetFaceDescriptor(ind).DomainIn())
+	      openelements.Append (hel);
+	    if (GetFaceDescriptor(ind).DomainOut())
+	      {
+		hel.Invert();
+		openelements.Append (hel);
+	      }
+	  }
+      }
+#endif
+
+
+    int cnt3 = 0, cnt4 = 0;
+    for (i = 1; i <= openelements.Size(); i++)
+      if (openelements.Elem(i).GetNP() == 3)
+	cnt3++;
+      else
+	cnt4++;
+
+
+    MyStr treequad;
+    if (cnt4)
+      treequad = MyStr(" (") + MyStr(cnt3) + MyStr (" + ") + 
+	MyStr(cnt4) + MyStr(")");
+
+    PrintMessage (5, openelements.Size(), treequad, " open elements");
+
+
+    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 < points.Size()+PointIndex::BASE)
+	      points[pi].SetType (FIXEDPOINT);
+	  }
+      }
+
+
+    for (i = 1; i <= GetNSeg(); i++)
+      {
+	const Segment & seg = LineSegment(i);
+	INDEX_2 i2(seg.p1, seg.p2);
+	i2.Sort();
+
+	if (!boundaryedges->Used (i2))
+	  cerr << "WARNING: no boundedge, but seg edge: " << i2 << endl;
+
+	boundaryedges -> Set (i2, 2);
+	segmentht -> Set (i2, i-1);
+      }
+  }
+
+  int Mesh :: HasOpenQuads () const
+  {
+    int i;
+    int no = GetNOpenElements();
+    for (i = 1; i <= no; i++)
+      if (OpenElement(i).GetNP() == 4)
+	return 1;
+    return 0;
+  }
+
+
+
+
+
+  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.p1, seg.p2);
+	    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.p2, seg.p1);
+	    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.p1 = i2.I1();
+	      seg.p2 = 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.p1 == el.PNum(k))
+			seg.geominfo[0] = el.GeomInfoPi(k);
+		      if (seg.p2 == 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.p1 << " - " << seg.p2 
+			 << " len = " << Dist (Point(seg.p1), Point(seg.p2))
+			 << 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.p1) = EDGEPOINT;
+      ptyps.Elem(seg.p2) = EDGEPOINT;
+      }
+      for (i = 1; i <= GetNOpenSegments(); i++)
+      {
+      const Segment & seg = GetOpenSegment (i);
+      ptyps.Elem(seg.p1) = EDGEPOINT;
+      ptyps.Elem(seg.p2) = 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.p1].SetType(EDGEPOINT);
+	points[seg.p2].SetType(EDGEPOINT);
+      }
+    for (i = 1; i <= GetNOpenSegments(); i++)
+      {
+	const Segment & seg = GetOpenSegment (i);
+	points[seg.p1].SetType (EDGEPOINT);
+	points[seg.p2].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.p1);
+	frontpoints.Set (seg.p2);
+      }
+
+    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();
+	  }
+      }
+    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 (i = 1; i <= GetNOpenElements(); i++)
+      {
+	const Element2d & face = OpenElement(i);
+	for (j = 1; j <= face.GetNP(); j++)
+	  dist[face.PNum(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++)
+      {
+	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]];
+      
+	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)
+  {
+    //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)
+  {
+    // 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 :: 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 () 
+  {
+    if (!lochfunc)
+      {
+	Point3d pmin, pmax;
+	GetBox (pmin, pmax);
+	SetLocalH (pmin, pmax, mparam.grading);
+      }
+
+    PrintMessage (3,
+		  "CalcLocalH: ", 
+		  GetNP(), " Points ", 
+		  GetNE(), " Elements ", 
+		  GetNSE(), " Surface Elements");
+
+
+    int i;
+    for (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 (i = 0; i < GetNSeg(); i++)
+      {
+	const Segment & seg = segments[i];
+	const Point3d & p1 = points[seg.p1];
+	const Point3d & p2 = points[seg.p2];
+	/*
+	  INDEX_2 i21(seg.p1, seg.p2);
+	  INDEX_2 i22(seg.p2, seg.p1);
+	  if (identifiedpoints)
+	  if (!identifiedpoints->Used (i21) && !identifiedpoints->Used (i22))
+	*/
+	if (!ident -> UsedSymmetric (seg.p1, seg.p2))
+	  {
+	    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(void)
+  {
+    PrintMessage (3, "Calculating local h from point distances");
+  
+    if (!lochfunc)
+      {
+	Point3d pmin, pmax;
+	GetBox (pmin, pmax);
+      
+	SetLocalH (pmin, pmax, mparam.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 elperr) 
+  {
+    PrintMessage (3, "Calculating local h from surface curvature");
+
+    if (!lochfunc)
+      {
+	Point3d pmin, pmax;
+	GetBox (pmin, pmax);
+      
+	SetLocalH (pmin, pmax, mparam.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.p1, seg.p2);
+	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.p1);
+	const Point3d & p2 = Point(seg.p2);
+	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).p1);
+    linepoint.Set (LineSegment(i).p2);
+    }
+
+    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.p1), Point(seg.p2), loch);
+	  break;
+	}
+      }
+  }
+
+
+  void Mesh :: LoadLocalMeshSize (const char * meshsizefilename)
+  {
+    if (!meshsizefilename) return;
+
+    ifstream msf(meshsizefilename);
+
+    if (!msf) return;
+
+    PrintMessage (3, "Load local mesh-size");
+    int nmsp, nmsl;
+    msf >> 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 ("problem in mesh-size file\n");
+	RestrictLocalH (pi, hi);
+      }
+    msf >> 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 ("problem in mesh-size file\n");
+	RestrictLocalHLine (p1, p2, hi);
+      }  
+  }
+
+
+
+  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
+  {
+    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);
+  }
+
+  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].p1 <= 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.p1);
+	pused.Set (seg.p2);
+      }
+
+    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 doesn�t 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.p1 = op2np[seg.p1];
+	seg.p2 = op2np[seg.p2];
+      }
+
+    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]];
+
+
+    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);
+    int i, j;
+    INDEX_2 i2;
+    int err = 0;
+
+
+    for (i = 1; i <= nf; i++)
+      {
+	const Element2d & sel = OpenElement(i);
+      
+	for (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);
+	    /*
+
+	    if (edges.Used(i2))
+	    {
+	    int hi;
+	    hi = edges.Get(i2);
+	    if (hi != 1) 
+	    err = 1;
+	    edges.Set(i2, 2);
+	    cnt2++;
+	    }
+	    else
+	    {
+	    swap (i2.I1(), i2.I2());
+	    edges.Set(i2, 1);
+	    cnt1++;
+	    }
+	    */
+	  }
+      }
+
+
+    /*
+      if (cnt1 != cnt2)
+      err = 2;
+    */
+
+    for (i = 1; i <= edges.GetNBags(); i++)
+      for (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");
+	      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 Point3d *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;
+
+
+		/*
+		  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
+  {
+    //  return 1;
+    // Test, whether 4 points have a common surface plus
+    // at least 4 edges at the boundary
+
+    int i, j, k;
+  
+    // non-tets are always legal
+    if (el.GetType() != TET)
+      {
+	el.SetLegal (1);
+	return 1;
+      }
+
+    // element has at least 2 inner points ---> legal
+    int cnti = 0;
+    for (j = 0; j < 4; j++)
+      if (PointType(el[j]) == INNERPOINT)
+	cnti++;
+    if (cnti >= 2)
+      {
+	el.SetLegal (1);
+	return 1;
+      }
+
+    // which faces are boundary faces ?
+    Element2d face;
+    int bface[4];
+
+    for (i = 1; i <= 4; i++)
+      {
+	el.GetFace (i, face);
+	INDEX_3 i3 (face.PNum(1), face.PNum(2), face.PNum(3));
+	i3.Sort();
+	bface[i-1] = surfelementht->Used (i3);
+      }
+
+    int bedge[4][4];
+    int segedge[4][4];
+    for (i = 0; i < 4; i++)
+      for (j = 0; j < i; j++)
+	{
+	  bool sege = 0, be = 0;
+
+	  INDEX_2 i2(el[i], el[j]);
+	  i2.Sort();
+
+	  /*	
+	    if (boundaryedges -> Used(i2))
+	    {
+	    be = 1;
+	    if (boundaryedges -> Get(i2) == 2)
+	    sege = 1;
+	    }
+	  */
+	  int pos = boundaryedges -> Position(i2);
+	  if (pos)
+	    {
+	      be = 1;
+	      if (boundaryedges -> GetData(pos) == 2)
+		sege = 1;
+	    }
+
+	  segedge[j][i] = segedge[i][j] = sege;
+	  bedge[j][i] = bedge[i][j] = be;
+	}
+
+    // two boundary faces and no edge is illegal
+    for (i = 0; i < 3; i++)
+      for (j = i+1; j < 4; j++)
+	{
+	  if (bface[i] && bface[j])
+	    {
+	      // common nodes:
+	      int pi1 = 0, pi2;
+	      while (pi1 == i || pi1 == j)
+		pi1++;
+	      pi2 = 6 - i - j - pi1;
+	      if (!segedge[pi1][pi2])
+		{
+		  // 2 boundary faces withoud edge in between
+		  el.SetLegal (0);
+		  return 0;
+		}
+	    }
+	}
+
+
+    // three boundary edges meeting in a Surface point
+    for (i = 0; i < 4; i++)
+      {
+	bool alledges = 1;
+	if (PointType(el[i]) == SURFACEPOINT)
+	  {
+	    for (j = 0; j < 4; j++)
+	      if (j != i)
+		{
+		  if (!bedge[i][j])
+		    {
+		      alledges = 0;
+		      break;
+		    }
+		}
+	    if (alledges)
+	      {
+		//	      cout << "tet illegal due to unmarked node" << endl;
+		el.SetLegal (0);
+		return 0;
+	      }
+	  }
+      }
+
+
+    /*  
+	{
+	// having 3 boundary edges and 4 surface nodes ???
+	int nodehasedge[4];
+	int canbe = 1;   // can be that illegal tet
+
+	for (i = 0; i < 4; i++)
+	nodehasedge[i] = 0;
+	for (i = 1; i <= 4; i++)
+	{
+	if (PointType(el.PNum(i)) != SURFACEPOINT)
+	canbe = 0;
+	for (j = i+1; j <= 4; j++)
+	{
+	INDEX_2 i2(el.PNum(i), el.PNum(j));
+	i2.Sort();
+	if (boundaryedges->Used(i2))
+	{
+	nodehasedge[i-1] = 1;
+	nodehasedge[j-1] = 1;
+	}
+
+	}
+	}
+	for (i = 0; i < 4; i++)
+	if (!nodehasedge[i])
+	canbe = 0;
+
+	if (canbe) return 0;
+    
+	}
+    */
+
+    {
+      // two connected edges on surface, but no face
+
+      int ltestmode = 0; // (el.PNum(1) == 10516);
+
+      if (ltestmode)
+	{
+	  (*testout) << "pnums: " << endl;
+	  for (i = 1; i <= 4; i++)
+	    (*testout) << el.PNum(i) << " ";
+	  (*testout) << endl;
+	}
+
+      for (i = 1; i <= 4; i++)
+	if (PointType(el.PNum(i)) == SURFACEPOINT)
+	  for (j = 1; j <= 4; j++)
+	    if (j != i)
+	      for (k = j+1; k <= 4; k++)
+		if (k != i)
+		  {
+		    int fnr = 10 - i - j - k;
+
+		    if (!bface[fnr-1] &&
+			bedge[i-1][j-1] &&
+			bedge[i-1][k-1])
+		      {
+			el.SetLegal (0);
+			return 0;
+		      }
+		    /*
+		      INDEX_2 e1(el.PNum(i), el.PNum(j));
+		      e1.Sort();
+		      INDEX_2 e2(el.PNum(i), el.PNum(k));
+		      e2.Sort();
+		      INDEX_3 face(el.PNum(i), el.PNum(j), el.PNum(k));
+		      face.Sort();
+		  
+		      if (ltestmode)
+		      {
+		      (*testout) << "i, j, k = " << i << ", " << j << ", " << k << endl;
+		      (*testout) << "eij = " << boundaryedges->Used(e1) 
+		      << " eik = " << boundaryedges->Used(e2) 
+		      << " face = " << surfelementht->Used (face) << endl;
+		      
+		      }
+		  
+		      if (boundaryedges->Used(e1) && 
+		      boundaryedges->Used(e2) &&
+		      !surfelementht->Used (face))
+		      {
+		      //		      cout << "tet illegal due to last case" << endl;
+		      el.SetLegal (0);
+		      return 0;
+		      }
+		    */
+		  }
+    
+    }
+
+
+    {
+      // connected surface edge and edge edge, but no face
+
+      for (i = 1; i <= 4; i++)
+	if (PointType(el.PNum(i)) == EDGEPOINT)
+	  for (j = 1; j <= 4; j++)
+	    if (j != i)
+	      for (k = j+1; k <= 4; k++)
+		if (k != i)
+		  {
+		    int fnr = 10 - i - j - k;
+
+		    if (!bface[fnr-1] &&
+			(bedge[i-1][j-1] && segedge[i-1][k-1] ||
+			 segedge[i-1][j-1] && bedge[i-1][k-1]))
+		      {
+			el.SetLegal (0);
+			return 0;
+		      }
+		  }
+    
+    }
+
+
+
+
+
+    
+    el.SetLegal (1);
+    return 1;
+  
+    /*
+      int i1, i2, i3, i4, j;
+      if (PointType(el.PNum(1)) != INNERPOINT && 
+      PointType(el.PNum(2)) != INNERPOINT && 
+      PointType(el.PNum(3)) != INNERPOINT &&
+      PointType(el.PNum(4)) != INNERPOINT)
+      {      
+      for (i1 = 1; i1 <= surfacesonnode.EntrySize(el.PNum(1)); i1++)
+      for (i2 = 1; i2 <= surfacesonnode.EntrySize(el.PNum(2)); i2++)
+      if (surfacesonnode.Get(el.PNum(1), i1) == 
+      surfacesonnode.Get(el.PNum(2), i2))
+      for (i3 = 1; i3 <= surfacesonnode.EntrySize(el.PNum(3)); i3++)
+      if (surfacesonnode.Get(el.PNum(1), i1) == 
+      surfacesonnode.Get(el.PNum(3), i3))
+      for (i4 = 1; i4 <= surfacesonnode.EntrySize(el.PNum(4)); i4++)
+      if (surfacesonnode.Get(el.PNum(1), i1) == 
+      surfacesonnode.Get(el.PNum(4), i4))
+      {
+      int j, numbe = 0;
+      INDEX_2 i2;
+
+      for (j = 1; j <= 6; j++)
+      {
+      switch (j)
+      {
+      case 1:
+      i2.I1() = el.PNum(1);
+      i2.I2() = el.PNum(2); break;
+      case 2: 
+      i2.I1() = el.PNum(1); 
+      i2.I2() = el.PNum(3); break;
+      case 3: 
+      i2.I1() = el.PNum(1); 
+      i2.I2() = el.PNum(4); break;
+      case 4:
+      i2.I1() = el.PNum(2); 
+      i2.I2() = el.PNum(3); break;
+      case 5:
+      i2.I1() = el.PNum(2);
+      i2.I2() = el.PNum(4); break;
+      case 6: 
+      i2.I1() = el.PNum(3); 
+      i2.I2() = el.PNum(4); break;
+      }
+
+      i2.Sort();
+      if (boundaryedges->Used (i2)) numbe++;
+      }
+
+      if (numbe >= 4)
+      {
+      // 			  (*testout) 
+      // 			    << "Tet illegal: " 
+      // 			    << "mat = " << el.GetIndex() << " "
+      // 			    << "surf = " << surfacesonnode.Get(el.PNum(1), i1) 
+      // 			    << " " 
+      // 			    << el.PNum(1) << " "
+      // 			    << el.PNum(2) << " "
+      // 			    << el.PNum(3) << " "
+      // 			    << el.PNum(4) << endl;
+
+      return 0;
+      }
+      }
+      }
+      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()
+  {
+    // int oldne, oldnse;
+    // int i, j, k, l;
+  
+    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(4);
+		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) (*testout) << "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, 5, 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
+	  
+	    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.PNum(1), el.PNum(3)) <
+		min2 (el.PNum(2), el.PNum(4)))
+	      min2pi = &ntets[0][0];
+	    else
+	      min2pi = &ntets[1][0];
+
+
+	    int firsttet = 1;
+	    for (int j = 1; j <= 2; j++)
+	      {
+		Element nel(4);
+		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;
+
+		if (legal)
+		  {
+		    (*testout) << nel << " ";
+		    if (firsttet)
+		      {
+			VolumeElement(i) = nel;
+			firsttet = 0;
+		      }
+		    else
+		      {
+			AddVolumeElement(nel);
+		      }
+		  }
+	      }
+	    (*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));
+	  }
+
+	timestamp = NextTimeStamp();
+      }
+  }
+
+  void Mesh :: BuildElementSearchTree ()
+  {
+    if (elementsearchtreets == GetTimeStamp())
+      return;
+
+    NgLock lock(mutex);
+    lock.Lock();
+
+    PrintMessage (4, "Rebuild element searchtree");
+
+    if (elementsearchtree)
+      delete elementsearchtree;
+    elementsearchtree = NULL;
+
+    Box3d box;
+    int i, j;
+    int ne = GetNE();
+    if (!ne) 
+      {
+	lock.UnLock();
+	return;
+      }
+
+    box.SetPoint (Point (VolumeElement(1).PNum(1)));
+    for (i = 1; i <= ne; i++)
+      {
+	const Element & el = VolumeElement(i);
+	for (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 (i = 1; i <= ne; i++)
+      {
+	const Element & el = VolumeElement(i);
+	box.SetPoint (Point (el.PNum(1)));
+	for (j = 1; j <= el.GetNP(); j++)
+	  box.AddPoint (Point (el.PNum(j)));
+
+	elementsearchtree -> Insert (box.PMin(), box.PMax(), i);
+      }
+
+    elementsearchtreets = GetTimeStamp();
+
+    lock.UnLock();
+  }
+
+
+  int Mesh :: GetElementOfPoint (const Point3d & p,
+				 double lami[3],
+				 bool build_searchtree,
+				 const int index) const
+  {
+    if (dimension == 2)
+      {
+	int i, j;
+	Vec3d col1, col2, col3;
+	Vec3d rhs, sol;
+	double eps = 1e-6;
+	int ne;
+      
+	ARRAY<int> locels;
+	if (0)
+	  {
+	    elementsearchtree->GetIntersecting (p, p, locels);
+	    ne = locels.Size();
+	  }
+	else
+	  ne = GetNSE();
+	ARRAY<Element2d> loctrigs;
+	Vec3d nv(0, 0, 1);
+	for (i = 1; i <= ne; i++)
+	  {
+	    int ii;
+	    if (0)
+	      ii = locels.Get(i);
+	    else
+	      ii = i;
+
+	    if((index >= 0) && (index != SurfaceElement(ii).GetIndex())) continue;
+
+	    //SZ 
+	    if(SurfaceElement(ii).GetType()==QUAD)
+	      {
+
+		const Element2d & el = SurfaceElement(ii); 
+	      
+		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())-b.Y()*(p.Y()-a.Y()))/
+		      (b.X()*c.Y() -b.Y()*c.X()); 
+		    lami[1]=(-c.X()*(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.) continue; 
+			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(ii);
+			  }
+		      
+			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)
+		  return(ii);
+		      
+		continue; 
+	      
+	      }
+	    else
+	      {
+		//	  SurfaceElement(ii).GetTets (loctets);
+		loctrigs.SetSize(1);
+		loctrigs.Elem(1) = SurfaceElement(ii);
+	      
+	      
+	      
+		for (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 = nv;
+		    rhs = p - p1;
+		  
+		    SolveLinearSystem (col1, col2, col3, rhs, sol);
+		  
+		    if (sol.X() >= -eps && sol.Y() >= -eps && 
+			sol.X() + sol.Y() <= 1+eps)
+		      {
+			lami[0] = sol.X();
+			lami[1] = sol.Y();
+			lami[2] = sol.Z();
+		      
+			return ii;
+		      }
+		  }
+	      }
+	  }
+	 
+	return 0;
+      }  
+    else
+      
+      {
+	int i, j;
+	Vec3d col1, col2, col3;
+	Vec3d rhs, sol;
+	double eps = 1e-4;
+	int ne;
+      
+	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();
+      
+	ARRAY<Element> loctets;
+	for (i = 1; i <= ne; i++)
+	  {
+	    int ii;
+	    if (elementsearchtree)
+	      ii = locels.Get(i);
+	    else
+	      ii = i;
+	  
+	    VolumeElement(ii).GetTets (loctets);
+	  
+	    for (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<Point3d> pointsloc;
+
+		    VolumeElement(ii).GetTetsLocal (loctetsloc);
+		    VolumeElement(ii).GetNodesLocalNew (pointsloc);
+
+		    const Element & le = loctetsloc.Get(j);
+		  
+		    Point3d p = 
+		      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] = p.X();
+		    lami[1] = p.Y();
+		    lami[2] = p.Z();
+		    return ii;
+		  }
+	      }
+	  }
+      
+	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 ()
+  {
+    int fdi;
+    int i, j;
+    int np = GetNP();
+
+    BitArray usedp(np);
+
+    fdi = 1;
+    while (fdi <= GetNFD())
+      {
+	int firstel = 0;
+	for (i = 1; i <= GetNSE(); i++)
+	  if (SurfaceElement(i).GetIndex() == fdi)
+	    {
+	      firstel = i;
+	      break;
+	    }
+	if (!firstel) continue;
+
+	usedp.Clear();
+	for (j = 1; j <= SurfaceElement(firstel).GetNP(); j++)
+	  usedp.Set (SurfaceElement(firstel).PNum(j));
+
+	int changed;
+	do
+	  {
+	    changed = 0;
+	    for (i = 1; i <= GetNSE(); i++)
+	      {
+		const Element2d & el = SurfaceElement(i);
+		if (el.GetIndex() != fdi)
+		  continue;
+
+		int has = 0;
+		int hasno = 0;
+		for (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 (j = 1; j <= el.GetNP(); j++)
+		    usedp.Set (el.PNum(j));
+	      }
+	  }
+	while (changed);
+
+	int nface = 0;
+	for (i = 1; i <= GetNSE(); i++)
+	  {
+	    Element2d & el = SurfaceElement(i);
+	    if (el.GetIndex() != fdi)
+	      continue;	  
+
+	    int hasno = 0;
+	    for (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 :: GetSurfaceElementsOfFace (int facenr, ARRAY<SurfaceElementIndex> & sei) const
+  {
+    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);
+  }
+
+
+
+
+  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 :: 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;
+
+    int i;
+    for (i = 1; i <= GetNSE(); i++)
+      if (SurfaceElement(i).GetIndex() == faceindex &&
+	  SurfaceElement(i).GetNP() != 3)
+	return 0;
+    return 1;
+  }
+
+  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 :: 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 0000000000..4ad0edbb9b
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/meshclass.hpp
@@ -0,0 +1,620 @@
+#ifndef MESHCLASS
+#define MESHCLASS
+
+/**************************************************************************/
+/* File:   meshclass.hh                                                    */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   20. Nov. 99                                                    */
+/**************************************************************************/
+
+/*
+  The mesh class
+*/
+
+
+
+enum resthtype { RESTRICTH_FACE, RESTRICTH_EDGE, 
+		 RESTRICTH_SURFACEELEMENT, RESTRICTH_POINT, RESTRICTH_SEGMENT };
+
+class HPRefElement;
+
+
+/// 2d/3d mesh
+class Mesh
+{
+public:
+  //  typedef MoveableArray<MeshPoint> 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;
+  /// type of point, is set in calcsurfacesofnode
+  //  ARRAY<POINTTYPE,PointIndex::BASE> ptyps;
+  /// type of element, set in calcsurfacesofnode
+  ARRAY<ELEMENTTYPE> eltyps;
+
+  /// 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;
+  ///
+  ARRAY<double> maxhdomain;
+  
+  /**
+     the face-index of the surface element maps into
+     this table.
+  */
+  ARRAY<FaceDescriptor> facedecoding;
+
+  /// sub-domain materials 
+  ARRAY<char*> materials;
+
+  /// 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 semaphor.
+  NgMutex mutex;
+
+
+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;
+
+
+
+  ///
+  Mesh();
+  ///
+  ~Mesh();
+
+  Mesh & operator= (const Mesh & mesh2);
+  
+  ///
+  void DeleteMesh();
+  
+  ///
+  void ClearSurfaceElements()
+  { 
+    surfelements.SetSize(0); 
+    timestamp = NextTimeStamp();
+  }
+
+  ///
+  void ClearVolumeElements()
+  {
+    volelements.SetSize(0); 
+    eltyps.SetSize(0);
+    timestamp = NextTimeStamp();
+  }
+
+  ///
+  void ClearSegments()
+  { 
+    segments.SetSize(0); 
+    timestamp = NextTimeStamp();
+  }
+  
+  ///
+  bool TestOk () const;
+
+
+  PointIndex AddPoint (const Point3d & p, int layer = 1);
+  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]; }
+
+  /*
+  POINTTYPE PointType (int i) const { return ptyps.Get(i); }
+  POINTTYPE PointType (PointIndex pi) const { return ptyps[pi]; }
+  */
+  POINTTYPE PointType (int i) const { return points.Get(i).Type(); }
+  POINTTYPE PointType (PointIndex pi) const { return points[pi].Type(); }
+
+
+  const T_POINTS & Points() const { return points; }
+  T_POINTS & Points() { return points; }
+  // ARRAY<POINTTYPE,PointIndex::BASE> & PointTypes() { return ptyps; }
+
+
+
+
+
+
+  SegmentIndex AddSegment (const Segment & s);
+  void DeleteSegment (int segnr)
+  {
+    segments.Elem(segnr).p1 = PointIndex::BASE-1;
+    segments.Elem(segnr).p2 = PointIndex::BASE-1;
+  }
+
+  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]; }
+
+
+
+
+  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]; }
+
+  
+  void GetSurfaceElementsOfFace (int facenr, ARRAY<SurfaceElementIndex> & sei) const;
+
+
+
+  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 (int i) const { return eltyps.Get(i); }
+  ELEMENTTYPE ElementType (ElementIndex i) const { return eltyps[i]; }
+
+  const T_VOLELEMENTS & VolumeElements() const { return volelements; }
+  T_VOLELEMENTS & VolumeElements() { return volelements; }
+
+
+  ///
+  double ElementError (int eli) const;
+
+  /// 
+  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 */
+  void FindOpenElements (int dom = 0);
+
+  
+  /**
+     finds segments without surface element,
+     and surface elements without neighbours.
+     store in opensegmentsy
+  */
+  void FindOpenSegments (int surfnr = 0);
+  /**
+     remove one layer of surface elements
+  */
+  void RemoveOneLayerSurfaceElements ();
+
+
+  int GetNOpenSegments () { return opensegments.Size(); }
+  const Segment & GetOpenSegment (int nr) { return opensegments.Get(nr); }
+  
+  /**
+     Checks overlap of boundary
+     return == 1, iff overlap
+  */
+  int CheckOverlappingBoundary ();
+  /**
+     Checks consistent boundary
+     return == 0, everything ok
+  */
+  int CheckConsistentBoundary () const;
+
+  /*
+    checks element orientation
+  */
+  int CheckVolumeMesh () const;
+
+
+  /**
+     finds average h of surface surfnr if surfnr > 0,
+     else of all surfaces.
+  */
+  double AverageH (int surfnr = 0) const;
+  /// Calculates localh 
+  void CalcLocalH ();
+  ///
+  void SetLocalH (const Point3d & pmin, const Point3d & pmax, double grading);
+  ///
+  void RestrictLocalH (const Point3d & p, double hloc);
+  ///
+  void RestrictLocalHLine (const Point3d & p1, const Point3d & p2, 
+			   double hloc);
+  /// number of elements per radius
+  void CalcLocalHFromSurfaceCurvature(double elperr);
+  ///
+  void CalcLocalHFromPointDistances(void);
+  ///
+  void RestrictLocalH (resthtype rht, int nr, double loch);
+  ///
+  void LoadLocalMeshSize (const char * meshsizefilename);
+  ///
+  void SetGlobalH (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; }
+
+  /// Find bounding box
+  void GetBox (Point3d & pmin, Point3d & pmax, int dom = -1) const;
+
+  /// Find bounding box of points of typ ptyp or less
+  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
+  int 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
+  {
+    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.
+  */
+  void Compress ();
+
+  ///
+  void Save (const string & filename) const;
+  ///
+  void Load (const string & filename);
+  ///
+  void Merge (const string & filename);
+
+
+  ///
+  void ImproveMesh (OPTIMIZEGOAL goal = OPT_QUALITY);
+
+  ///
+  void ImproveMeshJacobian (OPTIMIZEGOAL goal = OPT_QUALITY);
+  
+
+  /*
+#ifdef SOLIDGEOM
+  /// old
+  void ImproveMesh (const CSGeometry & surfaces, 
+		    OPTIMIZEGOAL goal = OPT_QUALITY);
+#endif  
+  */
+
+  /**
+     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
+  void BuildElementSearchTree ();
+  /// gives element of point, barycentric coordinates
+  int GetElementOfPoint (const Point3d & p,
+			 double * lami,
+			 bool build_searchtree = 0,
+			 const int index = -1) 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); }
+
+
+  ///
+  void SetMaterial (int domnr, const char * mat);
+  ///
+  const char * GetMaterial (int domnr) const;
+    
+
+  ///
+  void ClearFaceDescriptors()
+  { facedecoding.SetSize(0); }
+
+  ///
+  int GetNFD () const
+  { return facedecoding.Size(); }
+
+  const FaceDescriptor & GetFaceDescriptor (int i) const
+  { return facedecoding.Get(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; }
+
+
+
+  /// 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; }
+
+  ///
+  friend void OptimizeRestart (Mesh & mesh3d);
+  ///
+  void PrintMemInfo (ostream & ost) const;
+  /// 
+  friend class Meshing3;
+};
+
+
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/meshing/meshfunc.cpp b/contrib/Netgen/libsrc/meshing/meshfunc.cpp
new file mode 100644
index 0000000000..90fe9e68c5
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/meshfunc.cpp
@@ -0,0 +1,688 @@
+#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 i, oldne;
+    PointIndex pi;
+
+    int meshed;
+    int cntsteps; 
+
+
+    ARRAY<INDEX_2> connectednodes;
+
+    mesh3d.Compress();
+
+    //  mesh3d.PrintMemInfo (cout);
+
+
+
+    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();
+	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());
+ 
+	mp.maxh = min2 (globmaxh, mesh3d.MaxHDomain(k));
+
+	mesh3d.CalcSurfacesOfNode();
+	mesh3d.FindOpenElements(k);
+
+	if (!mesh3d.GetNOpenElements())
+	  continue;
+
+	int qstep;
+	for (qstep = 1; qstep <= 3; qstep++)
+	  {
+	    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 = 100;
+	      
+		for (pi = PointIndex::BASE; 
+		     pi < mesh3d.GetNP()+PointIndex::BASE; pi++)
+		  meshing.AddPoint (mesh3d[pi], pi);
+
+		mesh3d.GetIdentifications().GetPairs (0, connectednodes);
+		for (i = 1; i <= connectednodes.Size(); i++)
+		  meshing.AddConnectedPair (connectednodes.Get(i));
+	      
+		for (i = 1; i <= mesh3d.GetNOpenElements(); i++)
+		  {
+		    Element2d hel = mesh3d.OpenElement(i);
+		    meshing.AddBoundaryElement (hel);
+		  }
+	      
+		oldne = mesh3d.GetNE();
+	      
+		meshing.GenerateMesh (mesh3d, mpquad);
+	      
+		for (i = oldne + 1; i <= mesh3d.GetNE(); i++)
+		  mesh3d.VolumeElement(i).SetIndex (k);
+	      
+		(*testout) 
+		  << "mesh has " << mesh3d.GetNE() << " prism ?�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 (pi = PointIndex::BASE; 
+		 pi < mesh3d.GetNP()+PointIndex::BASE; pi++)
+	      meshing.AddPoint (mesh3d[pi], pi);
+	  
+
+	    for (i = 1; i <= mesh3d.GetNOpenElements(); i++)
+	      meshing.AddBoundaryElement (mesh3d.OpenElement(i));
+	  
+	    oldne = mesh3d.GetNE();
+
+	    meshing.Delaunay (mesh3d, mp);
+
+	    for (i = oldne + 1; i <= mesh3d.GetNE(); i++)
+	      mesh3d.VolumeElement(i).SetIndex (k);
+
+	    PrintMessage (3, mesh3d.GetNP(), " points, ",
+			  mesh3d.GetNE(), " elements");
+	  }
+
+
+	cntsteps = 0;
+	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);
+      
+
+	    for (PointIndex pi = PointIndex::BASE; 
+		 pi < mesh3d.GetNP()+PointIndex::BASE; pi++)
+	      meshing.AddPoint (mesh3d[pi], pi);
+
+	    for (i = 1; i <= mesh3d.GetNOpenElements(); i++)
+	      meshing.AddBoundaryElement (mesh3d.OpenElement(i));
+
+
+	    oldne = mesh3d.GetNE();
+
+	    mp.giveuptol = 15; 
+	    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");
+
+		//	      mesh3d.Save ("tmp.vol");
+
+
+		MeshOptimize3d optmesh;
+
+		const char * optstr = "mcmstmcmstmcmstmcm";
+		int j;
+		for (j = 1; j <= strlen(optstr); j++)
+		  {
+		    mesh3d.CalcSurfacesOfNode();
+		    mesh3d.FreeOpenElementsEnvironment(2);
+
+		    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(OPT_REST); break;
+		      }	  
+
+		  }
+
+		mesh3d.FindOpenElements(k);	      
+		PrintMessage (3, "Call remove problem");
+		RemoveProblem (mesh3d);
+		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, j;
+
+    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;
+
+	teterrpow = mp.opterrpow;
+	for (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 't': optmesh.SwapImprove2(mesh3d); break;
+#ifdef SOLIDGEOM
+	      case 'm': mesh3d.ImproveMesh(*geometry); break;
+	      case 'M': mesh3d.ImproveMesh(*geometry); break;
+#else
+	      case 'm': mesh3d.ImproveMesh(); break;
+	      case 'M': mesh3d.ImproveMesh(); break;
+#endif
+	      
+	      case 'j': mesh3d.ImproveMeshJacobian(); break;
+	      }
+	  }
+	MeshQuality3d (mesh3d);
+      }
+  
+    return MESHING3_OK;
+  }
+
+
+
+
+  void RemoveIllegalElements (Mesh & mesh3d)
+  {
+    int it = 10;
+    int nillegal, oldn;
+    int i;
+
+    PrintMessage (1, "Remove Illegal Elements");
+    // return, if non-pure tet-mesh
+    /*
+      if (!mesh3d.PureTetMesh())
+      return;
+    */
+    mesh3d.CalcSurfacesOfNode();
+
+    nillegal = mesh3d.MarkIllegalElements();
+
+    MeshOptimize3d optmesh;
+    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 0000000000..ab2d661050
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/meshfunc.hpp
@@ -0,0 +1,41 @@
+#ifndef FILE_MESHFUNC
+#define FILE_MESHFUNC
+
+/**************************************************************************/
+/* File:   meshfunc.hh                                                    */
+/* Author: Johannes Gerstmayr                                             */
+/* 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 0000000000..f329d1df76
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/meshfunc2d.cpp
@@ -0,0 +1,61 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+namespace netgen
+{
+
+  void Optimize2d (Mesh & mesh, MeshingParameters & mp)
+  {
+    int i, j;
+
+    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 (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);
+		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 0000000000..a4442867b3
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/meshing.hpp
@@ -0,0 +1,60 @@
+#ifndef FILE_MESHING
+#define FILE_MESHING
+
+#include <myadt.hpp>
+#include <gprim.hpp>
+#include <linalg.hpp>
+#include <opti.hpp>
+
+namespace netgen
+{
+
+  class CSGeometry;
+  
+  
+#include "msghandler.hpp"
+
+#include "meshtype.hpp"
+#include "localh.hpp"
+#include "meshclass.hpp"
+#include "global.hpp"
+
+
+#include "meshtool.hpp"
+#include "ruler2.hpp"
+#include "adfront2.hpp"
+#include "meshing2.hpp"
+#include "improve2.hpp"
+
+
+#include "geomsearch.hpp"
+#include "adfront3.hpp"
+#include "ruler3.hpp"
+
+#ifndef SMALLLIB
+#define _INCLUDE_MORE
+#endif
+#ifdef LINUX
+#define _INCLUDE_MORE
+#endif
+
+#ifdef _INCLUDE_MORE
+#include "meshing3.hpp"
+#include "improve3.hpp"
+#endif
+#include "findip.hpp"
+
+#include "topology.hpp"
+#include "curvedelems.hpp"
+#include "clusters.hpp"
+
+#ifdef _INCLUDE_MORE
+#include "meshfunc.hpp"
+#endif
+#include "bisect.hpp"
+#include "hprefinement.hpp"
+#include "boundarylayer.hpp"
+#include "specials.hpp"
+}
+
+#endif
diff --git a/contrib/Netgen/libsrc/meshing/meshing2.cpp b/contrib/Netgen/libsrc/meshing/meshing2.cpp
new file mode 100644
index 0000000000..e7643194c4
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/meshing2.cpp
@@ -0,0 +1,1864 @@
+#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 Box3d & aboundingbox)
+  {
+    boundingbox = aboundingbox;
+
+    LoadRules (NULL);
+    // LoadRules ("rules/quad.rls");
+    // LoadRules ("rules/triangle.rls");
+
+    adfront = new AdFront2(boundingbox);
+    starttime = GetTime();
+  }
+
+
+  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)
+  {
+    // (*testout) << "add point " << globind << endl;
+    adfront ->AddPoint (p, globind, mgi);
+  }
+
+  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, i2, 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;
+  }
+
+  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 (Point3d & p1, 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, double gh, int facenr)
+  {
+    ARRAY<int> pindex, lindex;
+    ARRAY<int> delpoints, dellines;
+
+    ARRAY<PointGeomInfo> upgeominfo;  // unique info
+    ARRAY<MultiPointGeomInfo> mpgeominfo;  // multiple info
+
+    ARRAY<Element2d> locelements;
+
+    int i, k, z1, z2, j, oldnp;
+    SurfaceElementIndex sei;
+    int baselineindex;
+    bool found;
+    int rulenr;
+    int globind;
+    Point3d p1, p2;
+
+    const PointGeomInfo * blgeominfo1;
+    const PointGeomInfo * blgeominfo2;
+
+    bool morerisc;
+    bool debugflag;
+
+    double h, his, hshould;
+
+
+    // 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, maxlegalline;
+    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)
+	  {
+	    const Point3d & sep1 = mesh[sel.PNum(1)];
+	    const Point3d & sep2 = mesh[sel.PNum(2)];
+	    const Point3d & sep3 = mesh[sel.PNum(3)];
+	    Point3d sepmin(sep1), sepmax(sep2);
+	    sepmin.SetToMin (sep2);
+	    sepmin.SetToMin (sep3);
+	    sepmin.SetToMax (sep2);
+	    sepmin.SetToMax (sep3);
+
+	    surfeltree.Insert (sepmin, sepmax, sei);
+	  }
+
+      
+	double trigarea = Cross (Vec3d (mesh.Point (sel.PNum(1)),
+					mesh.Point (sel.PNum(2))),
+				 Vec3d (mesh.Point (sel.PNum(1)),
+					mesh.Point (sel.PNum(3)))).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;
+
+      }
+
+
+    char * savetask = multithread.task;
+    multithread.task = "Surface meshing";
+
+    adfront ->SetStartFront ();
+
+
+    int plotnexttrial = 999;
+    //  starttime = GetTime();
+    while (!adfront ->Empty()) //  && !multithread.terminate)
+      {
+	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 (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";
+	  }
+
+
+	baselineindex = adfront -> SelectBaseLine (p1, p2, blgeominfo1, blgeominfo2, qualclass);
+
+	// cout << "baseline = " << baselineindex << ", p1, p2 = " << p1 << ", " << p2 << endl;
+
+	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);
+	
+
+	if (qualclass > 200)
+	  {
+	    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;
+
+	    //	    (*testout) << "3d->2d transformation" << endl;
+
+	    for (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) << endl << endl << 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 (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 (0);
+			    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 (i = 1; i <= legalpoints.Size(); i++)
+	      legalpoints.Elem(i) = 1;
+
+
+	    for (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) == 0)
+		  legalpoints.Elem(i) = 0;
+
+		if (plainpoints.Elem(i).Y() < 0)
+		  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 (mparam.checkchartboundary)
+	      {
+		for (i = 1; i <= chartboundpoints.Size(); i++)
+		  {
+		    plainpoints.Append (chartboundpoints.Get(i));
+		    locpoints.Append (chartboundpoints3d.Get(i));
+		    legalpoints.Append (0);
+		  }
+	      
+
+		for (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);
+	    //	    (*testout) << "Rule Nr = " << rulenr << endl;
+	    if (!rulenr)
+	      {
+		found = 0;
+		if ( debugflag || debugparam.haltnosuccess )
+		  PrintWarning ("no rule found");
+	      }
+	  }
+      
+	for (i = 1; i <= locelements.Size() && found; i++)
+	  {
+	    const Element2d & el = locelements.Get(i);
+
+	    for (j = 1; j <= el.GetNP(); j++)
+	      if (el.PNum(j) <= oldnp && !pindex.Get(el.PNum(j)))
+		{
+		  found = 0;
+		  PrintSysError ("meshing2, index missing");
+		}
+	  }
+
+
+	if (found)
+	  {
+	    locpoints.SetSize (plainpoints.Size());
+	    upgeominfo.SetSize(locpoints.Size());
+
+	    for (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 (i = oldnl+1; i <= loclines.Size(); i++)
+	      {
+		double eh = Dist (locpoints.Get(loclines.Get(i).I1()),
+				  locpoints.Get(loclines.Get(i).I2()));
+		if (eh > newedgemaxh)
+		  newedgemaxh = eh;
+	      }
+
+	    for (i = 1; i <= locelements.Size(); i++)
+	      {
+		Point3d pmin = locpoints.Get(locelements.Get(i).PNum(1));
+		Point3d pmax = pmin;
+		for (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 (i = 1; i <= locelements.Size(); i++)
+	      for (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 (i = 1; i <= locelements.Size(); i++)
+	      for (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 && mparam.checkoverlap)
+	  {
+	    // cout << "checkoverlap" << endl;
+	    // test for overlaps
+	  
+	    Point3d hullmin(1e10, 1e10, 1e10);
+	    Point3d hullmax(-1e10, -1e10, -1e10);
+	  
+	    for (i = 1; i <= locelements.Size(); i++)
+	      for (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 (i = oldnp+1; i <= locpoints.Size(); i++)
+	      critpoints.Append (locpoints.Get(i));
+
+	    for (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 (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++)
+		  {
+		    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 (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 (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 (i = oldnp+1; i <= locpoints.Size(); i++)
+	      {
+		globind = mesh.AddPoint (locpoints.Get(i));
+		pindex.Elem(i) = adfront -> AddPoint (locpoints.Get(i), globind);
+	      }
+	      
+	    for (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()) == 0 || 
+		    pindex.Get(loclines.Get(i).I2()) == 0)
+		  {
+		    (*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 (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 (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 (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;
+
+
+	      
+
+		const Point3d & sep1 = mesh.Point (mtri.PNum(1));
+		const Point3d & sep2 = mesh.Point (mtri.PNum(2));
+		const Point3d & sep3 = mesh.Point (mtri.PNum(3));
+
+		Point3d sepmin(sep1), sepmax(sep1);
+		for (j = 2; j <= mtri.GetNP(); j++)
+		  {
+		    sepmin.SetToMin (mesh.Point (mtri.PNum(j)));
+		    sepmax.SetToMax (mesh.Point (mtri.PNum(j)));
+		  }
+
+		surfeltree.Insert (sepmin, sepmax, mesh.GetNSE());
+
+
+		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;
+
+	      
+
+
+		for (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 (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 (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 )
+	      {
+		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) << "old number of lines = " << oldnl << endl;
+		for (i = 1; i <= loclines.Size(); i++)
+		  {
+		    (*testout) << "line ";
+		    for (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 (i = 1; i <= loclines.Size(); i++)
+		  {
+		    (*testout) << "line ";
+		    for (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;
+
+
+    //  cout << "surfeltree.depth = " << surfeltree.Tree().Depth() << endl;
+    EndMesh ();
+
+    if (!adfront->Empty())
+      return MESHING2_GIVEUP;
+    
+    return MESHING2_OK;
+  }
+
+
+
+
+
+
+
+
+
+}
+
+
+
+
+
+
+
+#ifdef OPENGL
+
+/* *********************** 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);
+  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 0000000000..a68a095d5e
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/meshing2.hpp
@@ -0,0 +1,149 @@
+#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;
+  /// 
+  Box3d boundingbox;
+  ///
+  double starttime;
+public:
+  ///
+  Meshing2 (const Box3d & aboundingbox);
+
+  ///
+  virtual ~Meshing2 ();
+
+  /// Load rules, either from file, or compiled rules
+  void LoadRules (const char * filename);
+
+  /// 
+  MESHING2_RESULT GenerateMesh (Mesh & mesh, double gh, int facenr);
+
+  ///
+  void AddPoint (const Point3d & p, PointIndex globind, MultiPointGeomInfo * mgi = NULL);
+
+  ///
+  void AddBoundaryElement (INDEX i1, INDEX i2,
+			   const PointGeomInfo & gi1, const PointGeomInfo & gi2);
+  
+  ///
+  void SetStartTime (double astarttime);
+
+protected:
+  ///
+  virtual void StartMesh ();
+  ///
+  virtual void EndMesh ();
+  ///
+  virtual double CalcLocalH (const Point3d & p, double gh) const;
+
+  ///
+  virtual void DefineTransformation (Point3d & p1, 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);
+  
+
+};
+
+
+
+
+
+
+
+
+#endif
+
+
+
+
+
+
+
diff --git a/contrib/Netgen/libsrc/meshing/meshing3.cpp b/contrib/Netgen/libsrc/meshing/meshing3.cpp
new file mode 100644
index 0000000000..c349b85e79
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/meshing3.cpp
@@ -0,0 +1,1260 @@
+#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<Element2d> & 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)
+{
+  adfront -> AddFace(elem);
+}  
+
+int Meshing3 :: AddConnectedPair (const INDEX_2 & apair)
+{
+  return adfront -> AddConnectedPair (apair);
+}
+
+MESHING3_RESULT Meshing3 :: 
+GenerateMesh (Mesh & mesh, const MeshingParameters & mp)
+{
+  ARRAY<Point3d> locpoints;      // local points
+  ARRAY<Element2d> 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
+  
+  int loktestmode = 0;
+
+  int uselocalh = mparam.uselocalh;
+
+  int giveuptol = mp.giveuptol; // 
+  MeshingStat3d stat;      // statistics
+  int plotstat_oldne = -1;
+
+  
+  // for star-shaped domain meshing
+  ARRAY<MeshPoint> grouppoints;      
+  ARRAY<Element2d> groupfaces;
+  ARRAY<PointIndex> grouppindex;
+  ARRAY<INDEX> groupfindex;
+  
+  
+  float minerr;
+  int hasfound;
+  double tetvol;
+  int giveup = 0;
+
+  
+  ARRAY<Point3d> tempnewpoints;
+  ARRAY<Element2d> 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");
+
+      if (giveup)
+	break;
+
+      // 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 Element2d & 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);
+      
+      stat.qualclass =
+        adfront -> GetLocals (baseelem, locpoints, locfaces, 
+			      pindex, findex, connectedpairs,
+			      houter, hinner,
+			      locfacesplit);
+
+
+      int pi1 = pindex.Get(locfaces[0].PNum(1));
+      int pi2 = pindex.Get(locfaces[0].PNum(2));
+      int pi3 = pindex.Get(locfaces[0].PNum(3));
+
+      /*      
+      (*testout) << "baseel = " << baseelem << ", ind = " << findex.Get(1) << endl;
+      (*testout) << "pi = " << pi1 << ", " << pi2 << ", " << pi3 << endl;
+      */
+
+      loktestmode = 0;
+      //      testmode = loktestmode;
+      // loktestmode = testmode =  (adfront->GetFace (baseelem).GetNP() == 4) && (rules.Size() == 5);
+
+      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;
+	  }
+      else
+	for (i = 1; i <= allowpoint.Size(); i++)	
+	  allowpoint.Elem(i) = 1;
+
+      
+
+
+      
+      if (stat.qualclass >= mp.starshapeclass)   
+	{
+	  // star-shaped domain removing
+
+	  grouppoints.SetSize (0);
+	  groupfaces.SetSize (0);
+	  grouppindex.SetSize (0);
+	  groupfindex.SetSize (0);
+	  
+	  adfront -> GetGroup (findex.Get(1), grouppoints, groupfaces, 
+			       grouppindex, groupfindex);
+
+	  int onlytri = 1;
+	  for (i = 1; i <= groupfaces.Size(); i++)
+	    if (groupfaces.Get(i).GetNP() != 3) 
+	      onlytri = 0;
+
+	  if (onlytri && groupfaces.Size() <= 20 &&
+	      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;
+
+      for (rotind = 1; rotind <= locfaces.Get(1).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)
+		allowpoint.Elem(i) = 0;
+	    }
+
+	  
+	  if (loktestmode)
+	    (*testout) << "plainpoints = " << endl << plainpoints << endl;
+
+	  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;
+	    }
+	  
+	  found = ApplyRules (plainpoints, allowpoint, 
+			      locfaces, locfacesplit, connectedpairs,
+			      locelements, delfaces, 
+			      stat.qualclass, mp.sloppy, rotind, err);
+	  
+	  if (loktestmode)
+	    {
+	      (*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)++;
+
+
+	  if (stat.qualclass > 80)
+	    {
+	      cerr << "Sorry, I failed" << endl;
+	      mesh.Save ("tempvol.out");
+	      exit (1);
+	    }
+	  
+	  
+	  // 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) << "testmode found" << 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 (testmode)
+	    {
+	      (*testout) << "testmode locpoints" << 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));
+	}
+
+      locelements.SetSize (0);
+      delpoints.SetSize(0);
+      delfaces.SetSize(0);
+
+      if (stat.qualclass >= giveuptol)
+	giveup = 1;
+#ifdef MYGRAPH
+      if (plotvolmesh && plotvolmesh->GiveUp())
+	giveup = 1;
+#endif
+    }
+  
+
+
+  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, i1, i2, i3, j1, j2, j3;
+  int n1, n2, n3, n, min1, min2, min3, max1, max2, max3;
+  int changed, filled;
+  double xmin, xmax, ymin, ymax, zmin, zmax;
+  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 Element2d & 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, pi2, pi3, pi4;
+
+	      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)
+		    {
+		      Element2d 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)
+{
+  int i, j;
+  
+  double filldist = mp.filldist;
+
+  (*testout) << "blockfill local h" << endl;
+  (*testout) << "rel filldist = " << filldist << endl;
+  PrintMessage (3, "blockfill local h");
+
+  /*  
+  (*mycout) << "boxes: " << mesh.LocalHFunction().GetNBoxes() << endl
+	    << "filldist = " << filldist << endl;
+  */
+  ARRAY<Point3d> npoints;
+  
+  adfront -> CreateTrees();
+
+  Point3d mpmin, mpmax;
+  // mesh.GetBox (mpmin, mpmax);
+  bool firstp = 1;
+
+  double maxh = 0;
+  for (i = 1; i <= adfront->GetNF(); i++)
+    {
+      const Element2d & el = adfront->GetFace(i);
+      for (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;
+
+	  if (firstp)
+	    {
+	      mpmin = p1;
+	      mpmax = p1;
+	      firstp = 0;
+	    }
+	  else
+	    {
+	      mpmin.SetToMin  (p1);
+	      mpmax.SetToMax  (p1);
+	    }
+	}
+    }
+
+  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;
+
+  int changed;
+  do 
+    {
+      mesh.LocalHFunction().ClearFlags();
+
+      for (i = 1; i <= adfront->GetNF(); i++)
+	{
+	  const Element2d & el = adfront->GetFace(i);
+	  Point3d pmin = adfront->GetPoint (el.PNum(1));
+	  Point3d pmax = pmin;
+	  
+	  for (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);
+	  //	  (*testout) << "cut : " << pmin << " - " << pmax << endl;
+	  mesh.LocalHFunction().CutBoundary (pmin, pmax);
+	}
+
+      locadfront = adfront;
+      mesh.LocalHFunction().FindInnerBoxes (adfront, NULL);
+
+      npoints.SetSize(0);
+      mesh.LocalHFunction().GetInnerPoints (npoints);
+
+      changed = 0;
+      for (i = 1; i <= npoints.Size(); i++)
+	{
+	  if (mesh.LocalHFunction().GetH(npoints.Get(i)) > 1.5 * maxh)
+	    {
+	      mesh.LocalHFunction().SetH (npoints.Get(i), 
+maxh);
+	      changed = 1;
+	    }
+	}
+    }
+  while (changed);
+
+  if (debugparam.slowchecks)
+    (*testout) << "Blockfill with points: " << endl;
+  for (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 (i = 1; i <= adfront->GetNF(); i++)
+    {
+      const Element2d & el = adfront->GetFace(i);
+      Point3d pmin = adfront->GetPoint (el.PNum(1));
+      Point3d pmax = pmin;
+      
+      for (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 (i = 1; i <= adfront->GetNF(); i++)
+    {
+      const Element2d & el = adfront->GetFace(i);
+      Point3d pmin = adfront->GetPoint (el.PNum(1));
+      Point3d pmax = pmin;
+      
+      for (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);
+    }
+
+  locadfront = adfront;
+  loch2.FindInnerBoxes (adfront, NULL);
+
+  npoints.SetSize(0);
+  loch2.GetOuterPoints (npoints);
+  
+  for (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 0000000000..45f44751e3
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/meshing3.hpp
@@ -0,0 +1,128 @@
+#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<Element2d> & 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);
+  ///
+  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, 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 0000000000..89bb2b16dd
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/meshtool.cpp
@@ -0,0 +1,978 @@
+#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 Point3d *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);
+    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)
+  }
+
+
+
+
+
+  double teterrpow = 2;
+
+  double CalcTetBadness (const Point3d & p1, const Point3d & p2,
+			 const Point3d & p3, const Point3d & p4, double h)
+  {
+    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) / 6;
+
+    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;
+
+    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, 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) / 6;  
+
+    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;
+
+
+
+    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)
+      {
+	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;
+	cout << "?";
+      }
+
+    double 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).precision(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).X() << " "
+	 << mesh.Point(i).Y() << " "
+	 << mesh.Point(i).Z() << "\n";
+    
+    of << 2 * mesh.GetNSeg() << endl;
+    for (i = 1; i <= mesh.GetNSeg(); i++)
+      {
+	seg = &mesh.LineSegment(i);
+
+	of << seg->p2 << " " << seg->p1 << " " << 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).X() << " "
+	      << mesh.Point(i).Y() << " "
+	      << mesh.Point(i).Z() << 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).p1 << " "
+	      << mesh2d.LineSegment(i).p2 << "  " << 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).X() << " "
+	      << mesh.Point(i).Y() << " "
+	      << mesh.Point(i).Z() << 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 (ElementIndex 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 i, j, k;
+  
+    mesh.FindOpenElements();
+    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++)
+      {
+	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 0000000000..1baafe40ff
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/meshtool.hpp
@@ -0,0 +1,83 @@
+#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);
+///
+extern double CalcTetBadnessGrad (const Point3d & p1, 
+				const Point3d & p2,
+				const Point3d & p3, 
+				const Point3d & p4, 
+				double h, int pi,
+				Vec3d & grad);
+
+
+/** 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);
+#endif
diff --git a/contrib/Netgen/libsrc/meshing/meshtype.cpp b/contrib/Netgen/libsrc/meshing/meshtype.cpp
new file mode 100644
index 0000000000..e26a0b48ce
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/meshtype.cpp
@@ -0,0 +1,2233 @@
+#include <mystdlib.h>
+
+#include "meshing.hpp"
+
+
+namespace netgen
+{
+
+
+
+  MultiPointGeomInfo :: MultiPointGeomInfo()
+  {
+    cnt = 0;
+  }
+
+  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");
+  }
+  
+
+  void MultiPointGeomInfo :: 
+  Init ()
+  {
+    cnt = 0;
+  }
+
+  void MultiPointGeomInfo :: 
+  DeleteAll ()
+  {
+    cnt = 0;
+  }
+
+
+
+
+  Segment :: Segment() 
+  {
+    p1 = -1;
+    p2 = -1; 
+    edgenr = -1;
+
+    singedge_left = 0;
+    singedge_right = 0;
+    seginfo = 0;
+
+    si = -1;
+
+    domin = -1;
+    domout = -1;
+    tlosurf = -1;
+
+    surfnr1 = -1;
+    surfnr2 = -1;
+    pmid = -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;
+    */
+  }    
+
+
+
+  Segment& Segment::operator=(const Segment & other)
+  {
+    p1 = other.p1;
+    p2 = other.p2;
+    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];
+    pmid = other.pmid;
+    meshdocval = other.meshdocval;
+
+    return *this;
+  }
+
+
+  ostream & operator<<(ostream  & s, const Segment & seg)
+  {
+    s << seg.p1 << "(gi=" << seg.geominfo[0].trignum << ") - "
+      << seg.p2 << "(gi=" << seg.geominfo[1].trignum << ")"
+      << " si = " << seg.si;
+    return s;
+  }
+
+  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;
+    switch (np)
+      {
+      case 3: typ = TRIG; break;
+      case 4: typ = QUAD; break;
+      case 6: typ = TRIG6; break;
+      case 8: typ = QUAD8; break;
+      }
+    order = 1;
+    refflag = 1;
+  } 
+
+  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;
+    order = 1;
+    refflag = 1;
+  } 
+
+
+
+
+  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;
+  deleted = 0;
+  order = 1;
+}
+
+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;
+  deleted = 0;
+  order = 1;
+}
+
+
+/*
+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]));
+}
+
+void Element2d :: Invert2()
+{
+  switch (typ)
+    {
+    case TRIG:
+      {
+	Swap (pnum[1], pnum[2]);
+	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)
+    {
+      PointIndex pi1;
+      if (PNum(1) < PNum(2) && PNum(1) < PNum(3))
+	return;
+      else
+	{
+	  if (PNum(2) < PNum(3))
+	    {
+	      pi1 = PNum(2);
+	      PNum(2) = PNum(3);
+	      PNum(3) = PNum(1);
+	      PNum(1) = pi1;
+	    }
+	  else
+	    {
+	      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();
+  static 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;
+  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.Elem(1) = 1 - p.X() - p.Y();
+      shape.Elem(2) = p.X();
+      shape.Elem(3) = p.Y();
+      break;
+    case QUAD:
+      shape.Elem(1) = (1-p.X()) * (1-p.Y());
+      shape.Elem(2) = p.X() * (1-p.Y());
+      shape.Elem(3) = p.X() * p.Y();
+      shape.Elem(4) = (1-p.X()) * p.Y();
+      break;
+    default:
+      PrintSysError ("Element2d::GetShape, illegal type ", typ);
+    }
+}
+
+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 :: 
+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();
+  static DenseMatrix trans(2,2);
+  static 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();
+  static DenseMatrix trans(2,2), dtrans(2,2);
+  static 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 Vec3d & n) const
+{
+  int i, j;
+  int nip = GetNIP();
+  static DenseMatrix trans(2,2);
+  static DenseMatrix pmat;
+  
+  pmat.SetSize (2, GetNP());
+
+  Vec3d t1, t2;
+  n.GetNormal(t1);
+  Cross (n, t1, t2);
+
+  for (i = 1; i <= nip; i++)
+    {
+      const Point3d & p = points.Get(PNum(i));
+      pmat.Elem(1, i) = p.X() * t1.X() + p.Y() * t1.Y() + p.Z() * t1.Z();
+      pmat.Elem(2, i) = p.X() * t2.X() + p.Y() * t2.Y() + p.Z() * t2.Z();
+    }
+
+  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.X() = hp.X();
+      ipd->p.Y() = hp.Y();
+      ipd->p.Z() = 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.deleted = 0;
+  order = 1;
+  partitionNumber = -1;
+}
+
+
+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.deleted = 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;
+    }
+  order = 1;
+}
+
+
+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.deleted = 0;
+  order = 1;
+}
+
+
+
+
+
+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;
+  order = el2.order;
+  hp_elnr = el2.hp_elnr;
+  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;
+    }
+}
+
+
+
+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
+{
+  Vec3d v1 = points.Get(PNum(2)) - 
+    points.Get(PNum(1));
+  Vec3d v2 = points.Get(PNum(3)) - 
+    points.Get(PNum(1));
+  Vec3d 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 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
+    case 10: // tet
+      {
+	face.SetType(TRIG);
+	for (int j = 1; j <= 3; j++)
+	  face.PNum(j) = PNum(tetfaces[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;
+      }
+    }
+}
+
+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 },
+      { 1, 0, 0 },
+      { 0, 0, 1 },
+      { 1, 0, 1 },
+      { 1, 1, 1 },
+      { 1, 0, 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]));
+}
+
+
+
+
+
+
+
+void Element :: GetNodesLocalNew (ARRAY<Point3d> & 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;
+      }
+    }
+  
+  points.SetSize(0);
+  for (i = 0; i < np; i++)
+    points.Append (Point3d (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 } };
+
+  // just a few:
+  static int tet10trigs[][3] = 
+  { { 2, 8, 9 },
+    { 3, 10, 8},
+    { 4, 9, 10 },
+    { 9, 8, 10 },
+    { 3, 1, 4 },
+    { 1, 2, 4 },
+    { 2, 1, 3 } };
+
+  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 = 7;
+	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(3);
+      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<IntegrationPointData*> ipdtet;
+ARRAY<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, Point3d & 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;
+  switch (typ)
+    {
+    case TET: pp = &eltetqp[0][0]; break;
+    case TET10: pp = &eltet10qp[ip-1][0]; break;
+    }
+
+  p.X() = pp[0];
+  p.Y() = pp[1];
+  p.Z() = pp[2];
+  weight = pp[3];
+}
+
+void Element :: 
+GetTransformation (int ip, const T_POINTS & points,
+		   DenseMatrix & trans) const
+{
+  int np = GetNP();
+  static DenseMatrix pmat(3, np), dshape(3, np);
+  pmat.SetSize (3, np);
+  dshape.SetSize (3, np);
+
+  Point3d 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;
+  switch (GetType())
+    {
+    case TET: dshapep = &ipdtet.Get(ip)->dshape; break;
+    case TET10: dshapep = &ipdtet10.Get(ip)->dshape; break;
+    }
+  
+  CalcABt (pmat, *dshapep, trans);
+}
+
+
+
+void Element :: GetShape (const Point3d & p, Vector & shape) const
+{
+  if (shape.Size() != GetNP())
+    {
+      cerr << "Element::GetShape: Length not fitting" << endl;
+      return;
+    }
+
+  switch (typ)
+    {
+    case TET:
+      {
+	shape.Elem(1) = 1 - p.X() - p.Y() - p.Z(); 
+	shape.Elem(2) = p.X();
+	shape.Elem(3) = p.Y();
+	shape.Elem(4) = 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.Elem(5) = 4 * lam1 * lam2;
+	shape.Elem(6) = 4 * lam1 * lam3;
+	shape.Elem(7) = 4 * lam1 * lam4;
+	shape.Elem(8) = 4 * lam2 * lam3;
+	shape.Elem(9) = 4 * lam2 * lam4;
+	shape.Elem(10) = 4 * lam3 * lam4;
+	
+	shape.Elem(1) = lam1 - 
+	  0.5 * (shape.Elem(5) + shape.Elem(6) + shape.Elem(7));
+	shape.Elem(2) = lam2 - 
+	  0.5 * (shape.Elem(5) + shape.Elem(8) + shape.Elem(9));
+	shape.Elem(3) = lam3 - 
+	  0.5 * (shape.Elem(6) + shape.Elem(8) + shape.Elem(10));
+	shape.Elem(4) = lam4 - 
+	  0.5 * (shape.Elem(7) + shape.Elem(9) + shape.Elem(10));
+	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 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 Point3d & p, DenseMatrix & dshape) const
+{
+  int np = GetNP();
+  if (dshape.Height() != 3 || dshape.Width() != np)
+    {
+      cerr << "Element::DShape: Sizes don't fit" << endl;
+      return;
+    }
+
+  int i, j;
+  double eps = 1e-6;
+  Vector shaper(np), shapel(np);
+
+  for (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 (j = 1; j <= np; j++)
+	dshape.Elem(i, j) = (shaper.Get(j) - shapel.Get(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 = 1; j <= np; j++)
+	      dshape.Elem(j, i) = (shaper.Get(j) - shapel.Get(j)) / (2 * eps);
+	  }
+      }
+    }
+}
+
+void Element :: 
+GetPointMatrix (const T_POINTS & points,
+		DenseMatrix & pmat) const
+{
+  int np = GetNP();
+  /*
+  if (pmat.Width() != np || pmat.Height() != 3)
+    {
+      cerr << "Element::GetPointMatrix: sizes don't fit" << endl;
+      return;
+    }
+  */
+  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 i, j;
+  int nip = GetNIP();
+  static DenseMatrix trans(3,3);
+  static DenseMatrix pmat;
+  
+  pmat.SetSize (3, 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 <= 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, Vec3d & dir, double & dd) const
+{
+  int i, j, k, l;
+  int nip = GetNIP();
+  static DenseMatrix trans(3,3), dtrans(3,3), hmat(3,3);
+  static 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.X(j);
+
+
+
+  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;
+}
+
+
+
+
+
+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));
+    }
+
+  int i;
+  for (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.Append (ipd); break;
+	case TET10: ipdtet10.Append (ipd); break;
+	default:
+	  PrintSysError ("Element::ComputeIntegrationPoint(2), illegal type ", int(typ));
+	}
+    }
+}
+
+
+
+
+
+
+
+
+FaceDescriptor ::  FaceDescriptor()
+{ 
+  surfnr = domin = domout  = bcprop = 0; 
+  domin_singular = domout_singular = 0;
+  tlosurf = -1;
+}
+
+FaceDescriptor :: 
+FaceDescriptor(int surfnri, int domini, int domouti, int tlosurfi)
+{ 
+  surfnr = surfnri; 
+  domin = domini; 
+  domout = domouti;
+  tlosurf = tlosurfi; 
+  bcprop = surfnri;
+  domin_singular = domout_singular = 0;
+}
+
+FaceDescriptor :: FaceDescriptor(const Segment & seg)
+{ 
+  surfnr = seg.si; 
+  domin = seg.domin+1;
+  domout = seg.domout+1;
+  tlosurf = seg.tlosurf+1;
+  bcprop = 0;
+  domin_singular = domout_singular = 0;
+}
+
+int FaceDescriptor ::  SegmentFits (const Segment & seg)
+{
+  return
+    surfnr == seg.si &&
+    domin == seg.domin+1 &&
+    domout == seg.domout+1  &&
+    tlosurf == seg.tlosurf+1;
+}
+
+
+ostream & operator<<(ostream  & s, const FaceDescriptor & fd)
+{
+  s << "surfnr = " << fd.surfnr 
+    << ", domin = " << fd.domin
+    << ", domout = " << fd.domout
+    << ", tlosurf = " << fd.tlosurf
+    << ", bcprop = " << fd.bcprop
+    << ", domin_sing = " << fd.domin_singular
+    << ", domout_sing = " << fd.domout_singular;
+  return s;
+}
+
+
+
+
+
+
+Identifications :: Identifications (Mesh & amesh)
+  : mesh(amesh)
+{
+  identifiedpoints = new INDEX_2_HASHTABLE<int>(100);
+  maxidentnr = 0;
+}
+
+Identifications :: ~Identifications ()
+{
+  delete identifiedpoints;
+}
+
+void Identifications :: Delete ()
+{
+  delete identifiedpoints;
+  identifiedpoints = new INDEX_2_HASHTABLE<int>(100);
+  maxidentnr = 0;
+}
+
+void Identifications :: Add (PointIndex pi1, PointIndex pi2, int identnr)
+{
+  INDEX_2 pair (pi1, pi2);
+  identifiedpoints->Set (pair, identnr);
+  if (identnr > maxidentnr) maxidentnr = identnr;
+  //  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;
+}
+
+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) const
+{
+  identmap.SetSize (mesh.GetNP());
+  identmap = 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);
+	
+	if (nr == identnr || !identnr)
+	  identmap.Elem(i2.I1()) = i2.I2();
+      }  
+}
+ 
+
+void Identifications :: GetPairs (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);
+      }  
+}
+
+
+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);	    
+	  }
+      }
+}
+
+
+
+
+
+MeshingParameters :: MeshingParameters ()
+{
+  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;
+  meshsizefilename = NULL;
+  startinsurface = 0;
+  checkoverlap = 1;
+  checkchartboundary = 1;
+  curvaturesafety = 2;
+  segmentsperedge = 1;
+  parthread = 0;
+
+  elsizeweight = 0.2;
+  giveuptol = 10;
+  maxoutersteps = 5;
+  starshapeclass = 5;
+  baseelnp = 0;
+  sloppy = 1;
+
+  badellimit = 175;
+  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
+      << " 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;
+  checkchartboundary = other.checkchartboundary;
+  curvaturesafety = other.curvaturesafety;
+  segmentsperedge = other.segmentsperedge;
+  parthread = other.parthread;
+  elsizeweight = other.elsizeweight;
+  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 0000000000..363efd3226
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/meshtype.hpp
@@ -0,0 +1,1025 @@
+#ifndef MESHTYPE
+#define MESHTYPE
+
+//#include <algorithm>
+
+/**************************************************************************/
+/* File:   meshtype.hpp                                                   */
+/* Author: Joachim Schoeberl                                              */
+/* Date:   01. Okt. 95                                                    */
+/**************************************************************************/
+
+/*
+    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 };
+
+
+class Mesh; // Added by Christophe for Gmsh (ISO C++ forbids declaration of 'Mesh' with no type)
+
+// class CSGeometry;
+
+extern int GetTimeStamp();
+extern 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);
+}
+
+
+
+#define MULTIPOINTGEOMINFO_MAX 100
+class MultiPointGeomInfo
+{
+  int cnt;
+  PointGeomInfo mgi[MULTIPOINTGEOMINFO_MAX];
+public:
+  MultiPointGeomInfo ();
+  int AddPointGeomInfo (const PointGeomInfo & gi);
+  void Init ();
+  void DeleteAll ();
+
+  int GetNPGI () const { return cnt; }
+  const PointGeomInfo & GetPGI (int i) const { return mgi[i-1]; }
+};
+
+
+class EdgePointGeomInfo
+{
+public:
+  int edgenr;
+  double dist; 
+  double u, v; // for OCC Meshing
+
+  EdgePointGeomInfo ()
+    : edgenr(0), dist(0.0), u(0.0), v(0.0) { ; }
+
+  EdgePointGeomInfo & operator= (const EdgePointGeomInfo & gi2)
+  {
+    edgenr = gi2.edgenr;  dist = gi2.dist;
+    u = gi2.u; v = gi2.v;
+    return *this;
+  }
+};
+
+inline ostream & operator<< (ostream & ost, const EdgePointGeomInfo & gi)
+{
+  return (ost << gi.edgenr);
+}
+
+
+
+
+
+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 hi; }
+  PointIndex operator-- (int) { int hi = i; i--; return 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.
+   will contain pointtype
+ */
+class MeshPoint : public Point3d
+{
+  int layer;
+  bool singular;
+  POINTTYPE type;
+public:
+  MeshPoint () : layer(1), singular(0), type(INNERPOINT) { ; }
+  MeshPoint (const Point3d & ap, int alayer = 1, POINTTYPE apt = INNERPOINT)
+    : Point3d (ap), layer(alayer), singular(0), type(apt) { ; }
+  
+  void SetPoint (const Point3d & ap)
+  { Point3d::operator= (ap); }
+  int GetLayer() const { return layer; }
+
+  bool IsSingular() const { return singular; }
+  void SetSingular(bool s = 1) { singular = s; }
+
+  POINTTYPE Type() const { return type; }
+  void SetType(POINTTYPE at) { type = at; }
+};
+
+
+
+// typedef MoveableArray<MeshPoint> T_POINTS;
+typedef ARRAY<MeshPoint,PointIndex::BASE> T_POINTS;
+
+
+class Element2d;
+ostream & operator<<(ostream  & s, const Element2d & el);
+
+/**
+  Triangle element for surface mesh generation.
+ */
+class Element2d
+{ 
+  /// point numbers
+  PointIndex pnum[ELEMENT2D_MAXPOINTS];
+  /// geom info of corner 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 deleted:1;  // element is deleted
+
+  /// order for hp-FEM
+  unsigned int order:6;
+
+
+public:
+  ///
+  Element2d (int anp = 3);
+  ///
+  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 order; }
+  void SetOrder (int aorder) { order = aorder; }
+
+  ///
+  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;
+  /// matrix 2 * np
+  void GetDShape (const Point2d & p, class DenseMatrix & 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 Vec3d & 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; 
+  }
+
+  void SetRefinementFlag (bool rflag = 1) 
+  { refflag = rflag; }
+  bool TestRefinementFlag () const
+  { return refflag; }
+
+  int HasFace(const Element2d& el) const;
+  ///
+  int meshdocval;
+  ///
+  int hp_elnr;
+};
+
+
+
+
+class IntegrationPointData
+{
+public:
+  Point3d p;
+  double weight;
+  Vector shape;
+  DenseMatrix dshape;
+};
+
+
+
+
+class Element;
+ostream & operator<<(ostream  & s, const Element & el);
+
+
+
+/**
+  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 deleted:1;   // element is deleted, will be removed from array
+  };
+  /// surface or sub-domain index
+  short int index;
+  /// order for hp-FEM
+  unsigned int order:6;
+  /// stored shape-badness of element
+  float badness;
+  /// number of partition for parallel compution 
+  short int partitionNumber;
+  ///
+  
+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: return 4;
+      case TET10: return 4;
+      case PRISM12: return 6;
+      case PRISM: return 6; //SZ 
+      default:
+#ifdef DEBUG
+	PrintSysError ("Element3d::GetNV not implemented for typ ", typ)
+#endif
+	  ;
+      }
+    return np;
+  }
+  // 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 order; }
+  void SetOrder (int aorder) { order = aorder; }
+
+  ///
+  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<Point3d> & points) const;
+  void GetNodesLocalNew (ARRAY<Point3d> & 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, Point3d & 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 Point3d & p, class Vector & shape) const;
+  void GetShapeNew (const Point<3> & p, class FlatVector & shape) const;
+  /// matrix 2 * np
+  void GetDShape (const Point3d & 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, Vec3d & dir, double & dd) const;
+
+  ///
+  friend ostream & operator<<(ostream  & s, const Element & el);
+
+  void SetRefinementFlag (bool rflag = 1) 
+  { flags.refflag = rflag; }
+  int TestRefinementFlag () const
+  { return flags.refflag; }
+
+  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; 
+  }
+
+  int GetPartition () const { return partitionNumber; }
+  void SetPartition (int nr) { partitionNumber = nr; }; 
+
+  int hp_elnr;
+};
+
+
+class Segment;
+ostream & operator<<(ostream  & s, const Segment & seg);
+
+
+/**
+  Edge segment.
+  */
+class Segment
+{
+public:
+  ///
+  Segment();
+
+  friend ostream & operator<<(ostream  & s, const Segment & seg);
+
+  /// point index 1
+  PointIndex p1;
+  /// point index 2
+  PointIndex p2;    
+  /// edge nr
+  int edgenr;
+  ///
+  unsigned int singedge_left:1;
+  unsigned int singedge_right:1;
+
+  /// 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;
+
+
+  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;
+};
+
+
+// class Surface;  
+class FaceDescriptor;
+ostream & operator<< (ostream  & s, const FaceDescriptor & fd);
+
+///
+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;
+
+public:
+  FaceDescriptor();
+  FaceDescriptor(int surfnri, int domini, int domouti, int tlosurfi);
+  FaceDescriptor(const Segment & seg);
+
+  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; }
+  void SetSurfNr (int sn) { surfnr = sn; }
+  void SetDomainIn (int di) { domin = di; }
+  void SetDomainOut (int dom) { domout = dom; }
+  void SetBCProperty (int bc) { bcprop = bc; }
+
+  friend ostream & operator<<(ostream  & s, const FaceDescriptor & fd);
+
+
+  ///
+  bool domin_singular;
+  bool domout_singular;
+
+
+};
+
+ 
+
+
+
+
+class 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
+  */
+  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
+  **/
+  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;
+  /// file for meshsize
+  const char * meshsizefilename;
+  /// start surfacemeshing from everywhere in surface
+  int startinsurface;
+  /// check overlapping surfaces (debug)
+  int checkoverlap;
+  /// 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
+  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;
+
+  ///
+  int secondorder;
+  /// high order element curvature
+  int elementorder;
+  /// quad-dominated surface meshing
+  int quad;
+  ///
+  int inverttets;
+  ///
+  int inverttrigs;
+  ///
+  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
+{
+private:
+  Mesh & mesh;
+
+  /// identify points (thin layers, periodic b.c.)  
+  INDEX_2_HASHTABLE<int> * identifiedpoints;
+
+  /// number of identifications (or, actually used identifications ?)
+  int maxidentnr;
+
+public:
+  ///
+  Identifications (Mesh & amesh);
+  ///
+  ~Identifications ();
+
+  void Delete ();
+
+  /*
+    Identify points pi1 and pi2, due to
+    identification nr identnr
+  */
+  void Add (PointIndex pi1, PointIndex pi2, int identnr);
+
+
+  int Get (PointIndex pi1, PointIndex pi2) const;
+  int GetSymmetric (PointIndex pi1, PointIndex pi2) 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) const;
+  ///
+  void GetPairs (int identnr, ARRAY<INDEX_2> & identpairs) const;
+  ///
+  int GetMaxNr () const { return maxidentnr; }  
+
+  /// remove secondorder
+  void SetMaxPointNr (int maxpnum);
+};
+
+
+
+
+
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/meshing/msghandler.cpp b/contrib/Netgen/libsrc/meshing/msghandler.cpp
new file mode 100644
index 0000000000..04da502a73
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/msghandler.cpp
@@ -0,0 +1,193 @@
+//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 MyStr msgstatus = "";
+
+
+
+
+void ResetStatus()
+{
+  SetStatMsg("idle");
+
+  for (int i = 0; i < msgstatus_stack.Size(); i++)
+    delete msgstatus_stack[i];
+  msgstatus_stack.SetSize(0);
+
+  // multithread.task = "";
+  multithread.percent = 100.;
+}
+
+void PushStatus(const MyStr& s)
+{
+  msgstatus_stack.Append(new MyStr (s));
+  SetStatMsg(s);
+}
+
+void PushStatusF(const MyStr& s)
+{
+  msgstatus_stack.Append(new MyStr (s));
+  SetStatMsg(s);
+  PrintFnStart(s);
+}
+
+void PopStatus()
+{
+  SetThreadPercent(100.);
+  if (msgstatus_stack.Size())
+    {
+      if (msgstatus_stack.Size() > 1)
+	SetStatMsg (*msgstatus_stack.Last());
+      else
+	SetStatMsg ("");
+      delete msgstatus_stack.Last();
+      msgstatus_stack.SetSize(msgstatus_stack.Size()-1);
+    }
+  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;
+}
+
+
+
+#ifdef SMALLLIB
+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 0000000000..7de9425193
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/msghandler.hpp
@@ -0,0 +1,52 @@
+#ifndef FILE_MSGHANDLER
+#define FILE_MSGHANDLER
+
+/**************************************************************************/
+/* File:   msghandler.hh                                                  */
+/* Author: Johannes Gerstmayr                                             */
+/* Date:   20. Nov. 99                                                    */
+/**************************************************************************/
+
+
+extern void PrintDot(char ch = '.');
+
+
+//Message Pipeline:
+
+//importance: importance of message: 1=very important, 3=middle, 5=low, 7=unimportant
+extern void PrintMessage(int importance, 
+			 const MyStr& s1, const MyStr& s2=MyStr());
+extern void PrintMessage(int importance, 
+			 const MyStr& s1, const MyStr& s2, const MyStr& s3, const MyStr& s4=MyStr());
+extern 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 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 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 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 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 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 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 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 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 void SetStatMsg(const MyStr& s);
+
+extern void PushStatus(const MyStr& s);
+extern void PushStatusF(const MyStr& s);
+extern void PopStatus();
+extern void SetThreadPercent(double percent);
+
+
+#endif
+
diff --git a/contrib/Netgen/libsrc/meshing/netrule2.cpp b/contrib/Netgen/libsrc/meshing/netrule2.cpp
new file mode 100644
index 0000000000..c75c69d521
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/netrule2.cpp
@@ -0,0 +1,226 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+namespace netgen
+{
+
+netrule :: netrule ()
+{
+  name = new char[1];
+  name[0] = char(0);
+  quality = 0;
+}
+
+netrule ::  ~netrule()
+{
+  if(name != NULL) delete [] name;
+  for(int i=0; i<oldutofreearea_i.Size(); i++)
+    delete oldutofreearea_i[i];
+}
+
+
+/*
+void netrule :: GetFreeArea (ARRAY<Point2d> & afreearea)
+  {
+  int i;
+
+  afreearea.SetSize (freearea.Size());
+  for (i = 1; i <= freearea.Size(); i++)
+    afreearea[i] = freearea[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);
+  FlatVector devfree1(vs, mem2);
+  FlatVector devfree2(vs, mem3);
+
+  if (tolclass <= oldutofreearea_i.Size())
+    {
+      oldutofreearea_i[tolclass-1] -> Mult (devp, devfree);
+    }
+  else
+    {
+      oldutofreearea.Mult (devp, devfree1);
+      oldutofreearealimit.Mult (devp, devfree2);
+      devfree.Set2 (lam1, devfree1, lam2, devfree2);
+    }
+
+  
+  int fzs = freezone.Size();
+  transfreezone.SetSize (fzs);
+
+  if (fzs > 0)
+    {
+      transfreezone[0].X() = lam1 * freezone[0].X() + lam2 * freezonelimit[0].X() + devfree[0];
+      transfreezone[0].Y() = lam1 * freezone[0].Y() + lam2 * freezonelimit[0].Y() + devfree[1];
+      fzmaxx = fzminx = transfreezone[0].X();
+      fzmaxy = fzminy = transfreezone[0].Y();
+    }
+
+  for (int i = 1; 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 (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);    // should not be necessary
+
+	  freesetinequ(i,0) = vn.X(); 
+	  freesetinequ(i,1) = vn.Y(); 
+	  freesetinequ(i,2) = -(p1.X() * vn.X() + p1.Y() * vn.Y());
+	}
+
+      /*
+      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 i;
+  int left, right, allleft, allright;
+  double nx, ny, nl, c;
+
+  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 (i = 1; i <= transfreezone.Size(); i++)
+    {
+      if (freesetinequ.Get(i, 1) * p1.X() + freesetinequ.Get(i, 2) * p1.Y() +
+	  freesetinequ.Get(i, 3) > -1e-6 &&
+	  freesetinequ.Get(i, 1) * p2.X() + freesetinequ.Get(i, 2) * p2.Y() +
+	  freesetinequ.Get(i, 3) > -1e-6
+	  ) return 0;
+    }
+
+  nx =  (p2.Y() - p1.Y());
+  ny = -(p2.X() - p1.X());
+  nl = sqrt (nx * nx + ny * ny);
+  if (nl > 1e-8)
+    {
+      nx /= nl;
+      ny /= nl;
+      c = - (p1.X() * nx + p1.Y() * ny);
+
+      allleft = 1;
+      allright = 1;
+
+      for (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++)
+    {
+      if (! CCW (transfreezone.Get(i), 
+		 transfreezone.Get(i % n + 1),
+		 transfreezone.Get( (i+1) % n + 1 ) ) )
+	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 * tf = &linetolerances.Get(li);
+  return tf->f1 * dx * dx + tf->f2 * dx * dy + tf->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 0000000000..fe6a7417c1
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/netrule3.cpp
@@ -0,0 +1,1138 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+// #define MARK
+// #include <prof.h>
+
+
+namespace netgen
+{
+
+
+vnetrule :: vnetrule ()
+{
+  name = "";
+  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.Elem(j) = allp.Get(i+3*j-3);
+
+      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.Elem(j);
+    }
+
+  // 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;
+
+
+  static ARRAY<int> 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;
+  static 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 lam1 = rs1 * vii22 - rs2 * vii12;
+	      double lam2 = rs2 * vii11 - rs1 * vii12;
+
+	      if (fabs (lam1) > fabs (lam2))
+		{
+		  if (lam1 < 0)
+		    an *= -1;
+		}
+	      else
+		{
+		  if (lam2 < 0)
+		    an *= -1;
+		}
+
+
+	      if (lam1 * lam2 < 0 && 0)
+		{
+		  if (fabs (lam1) > 1e-14 && fabs (lam2) > 1e-14)
+		    {
+		      //		      (*mycout) << "lam1 lam2 < 0" << endl;
+		      (*testout) << "lami 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) << "lami = " << lam1 << ", " << lam2 << 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 lam1 = rs1 * vii22 - rs2 * vii12;
+	      double lam2 = rs2 * vii11 - rs1 * vii12;
+
+	      //	      (*testout) << "lam1, lam2 = " << lam1 << ", " << lam2 << endl;
+
+
+	      if (fabs (lam1) > fabs (lam2))
+		{
+		  if (lam1 < 0)
+		    an *= -1;
+		}
+	      else
+		{
+		  if (lam2 < 0)
+		    an *= -1;
+		}
+
+
+	      if (lam1 * lam2 < 0)
+		{
+		  if (fabs (lam1) > 1e-14 && fabs (lam2) > 1e-14)
+		    {
+		      //		      (*mycout) << "lam1 lam2 < 0" << endl;
+		      (*testout) << "lami 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) << "lami = " << lam1 << ", " << lam2 << 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;
+
+
+  static ARRAY<int> 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;
+    }
+
+  static ARRAY<int> 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/parser2.cpp b/contrib/Netgen/libsrc/meshing/parser2.cpp
new file mode 100644
index 0000000000..48ef280eb4
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/parser2.cpp
@@ -0,0 +1,559 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+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 == ',')
+	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);
+  //  (*mycout) << "Rule " << name << " found." << endl;
+
+  do
+    {
+      ist >> buf;
+
+      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;    // ')'
+
+	      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;
+
+	      ist >> ch;
+	      while (ch != ';')
+		{
+		  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'
+		    }
+
+		  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;    // ')'
+
+	      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 == ',')
+		{
+		  ist >> elements.Last().PNum(2);
+		  ist >> ch;    // ','
+		}
+	      if (ch == ',')
+		{
+		  ist >> elements.Last().PNum(3);
+		  ist >> ch;    // ','
+		}
+	      if (ch == ',')
+		{
+		  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);
+  for (i = 0; i < oldutofreearea_i.Size(); i++)
+    {
+      oldutofreearea_i[i] = new DenseMatrix (oldutofreearea.Height(), oldutofreearea.Width());
+      DenseMatrix & mati = *oldutofreearea_i[i];
+      for (int j = 0; j < oldutofreearea.Height(); j++)
+	for (int k = 0; k < oldutofreearea.Width(); k++)
+	  mati(j,k) = 1.0 / (i+1) * oldutofreearea(j,k) + (1 - 1.0/(i+1)) * oldutofreearealimit(j,k);
+    }
+}
+
+
+
+
+extern const char * triarules[];
+extern const char * quadrules[];
+
+void Meshing2 :: LoadRules (const char * filename)
+{
+  char buf[256];
+  istream * ist;
+  char *tr1 = NULL;
+
+  /*
+  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)
+	{
+	  hcp = triarules;
+	  PrintMessage (3, "load internal triangle rules");
+	}
+      else
+	{
+	  hcp = quadrules;
+	  PrintMessage (3, "load internal quad rules");
+	  // LoadRules ("rules/quad.rls");
+	}
+
+      int len = 0;
+      while (*hcp)
+	{
+	  len += strlen (*hcp);
+	  hcp++;
+	}
+      tr1 = new char[len+1];
+      tr1[0] = 0;
+
+
+      if (!mparam.quad)
+	hcp = triarules;
+      else
+	hcp = quadrules;
+
+
+      char * tt1 = tr1;
+      while (*hcp)
+	{
+	  strcat (tt1, *hcp);
+	  tt1 += strlen (*hcp);
+	  hcp++;
+	}
+
+      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)
+	{
+	  netrule * rule = new netrule;
+	  rule -> LoadRule(*ist);
+	  rules.Append (rule);
+	}
+    }
+
+  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 0000000000..23ee84802e
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/parser3.cpp
@@ -0,0 +1,987 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+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 == ',')
+	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);
+
+  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 == ',')
+		{
+		  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 == ',')
+		{
+		  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 i, j, k, nfp;
+	  Point3d p;
+
+	  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 i, j, k, nfp;
+	  Point3d p;
+
+	  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 == ',')
+		{
+		  //		  elements.Last().SetNP(2);
+		  ist >> elements.Last().PNum(2);
+		  ist >> ch;    // ','
+		}
+	      if (ch == ',')
+		{
+		  //		  elements.Last().SetNP(3);
+		  ist >> elements.Last().PNum(3);
+		  ist >> ch;    // ','
+		}
+	      if (ch == ',')
+		{
+		  //		  elements.Last().SetNP(4);
+		  elements.Last().SetType(TET);
+		  ist >> elements.Last().PNum(4);
+		  ist >> ch;    // ','
+		}
+	      if (ch == ',')
+		{
+		  //		  elements.Last().SetNP(5);
+		  elements.Last().SetType(PYRAMID);
+		  ist >> elements.Last().PNum(5);
+		  ist >> ch;    // ','
+		}
+	      if (ch == ',')
+		{
+		  //		  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 (i = 1; i <= 3; i++)
+	{
+	  for (j = 1; j <= points.Size(); j++)
+	    vp.Elem(j) = points.Get(j).X(i);
+	  oldutofreezone->Mult(vp, vfp);
+	  for (j = 1; j <= freezone.Size(); j++)
+	    freezone.Elem(j).X(i) = vfp.Get(j);
+	}
+      //      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));
+    }
+
+
+  {
+    char ok;
+    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 <= elements.Size(); i++)
+	  if (elements.Get(i).GetNP() == 6)
+	    {
+	      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);
+
+    int 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));
+      }
+  }
+
+  
+  //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 edge(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))
+			{
+			  edge.I(1) = f1.I(f11);
+			  edge.I(2) = f1.I(f12);
+			}
+	      //	      (*testout) << "edge = " << edge.I(1) << "-" << edge.I(2) << endl;
+	      //	      (*testout) << "ind = " << ind << " edge = " << edge << endl;
+	      for (int eli = 1; eli <= GetNOldF(); eli++)
+		{
+		  if (GetNP(eli) == 4)
+		    {
+		      for (int elr = 1; elr <= 4; elr++)
+			{
+			  if (GetPointNrMod (eli, elr) == edge.I(1) &&
+			      GetPointNrMod (eli, elr+2) == edge.I(2))
+			    {
+			      /*
+			      (*testout) << "edge is diagonal of rectangle" << endl;
+			      (*testout) << "edge = " << edge.I(1) << "-" << edge.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; 
+      int 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++;
+	}
+
+      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 0000000000..7e696554c0
--- /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 0000000000..baf0bef388
--- /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 0000000000..a97e7f13e5
--- /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 0000000000..d4e997c1fe
--- /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 0000000000..57fb010144
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/quadrls.cpp
@@ -0,0 +1,765 @@
+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 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",\
+"\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",\
+"\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 0000000000..f7578d3394
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/refine.cpp
@@ -0,0 +1,721 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+
+namespace netgen
+{
+  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.p1, el.p2);
+	PointIndex pinew;
+	EdgePointGeomInfo ngi;
+
+	if (between.Used(i2))
+	  {
+	    pinew = between.Get(i2);
+	    ngi = epgi[pinew]; 
+	  }
+	else
+	  {
+	    Point3d pnew;
+
+	    PointBetween (mesh.Point (el.p1),
+			  mesh.Point (el.p2), 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.p2 = pinew;
+	ns1.epgeominfo[1] = ngi;
+	ns2.p1 = 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();
+
+		  Point3d 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 },
+		  { 1, 3, 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
+		    {
+		      Point3d 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 int reverse[8] =
+	   {
+	      0, 0, 0, 0, 0, 1, 0, 1
+	   };
+
+	   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 = 1 - 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 },
+	       };
+
+
+	     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 },
+	       };
+
+	     static int fbetw[6][3] =
+	     { { 1, 6, 16 },
+	       { 3, 4, 16 },
+	       { 1, 5, 17 },
+               { 2, 4, 17 },
+	       { 2, 6, 18 },
+	       { 3, 5, 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();
+    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<Point3d> should(np);
+	ARRAY<Point3d> 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 = 1; j <= 3; j++)
+			mesh.Point(i).X(j) = 
+			  lam * should.Get(i).X(j) +
+			  (1-lam) * can.Get(i).X(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);
+		mesh.ImproveMesh (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 0000000000..82c6dbe57d
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/ruler2.cpp
@@ -0,0 +1,646 @@
+#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> & llines,
+			   int maxlegalline,
+			   ARRAY<Element2d> & elements,
+			   ARRAY<INDEX> & dellines, int tolerance)
+{
+  int i, j, ri, nlok, npok, incnpok, refpi, locli = 0;
+
+  double maxerr = 0.5 + 0.3 * tolerance;
+  double minelerr = 2 + 0.5 * tolerance * tolerance;
+
+
+  bool ok;
+  int found;   // rule number
+  Vector oldu, newu;
+  Point2d np;
+  Vec2d linevec;
+  int oldnp;
+  INDEX_2 loclin;
+  double hf, elerr;
+  int noldlp, noldll;
+  int loctestmode;
+
+  static ARRAY<int> pused, lused;
+  static ARRAY<int> pmap, lmap, pfixed;
+  static ARRAY<int> pnearness, lnearness;
+  
+  static ARRAY<Point2d> tempnewpoints;
+  static ARRAY<INDEX_2> tempnewlines;
+  static ARRAY<int> tempdellines;
+  static ARRAY<Element2d> tempelements;
+
+
+
+  elements.SetSize (0);
+  dellines.SetSize (0);
+
+  noldlp = lpoints.Size();
+  noldll = llines.Size();
+
+  pused.SetSize (maxlegalpoint);
+  lused.SetSize (maxlegalline);
+  pnearness.SetSize (noldlp);
+  lnearness.SetSize (llines.Size());
+
+
+  testmode = debugparam.debugoutput;
+  loctestmode = testmode;
+
+  if (loctestmode)
+    {
+      (*testout) << endl << endl << "Check new environment" << endl;
+      (*testout) << "tolerance = " << tolerance << endl;
+      for (i = 1; i <= lpoints.Size(); i++)
+	(*testout) << "P" << i << " = " << lpoints.Get(i) << endl;
+      (*testout) << endl;
+      for (i = 1; i <= llines.Size(); i++)
+	(*testout) << "(" << llines.Get(i).I1() << "-" << llines.Get(i).I2() << ")" << endl;
+    }
+
+  // check every rule
+
+  found = 0;
+
+  
+  for (i = 1; i <= noldlp; i++)
+    pnearness.Set(i, 1000);
+  
+  for (j = 1; j <= 2; j++)
+    pnearness.Set(llines.Get(1).I(j), 0);
+    
+
+  do
+    {
+      ok = 1;
+      for (i = 1; i <= maxlegalline; i++)
+	{
+	  const INDEX_2 & hline = llines.Get(i);
+
+	  /*
+	  int minn = INT_MAX-1;
+	  for (j = 1; j <= 2; j++)
+	    {
+	      int hi = pnearness.Get(hline.I(j));
+	      if (hi < minn) minn = hi;
+	    }
+	  */
+	  int minn = pnearness.Get(hline.I1());
+	  int minn2 = pnearness.Get(hline.I2());
+	  if (minn2 < minn)
+	    minn = minn2;
+
+	  /*
+	  for (j = 1; j <= 2; j++)
+	    {
+	      int hpi = hline.I(j);
+	      if (pnearness.Get(hpi) > minn+1)
+		{
+		  ok = 0;
+		  pnearness.Set(hpi, minn+1);
+		}
+	    }
+	  */
+	  int hpi = hline.I1();
+	  if (pnearness.Get(hpi) > minn+1)
+	    {
+	      ok = 0;
+	      pnearness.Set(hpi, minn+1);
+	    }
+	  hpi = hline.I2();
+	  if (pnearness.Get(hpi) > minn+1)
+	    {
+	      ok = 0;
+	      pnearness.Set(hpi, minn+1);
+	    }
+	}
+    }
+  while (!ok);
+  
+  for (i = 1; i <= maxlegalline /* lnearness.Size() */; i++)
+    {
+      lnearness.Set(i, 0);
+      for (j = 1; j <= 2; j++)
+	lnearness.Elem(i) += pnearness.Get(llines.Get(i).I(j));
+    }
+
+
+  for (ri = 1; ri <= rules.Size(); ri++)
+    {
+      netrule * rule = rules.Get(ri);
+
+      if (loctestmode)
+	(*testout) << "Rule " << rule->Name() << endl;
+
+      if (rule->GetQuality() > tolerance) continue;
+
+      pmap.SetSize (rule->GetNP());
+      lmap.SetSize (rule->GetNL());
+      
+      lused = 0;
+      pused = 0;
+      pmap = 0;
+      lmap = 0;
+      /*
+      for (i = 1; i <= lused.Size(); i++)
+	lused.Set (i, 0);
+      for (i = 1; i <= pused.Size(); i++)
+	pused.Set (i, 0);
+      for (i = 1; i <= pmap.Size(); i++)
+	pmap.Set(i, 0);
+      for (i = 1; i <= lmap.Size(); i++)
+	lmap.Set(i, 0);
+      */
+
+      lused[0] = 1;   // .Set (1, 1);
+      lmap[0] = 1;    // .Set (1, 1);
+
+      for (j = 1; j <= 2; j++)
+	{
+	  pmap.Elem(rule->GetPointNr (1, j)) = llines.Get(1).I(j);
+	  pused.Elem(llines.Get(1).I(j))++;
+	}
+
+
+      nlok = 2;
+
+      while (nlok >= 2)
+	{
+
+	  if (nlok <= rule->GetNOldL())
+
+	    {
+	      ok = 0;
+	      while (!ok && lmap.Get(nlok) < maxlegalline  /* llines.Size() */)
+		{
+		  lmap.Elem(nlok)++;
+		  locli = lmap.Get(nlok);
+
+		  if (!lused.Get(locli) && 
+		      lnearness.Get(locli) <= rule->GetLNearness (nlok) )
+		    {
+		      ok = 1;
+
+		      loclin = llines.Get(locli);
+
+		      linevec.X() = lpoints.Get (loclin.I2()).X() -
+			lpoints.Get (loclin.I1()).X();
+		      linevec.Y() = lpoints.Get (loclin.I2()).Y() -
+			lpoints.Get (loclin.I1()).Y();
+
+		      if (rule->CalcLineError (nlok, linevec) > maxerr)
+			  ok = 0;
+
+		      for (j = 1; j <= 2 && ok; j++)
+			{
+			  refpi = rule->GetPointNr (nlok, j);
+
+			  if (pmap.Get(refpi) != 0)
+			    {
+			      if (pmap.Get(refpi) != loclin.I(j))
+				ok = 0;
+			    }
+			  else
+			    {
+			      if (rule->CalcPointDist (refpi, lpoints.Get(loclin.I(j))) > maxerr
+				  || !legalpoints.Get(loclin.I(j))
+				  || pused.Get(loclin.I(j))) ok = 0;
+			    }
+			}
+		    }
+		}
+
+	      if (ok)
+		{
+		  lused.Set (locli, 1);
+		  for (j = 1; j <= 2; j++)
+		    {
+		      pmap.Set(rule->GetPointNr (nlok, j), loclin.I(j));
+		      pused.Elem(loclin.I(j))++;
+		    }
+
+		  nlok++;
+		}
+	      else
+		{
+		  lmap.Elem(nlok) = 0;
+		  nlok--;
+
+		  lused.Set (lmap.Get(nlok), 0);
+		  for (j = 1; j <= 2; j++)
+		    {
+		      pused.Elem(llines.Get(lmap.Get(nlok)).I(j)) --;
+		      if (! pused.Get (llines.Get (lmap.Get (nlok)).I(j)))
+			pmap.Set (rule->GetPointNr (nlok, j), 0);
+		    }
+		}
+	    }
+
+	  else
+
+	    {
+
+	      // all lines are mapped !!
+
+	      // map also all points:
+
+	      npok = 1;
+	      incnpok = 1;
+
+	      pfixed.SetSize (pmap.Size());
+	      for (i = 1; i <= pmap.Size(); i++)
+		pfixed.Elem(i) = (pmap.Get(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
+
+		    {
+		      if (ok)
+			foundmap.Elem(ri)++; 
+
+		      if (loctestmode)
+			(*testout) << "lines and points mapped" << endl;
+
+
+		      ok = 1;
+
+		      // 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)
+			{
+			  oldu.SetSize (2 * rule->GetNOldP());
+			  
+			  for (i = 1; i <= rule->GetNOldP(); i++)
+			    {
+			      Vec2d ui(rule->GetPoint(i), lpoints.Get(pmap.Get(i)));
+			      oldu.Set (2*i-1, ui.X());
+			      oldu.Set (2*i  , ui.Y());
+			    }
+			  
+			  rule -> SetFreeZoneTransformation (oldu, tolerance);
+			}
+		      
+
+		      if (ok && !rule->ConvexFreeZone())
+			{
+			  ok = 0;
+			  if (loctestmode) (*testout) << "freezone not convex" << endl;
+
+			  /*
+			  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:
+
+		      for (i = 1; i <= maxlegalpoint && ok; i++)
+			{
+			  if ( !pused.Get(i) &&
+			       rule->IsInFreeZone (lpoints.Get(i)) )
+			    {
+			      ok = 0;
+			      if (loctestmode)
+				(*testout) << "Point " << i << " in freezone" << endl;
+			    }
+			}
+
+
+		      for (i = maxlegalpoint+1; i <= lpoints.Size() && ok; i++)
+			{
+			  if ( rule->IsInFreeZone (lpoints.Get(i)) )
+			    {
+			      ok = 0;
+			      if (loctestmode)
+				(*testout) << "Point " << i << " in freezone" << endl;
+			    }
+			}
+
+		      for (i = 1; i <= maxlegalline && ok; i++)
+			{
+			  if (!lused.Get(i) && 
+			      rule->IsLineInFreeZone (lpoints.Get(llines.Get(i).I1()),
+						      lpoints.Get(llines.Get(i).I2())))
+			    {
+			      ok = 0;
+			      if (loctestmode)
+				(*testout) << "line " << llines.Get(i).I1() << "-"
+					   << llines.Get(i).I2() << " in freezone" << endl;
+			    }
+			}
+		      for (i = maxlegalline+1; i <= llines.Size() && ok; i++)
+			{
+			  if (rule->IsLineInFreeZone (lpoints.Get(llines.Get(i).I1()),
+						      lpoints.Get(llines.Get(i).I2())))
+			    {
+			      ok = 0;
+			      if (loctestmode)
+				(*testout) << "line " << llines.Get(i).I1() << "-"
+					   << llines.Get(i).I2() << " in freezone" << endl;
+			    }
+			}
+
+
+		      /*
+		      // 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)
+			{
+			  if (loctestmode)
+			    (*testout) << "rule ok" << endl;
+
+			  //			  newu = rule->GetOldUToNewU() * oldu;
+			  if (rule->GetNOldP() < rule->GetNP())
+			    {
+			      newu.SetSize (rule->GetOldUToNewU().Height());
+			      rule->GetOldUToNewU().Mult (oldu, newu);
+			    }
+
+			  // Setze neue Punkte:
+
+			  oldnp = rule->GetNOldP();
+
+			  for (i = oldnp + 1; i <= rule->GetNP(); i++)
+			    {
+			      np = rule->GetPoint(i);
+			      np.X() += newu.Elem (2 * (i-oldnp) - 1);
+			      np.Y() += newu.Elem (2 * (i-oldnp));
+
+			      pmap.Elem(i) = lpoints.Append (np);
+			    }
+
+			  // Setze neue Linien:
+
+			  for (i = rule->GetNOldL() + 1; i <= rule->GetNL(); i++)
+			    {
+			      llines.Append (INDEX_2 (pmap.Get(rule->GetPointNr (i, 1)),
+						      pmap.Get(rule->GetPointNr (i, 2))));
+			    }
+
+
+			  // delete old lines:
+
+			  for (i = 1; i <= rule->GetNDelL(); i++)
+			    dellines.Append (lmap.Get(rule->GetDelLine(i)));
+
+			  // insert new elements:
+
+			  for (i = 1; i <= rule->GetNE(); i++)
+			    {
+			      elements.Append (rule->GetElement(i));
+			      for (j = 1; j <= elements.Get(i).GetNP(); j++)
+				elements.Elem(i).PNum(j) = pmap.Get(elements.Get(i).PNum(j));
+			    }
+
+
+			  elerr = 0;
+			  for (i = 1; i <= elements.Size(); i++)
+			    {
+			      if (!mparam.quad)
+				hf = CalcElementBadness (lpoints, elements.Get(i));
+			      else
+				hf = elements.Get(i).CalcJacobianBadness (lpoints) * 5;
+			      if (loctestmode)
+				(*testout) << "r " << rule->Name() << "bad = " << hf << endl;
+			      if (hf > elerr) elerr = hf;
+			    }
+
+			  if (loctestmode)
+			    (*testout) << "error = " << elerr;
+
+
+			  canuse.Elem(ri) ++;
+
+			  if (elerr < minelerr)
+			    {
+
+			      if (loctestmode)
+				{
+				  (*testout) << "rule = " << rule->Name() << endl;
+				  (*testout) << "class = " << tolerance << endl;
+				  (*testout) << "lpoints: " << endl;
+				  for (i = 1; i <= lpoints.Size(); i++)
+				    (*testout) << lpoints.Get(i) << endl;
+				  (*testout) << "llines: " << endl;
+				  for (i = 1; i <= llines.Size(); i++)
+				    (*testout) << llines.Get(i).I1() << " " << llines.Get(i).I2() << endl;
+
+				  (*testout) << "Freezone: ";
+				  for (i = 1; i <= rule -> GetTransFreeZone().Size(); i++)
+				    (*testout) << rule->GetTransFreeZone().Get(i) << endl;
+				}
+
+
+
+			      minelerr = elerr;
+			      found = ri;
+
+			      tempnewpoints.SetSize (0);
+			      for (i = noldlp+1; i <= lpoints.Size(); i++)
+				tempnewpoints.Append (lpoints.Get(i));
+
+			      tempnewlines.SetSize (0);
+			      for (i = noldll+1; i <= llines.Size(); i++)
+				tempnewlines.Append (llines.Get(i));
+
+			      tempdellines.SetSize (0);
+			      for (i = 1; i <= dellines.Size(); i++)
+				tempdellines.Append (dellines.Get(i));
+
+			      tempelements.SetSize (0);
+			      for (i = 1; i <= elements.Size(); i++)
+				tempelements.Append (elements.Get(i));
+			    }
+
+			  lpoints.SetSize (noldlp);
+			  llines.SetSize (noldll);
+			  dellines.SetSize (0);
+			  elements.SetSize (0);
+			  ok = 0;
+
+			}
+
+
+		      npok = rule->GetNOldP();
+		      incnpok = 0;
+		    }
+		}
+
+	      nlok = rule->GetNOldL();
+
+	      lused.Set (lmap.Get(nlok), 0);
+
+	      for (j = 1; j <= 2; j++)
+		{
+		  refpi = rule->GetPointNr (nlok, j);
+		  pused.Elem(pmap.Get(refpi))--;
+
+		  if (pused.Get(pmap.Get(refpi)) == 0)
+		    {
+		      pmap.Set(refpi, 0);
+		    }
+		}
+	    }
+	}
+    }
+
+
+  if (found)
+    {
+      for (i = 0; i < tempnewpoints.Size(); i++)
+	lpoints.Append (tempnewpoints[i]);
+      for (i = 0; i < tempnewlines.Size(); i++)
+	llines.Append (tempnewlines[i]);
+      for (i = 0; i < tempdellines.Size(); i++)
+	dellines.Append (tempdellines[i]);
+      for (i = 0; i < tempelements.Size(); i++)
+	elements.Append (tempelements[i]);
+    }
+
+
+  return found;
+}
+
+
+
+
+
+}
diff --git a/contrib/Netgen/libsrc/meshing/ruler2.hpp b/contrib/Netgen/libsrc/meshing/ruler2.hpp
new file mode 100644
index 0000000000..4ef855d4ec
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/ruler2.hpp
@@ -0,0 +1,166 @@
+#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<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); }
+
+  ///
+  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 * tf = &tolerances.Get(pi);
+    return tf->f1 * dx * dx + tf->f2 * dx * dy + tf->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 0000000000..48ba5bc465
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/ruler3.cpp
@@ -0,0 +1,1176 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+// #define MARK
+// #include <prof.h>
+
+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: 1 .. it is allowed to use pointi
+ ARRAY<Element2d> & 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 
+ )
+
+{
+  int i, j, k, ri, nfok, npok, incnpok, refpi, locpi, locfi, locfr;
+  int hi, minn, hpi;
+  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 Element2d * locface = NULL;
+  int loktestmode;
+  
+  static ARRAY<int> pused;       // point is already mapped
+  static ARRAY<int> fused;       // face is already mapped
+  static ARRAY<int> pmap;        // map of reference point to local point
+  static ARRAY<int> pfixed;      // ???
+  static ARRAY<int> fmapi;       // face in reference is mapped to face nr ...
+  static ARRAY<int> fmapr;       // face in reference is rotated to map 
+  static ARRAY<Point3d> transfreezone;  // transformed free-zone
+  static INDEX cnt = 0;
+  INDEX_2_HASHTABLE<int> ledges(lfaces.Size());  // edges in local environment
+  
+  static ARRAY<Point3d> tempnewpoints;
+  static ARRAY<Element2d> tempnewfaces;
+  static ARRAY<int> tempdelfaces;
+  static ARRAY<Element> tempelements;
+  static ARRAY<Box3d> triboxes;         // bounding boxes of local faces
+
+
+  static ARRAY<int> pnearness;
+  static ARRAY<int> fnearness;
+  
+  cnt++;
+  
+  delfaces.SetSize (0);
+  elements.SetSize (0);
+
+
+  // determine topological distance of faces and points to
+  // base element
+
+
+  pnearness.SetSize (lpoints.Size());
+  fnearness.SetSize (lfacesplit);
+
+  for (i = 1; i <= pnearness.Size(); i++)
+    pnearness.Set(i, INT_MAX/10);
+
+  for (j = 1; j <= lfaces.Get(1).GetNP(); j++)
+    pnearness.Set(lfaces.Get(1).PNum(j), 0);
+
+
+  // MARK(appl1);
+  do
+    {
+      ok = 1;
+      
+      for (i = 1; i <= lfacesplit; i++)
+	{
+	  const Element2d & hface = lfaces.Get(i);
+
+	  minn = INT_MAX-1;
+	  for (j = 1; j <= hface.GetNP(); j++)
+	    {
+	      hi = pnearness.Get(hface.PNum(j));
+	      if (hi < minn) minn = hi;
+	    }
+	  
+	  for (j = 1; j <= hface.GetNP(); j++)
+	    {
+	      hpi = hface.PNum(j);
+	      if (pnearness.Get(hpi) > minn+1)
+		{
+		  ok = 0;
+		  pnearness.Set(hpi, 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 (edge.I1() > pnearness.Size() ||
+		edge.I2() > pnearness.Size() ||
+		edge.I1() < 1 ||
+		edge.I2() < 1)
+	      {
+		cerr << "pair out of range" << endl;
+		exit (1);
+	      }
+		
+	    if (pnearness.Get(edge.I1()) >
+		pnearness.Get(edge.I2()) + 1)
+	      {
+		;
+		ok = 0;
+		pnearness.Elem(edge.I1()) = 
+		  pnearness.Get(edge.I2()) + 1;
+	      }
+	    if (pnearness.Get(edge.I2()) >
+		pnearness.Get(edge.I1()) + 1)
+	      {
+		;
+		ok = 0;
+		pnearness.Elem(edge.I2()) = 
+		  pnearness.Get(edge.I1()) + 1;
+	      }
+	  }
+    }
+  while (!ok);
+
+  
+  for (i = 1; i <= fnearness.Size(); i++)
+    {
+      fnearness.Set(i, 0);
+      for (j = 1; j <= lfaces.Get(i).GetNP(); j++)
+	fnearness.Elem(i) += pnearness.Get(lfaces.Get(i).PNum(j));
+    }
+
+
+  // MARK(appl2);
+
+  // find bounding boxes of faces
+
+  triboxes.SetSize (lfaces.Size());
+  for (i = 1; i <= lfaces.Size(); i++)
+    {
+      const Element2d & face = lfaces.Get(i);
+      triboxes.Elem(i).SetPoint (lpoints.Get(face.PNum(1)));
+      for (j = 2; j <= face.GetNP(); j++)
+	triboxes.Elem(i).AddPoint (lpoints.Get(face.PNum(j)));
+    }
+
+  // MARK(appl3);  
+
+  /*
+  for (j = 1; j <= lfacesplit; j++)
+    for (k = 1; k <= lfaces.Get(j).GetNP(); k++)
+      {
+	INDEX_2 i2;
+	i2.I1() = lfaces.Get(j).PNumMod(k);
+	i2.I2() = lfaces.Get(j).PNumMod(k+1);
+	i2.Sort();
+	ledges.Set (i2, 1);
+      }
+  */  
+  
+  for (j = 1; j <= lfacesplit; j++)
+    {
+      const Element2d & face = lfaces.Get(j);
+      int newp, oldp;
+
+      newp = face.PNum(face.GetNP());
+      for (k = 1; k <= face.GetNP(); k++)
+	{
+	  oldp = newp;
+	  newp = face.PNum(k);
+	  
+	  INDEX_2 i2(oldp, newp);
+	  i2.Sort();
+	  ledges.Set (i2, 1);
+	}
+    }
+
+
+  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;
+
+
+  // check each rule:
+
+  // MARK(applyml);
+
+  for (ri = 1; ri <= rules.Size(); ri++)
+    { 
+      sprintf (problems.Elem(ri), "");
+
+      rule = rules.Get(ri);
+      
+      if (rule->GetNP(1) != lfaces.Get(1).GetNP())
+	continue;
+
+      if (rule->GetQuality() > tolerance)
+	{
+	  if (testmode)
+	    sprintf (problems.Elem(ri), "Quality not ok");
+	  continue;
+	}
+      
+      if (testmode)
+	sprintf (problems.Elem(ri), "no mapping found");
+      
+      loktestmode = testmode || rule->TestFlag ('t');
+      /*
+      if (tolerance > 8)
+	loktestmode = 1;
+      */
+
+      if (loktestmode)
+	(*testout) << "Rule " << ri << " = " << rule->Name() << endl;
+      
+      pmap.SetSize (rule->GetNP());
+      fmapi.SetSize (rule->GetNF());
+      fmapr.SetSize (rule->GetNF());
+      
+      for (i = 1; i <= lfaces.Size(); i++)
+	fused.Set (i, 0);
+      for (i = 1; i <= lpoints.Size(); i++)
+	pused.Set (i, 0);
+      for (i = 1; i <= pmap.Size(); i++)
+	pmap.Set(i, 0);
+      for (i = 1; i <= fmapi.Size(); i++)
+	fmapi.Set(i, 0);
+      for (i = 1; i <= fmapr.Size(); i++)
+	fmapr.Set(i, rule->GetNP(i));
+      
+      fused.Set (1, 1);
+      
+      fmapi.Set (1, 1);
+      fmapr.Set (1, rotind1);
+
+      
+      for (j = 1; j <= lfaces.Get(1).GetNP(); j++)
+	{
+	  locpi = lfaces.Get(1).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;
+      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)
+				    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
+	    
+	    { 
+
+	      // 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) || !allowpoint.Get(locpi) ||
+				  pnearness.Get(locpi) > rule->GetPNearness(npok))
+				{
+				  ok = 0;
+				}
+			      else
+				{
+				  const Point3d & lp = lpoints.Get(locpi);
+				  const Point3d & rp = rule->GetPoint(npok);
+
+				  if ( Dist2 (lp, rp) * rule->PointDistFactor(npok) > minerr)
+				    ok = 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
+		    
+		    {
+		      
+		      // 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++)
+			{
+			  int i1, i2;
+			  i1 = pmap.Get(rule->GetEdge(i).i1);
+			  i2 = pmap.Get(rule->GetEdge(i).i2);
+
+			  INDEX_2 in2(i1, 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++)
+				{
+				  int i1, i2;
+				  i1 = pmap.Get(el.PNum(j));
+				  i2 = pmap.Get(el.PNum(j+3));
+				  
+				  INDEX_2 in2(i1, i2);
+				  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.Set (3*i-2, lp.X()-rp.X());
+			  oldu.Set (3*i-1, lp.Y()-rp.Y());
+			  oldu.Set (3*i  , lp.Z()-rp.Z());
+			  
+			  allp.Set (3*i-2, lp.X());
+			  allp.Set (3*i-1, lp.Y());
+			  allp.Set (3*i  , 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.Set (3*i-2, rp.X() + newu.Get(3*i-2 - idiff));
+			  allp.Set (3*i-1, rp.Y() + newu.Get(3*i-1 - idiff));
+			  allp.Set (3*i  , rp.Z() + newu.Get(3*i   - 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 Element2d & 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);
+			    }
+
+
+			  // MARK(m2);			  
+			  //			  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.Elem (3 * (i-oldnp) - 2);
+			      np.Y() += newu.Elem (3 * (i-oldnp) - 1);
+			      np.Z() += newu.Elem (3 * (i-oldnp));
+			      
+			      pmap.Elem(i) = lpoints.Append (np);
+			    }
+			  
+			  // Set new Faces:
+			  
+			  for (i = rule->GetNOldF() + 1; i <= rule->GetNF(); i++)
+			    if (!fmapi.Get(i))
+			      {
+				Element2d 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)
+				{
+				  if (loktestmode)
+				    {
+				      sprintf (problems.Elem(ri), "Orientation wrong");
+				      (*testout) << "Orientation wrong" << 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 (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;
+    }
+
+  //  (*testout) << "end" << endl;
+
+  // if successfull, reload best choice
+  
+  if (found)
+    {
+
+#ifdef debug
+      // if face in advancing front ???
+      for (i = 1; i <= tempnewfaces.Size(); i++)
+	{
+	  hc = 1;
+	  for (k = 1; k <= lfaces.Size() && hc; k++)
+	    for (j = 1; j <= 3 && hc; j++)
+	      if (tempnewfaces.Elem(i).PNumMod(j  ) == lfaces.Get(k).PNum(1) &&
+		  tempnewfaces.Elem(i).PNumMod(j+1) == lfaces.Get(k).PNum(3) &&
+		  tempnewfaces.Elem(i).PNumMod(j+2) == lfaces.Get(k).PNum(2))
+		{
+		  tempdelfaces.Append(k);
+		  tempnewfaces.Elem(i).PNum(1) = 0;
+		  hc = 0;
+		  cerr << "Ruler-reload necessary" << endl;
+		}
+	}
+#endif
+      
+      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;
+  return found;
+}
+}
diff --git a/contrib/Netgen/libsrc/meshing/ruler3.hpp b/contrib/Netgen/libsrc/meshing/ruler3.hpp
new file mode 100644
index 0000000000..483d83ed4e
--- /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 0000000000..d260eb20d7
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/secondorder.cpp
@@ -0,0 +1,496 @@
+#include <mystdlib.h>
+#include "meshing.hpp"
+
+
+namespace netgen
+{
+
+
+
+  Refinement :: Refinement ()
+  {
+    ;
+  }
+
+  Refinement :: ~Refinement ()
+  {
+    ;
+  }
+  
+  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.p1, el.p2);
+
+	if (between.Used(i2))
+	  el.pmid = between.Get(i2);
+	else
+	  {
+	    Point3d pb;
+	    EdgePointGeomInfo ngi;
+	    PointBetween (mesh.Point (el.p1),
+			  mesh.Point (el.p2), 0.5,
+			  el.surfnr1, el.surfnr2,
+			  el.epgeominfo[0], el.epgeominfo[1],
+			  pb, ngi);
+	  
+	    el.pmid = mesh.AddPoint (pb);
+	    between.Set (i2, el.pmid);
+	  }
+      }
+
+    // refine surface elements
+    nse = mesh.GetNSE();
+    for (SurfaceElementIndex sei = 0; sei < nse; sei++)
+      {
+	int j;
+	const Element2d & el = mesh.SurfaceElement(sei);
+
+	int onp;
+      
+	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];
+      
+	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
+	      {
+		Point3d 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++)
+      {
+	int j;
+	const Element & el = mesh.VolumeElement(i);
+	int onp;
+
+	Element newel;
+	newel.SetIndex (el.GetIndex());
+
+	static int betw_tet[6][3] =
+	  { { 1, 2, 5 },
+	    { 1, 3, 6 },
+	    { 1, 4, 7 },
+	    { 2, 3, 8 },
+	    { 2, 4, 9 },
+	    { 3, 4, 10 } };
+	static int betw_prism[6][3] =
+	  {
+	    { 1, 3, 7 },
+	    { 1, 2, 8 },
+	    { 2, 3, 9 },
+	    { 4, 6, 10 },
+	    { 4, 5, 11 },
+	    { 5, 6, 12 },
+	  };
+	int (*betw)[3];
+
+	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.PNum(betw[j][0]),
+		       newel.PNum(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();
+  
+    //  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<Point3d> should(np);
+	ARRAY<Point3d> 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 = 1; j <= 3; j++)
+			mesh.Point(i).X(j) = 
+			  lam * should.Get(i).X(j) +
+			  (1-lam) * can.Get(i).X(j);
+		    }
+		  else
+		    mesh.Point(i) = 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();
+	    mesh.ImproveMeshJacobian (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.cpp b/contrib/Netgen/libsrc/meshing/smoothing2.cpp
new file mode 100644
index 0000000000..d26b20ad5d
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/smoothing2.cpp
@@ -0,0 +1,922 @@
+#include <mystdlib.h>
+
+#include "meshing.hpp"
+#include <opti.hpp>
+
+namespace netgen
+{
+
+  static const MeshOptimize2d * meshthis;
+
+
+#ifdef OLD
+
+  void CalcTriangleBadness (double x2, double x3, double y3, double metricweight,
+				   double h, double & badness, double & g1x, double & g1y)
+  {
+    // badness = sqrt(3.0) /36 * circumference^2 / area - 1 
+    // p1 = (0, 0), p2 = (x2, 0), p3 = (x3, y3);
+
+    Vec2d v23;
+    double l12, l13, l23, cir, area;
+    static const double c = sqrt(3.0) / 36;
+    double c1, c2, c3, c4;
+
+    v23.X() = x3 - x2;
+    v23.Y() = y3;
+
+    l12 = x2;
+    l13 = sqrt (x3*x3 + y3*y3);
+    l23 = v23.Length();
+
+    cir = l12 + l13 + l23;
+    area = 0.5 * x2 * y3;
+
+    if (area <= 1e-24 * cir * cir)
+      {
+	g1x = 0;
+	g1y = 0;
+	badness = 1e10;
+	return;
+      }
+
+    badness = c * cir * cir / area - 1;
+
+    c1 = 2 * c * cir / area;
+    c2 = 0.5 * c * cir * cir / (area * area);
+
+    g1x = c1 * ( - 1 - x3 / l13) - c2 * (-v23.Y());
+    g1y = c1 * (     - y3 / l13) - c2 * ( v23.X());
+  
+    //  metricweight = 0.1;
+    if (metricweight > 0)
+      {
+	// area = (x2 - x1) * (y3 - y1) - (x3 - x1) * (y2 - y1);
+	// add:  metricweight * (area / h^2 + h^2 / area - 2)
+      
+	const double area = x2 * y3;
+	const double dareax1 = -y3; 
+	const double dareay1 = x3 - x2; 
+
+	const double areahh = area / (h * h);
+	const double fac = metricweight * (areahh - 1 / areahh) / area;
+
+	badness += metricweight * (areahh + 1 / areahh - 2);
+	g1x += fac * dareax1;
+	g1y += fac * dareay1; 
+      
+	/*
+	// add: metricweight * (l1^2/h^2 + l2^2/h^2 + l3^2/h2 + h^2/l1^2 + h^2/l2^2 + h^2/l3^2 - 6)
+	double h2 = h*h;
+	double l1 = x2*x2;
+	double l2 = x3*x3+y3*y3;
+	double l3 = (x2-x3)*(x2-x3)+y3*y3;
+	double dl1dx = 2*(-x2);
+	double dl1dy = 0;
+	double dl2dx = -2*x3;
+	double dl2dy = -2*y3;
+
+	badness += (l1/h2 + l2/h2 + l3/h2 +h2/l1 + h2/l2 + h2/l3-6) * metricweight;
+
+	g1x += metricweight * (dl1dx/h2-h2/(l1*l1)*dl1dx + dl2dx/h2-h2/(l2*l2)*dl2dx);
+	g1y += metricweight * (dl1dy/h2-h2/(l1*l1)*dl1dy + dl2dy/h2-h2/(l2*l2)*dl2dy);
+	*/
+      }
+  }
+
+#endif
+
+
+  /*
+  static const double c_trig = sqrt(3.0) / 12;
+  static const double c_trig4 = sqrt(3.0) / 3;
+  */
+  static const double c_trig = 0.14433756;
+  static const double c_trig4 = 0.57735026;
+
+  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)
+      
+	double 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; 
+      }
+  }
+
+
+
+
+
+
+
+
+
+#ifdef OLD
+  double CalcTriangleBadness (const Point3d & p1, 
+			      const Point3d & p2, 
+			      const Point3d & p3,
+			      double metricweight,
+			      double h)
+  {
+    double badness;
+    double g1x, g1y;
+  
+    Vec3d e1 (p1, p2);
+    Vec3d e2 (p1, p3);
+  
+    double e1l = e1.Length() + 1e-24;
+    e1 /= e1l;
+    double e1e2 = (e1 * e2);
+    e2.Add (-e1e2, e1);
+    double e2l = e2.Length();
+  
+    CalcTriangleBadness ( e1l, e1e2, e2l,
+			  metricweight, h, badness, g1x, g1y);
+    return badness;
+  }
+#endif
+
+
+
+
+  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);
+  }
+
+
+
+
+
+  static MeshPoint sp1; 
+  static PointGeomInfo gi1;
+  static Vec3d n, t1, t2;
+  static ARRAY<SurfaceElementIndex> locelements(0);
+  static ARRAY<int> locrots(0);
+  static ARRAY<double> lochs(0);
+  // static int locerr2;
+  static double locmetricweight = 0;
+  static double loch;
+  static int surfi, surfi2;
+  static int uselocalh;
+
+
+  class Opti2SurfaceMinFunction : public MinFunction
+  {
+    const Mesh & mesh;
+  public:
+    Opti2SurfaceMinFunction (const Mesh & amesh)
+      : mesh(amesh)
+    { } ;
+    virtual double FuncGrad (const Vector & x, Vector & g) 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
+  {
+    Vec3d n, vgrad;
+    Point3d pp1;
+    double g1x, g1y;
+    double badness, hbadness;
+
+    vgrad = 0;
+    badness = 0;
+
+    meshthis -> GetNormalVector (surfi, sp1, gi1, n);
+
+    pp1 = sp1;
+    pp1.Add2 (x.Get(1), t1, x.Get(2), t2);
+
+    //  meshthis -> ProjectPoint (surfi, pp1);
+    // meshthis -> GetNormalVector (surfi, pp1, n);
+
+    for (int j = 0; j < locelements.Size(); j++)
+      {
+	int roti = locrots[j];
+	const Element2d & bel = mesh[locelements[j]];
+
+	Vec3d e1(pp1, mesh[bel.PNumMod(roti + 1)]);
+	Vec3d e2(pp1, mesh[bel.PNumMod(roti + 2)]);
+
+	if (uselocalh) loch = lochs[j];
+
+	double e1l = e1.Length();
+	if (Determinant(e1, e2, n) > 1e-8 * e1l * e2.Length())
+	  {
+	    e1 /= e1l;
+	    double e1e2 = e1 * e2;
+	    e2.Add (-e1e2, e1);
+	    double e2l = e2.Length();
+
+	    CalcTriangleBadness ( e1l, e1e2, e2l, locmetricweight, loch,
+				  hbadness, g1x, g1y);
+
+	    badness += hbadness;
+	    vgrad.Add2 (g1x, e1, g1y / e2l, e2);
+	  }
+	else
+	  badness += 1e8;
+      }
+
+    vgrad.Add (-(vgrad * n), n);
+
+    grad.Elem(1) = vgrad * t1;
+    grad.Elem(2) = vgrad * t2;
+    return badness;
+  }
+
+
+  class Opti2EdgeMinFunction : public MinFunction
+  {
+    const Mesh & mesh;
+  public:
+    Opti2EdgeMinFunction (const Mesh & amesh)
+      : mesh(amesh) { } ;
+
+    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;
+    Vec3d n1, n2, v1, v2, e1, e2, vgrad;
+    Point3d pp1;
+    Vec2d g1;
+    double badness, hbadness;
+
+    vgrad.X() = 0;
+    vgrad.Y() = 0;
+    vgrad.Z() = 0;
+    badness = 0;
+
+    pp1 = sp1 + x.Get(1) * t1;
+    meshthis -> ProjectPoint2 (surfi, surfi2, pp1);
+
+    for (j = 0; j < locelements.Size(); j++)
+      {
+	rot = locrots[j];
+	const Element2d & bel = mesh[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 (uselocalh) loch = lochs[j];
+	CalcTriangleBadness ( (e1 * v1), (e1 * v2), (e2 * v2), locmetricweight, loch,
+			      hbadness, g1.X(), g1.Y());
+
+	badness += hbadness;
+
+	vgrad.X() += g1.X() * e1.X() + g1.Y() * e2.X();
+	vgrad.Y() += g1.X() * e1.Y() + g1.Y() * e2.Y();
+	vgrad.Z() += g1.X() * e1.Z() + g1.Y() * e2.Z();
+      }
+
+    meshthis -> GetNormalVector (surfi, pp1, n1);
+    meshthis -> GetNormalVector (surfi2, pp1, n2);
+
+    v1 = Cross (n1, n2);
+    v1 /= v1.Length();
+
+    grad.Elem(1) = (vgrad * v1) * (t1 * v1);
+
+    return badness;
+  }
+
+
+
+
+  class Opti2SurfaceMinFunctionJacobian : public MinFunction
+  {
+    const Mesh & mesh;
+  public:
+    Opti2SurfaceMinFunctionJacobian (const Mesh & amesh)
+      : mesh(amesh)
+    { } ;
+    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 j, k, lpi, gpi;
+    Vec3d n, vgrad;
+    Point3d pp1;
+    Vec2d g1, vdir;
+    double badness, hbadness, hbad, hderiv;
+
+    vgrad = 0;
+    badness = 0;
+
+    meshthis -> GetNormalVector (surfi, sp1, gi1, n);
+
+    pp1 = sp1;
+    pp1.Add2 (x.Get(1), t1, x.Get(2), t2);
+
+    //  meshthis -> ProjectPoint (surfi, pp1);
+    //  meshthis -> GetNormalVector (surfi, pp1, n);
+
+    static ARRAY<Point2d> pts2d;
+    pts2d.SetSize(mesh.GetNP());
+
+    grad = 0;
+
+    for (j = 1; j <= locelements.Size(); j++)
+      {
+	lpi = locrots.Get(j);
+	const Element2d & bel = 
+	  mesh[locelements.Get(j)];
+      
+	gpi = bel.PNum(lpi);
+
+	for (k = 1; k <= bel.GetNP(); k++)
+	  {
+	    PointIndex pi = bel.PNum(k);
+	    pts2d.Elem(pi) = Point2d (t1 * (mesh.Point(pi) - sp1), 
+				      t2 * (mesh.Point(pi) - sp1)); 
+	  }				    
+	pts2d.Elem(gpi) = Point2d (x.Get(1), x.Get(2));
+      
+
+	for (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.Elem(k) += 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;
+    Vec3d n, vgrad;
+    Point3d pp1;
+    Vec2d g1, vdir;
+    double badness, hbadness, hbad, hderiv;
+
+    vgrad = 0;
+    badness = 0;
+
+    meshthis -> GetNormalVector (surfi, sp1, gi1, n);
+
+    pp1 = sp1;
+    pp1.Add2 (x.Get(1), t1, x.Get(2), t2);
+
+    static ARRAY<Point2d> pts2d;
+    pts2d.SetSize(mesh.GetNP());
+
+    deriv = 0;
+
+    for (j = 1; j <= locelements.Size(); j++)
+      {
+	lpi = locrots.Get(j);
+	const Element2d & bel = 
+	  mesh[locelements.Get(j)];
+      
+	gpi = bel.PNum(lpi);
+
+	for (k = 1; k <= bel.GetNP(); k++)
+	  {
+	    PointIndex pi = bel.PNum(k);
+	    pts2d.Elem(pi) = Point2d (t1 * (mesh.Point(pi) - sp1), 
+				      t2 * (mesh.Point(pi) - sp1)); 
+	  }				    
+	pts2d.Elem(gpi) = Point2d (x.Get(1), x.Get(2));
+      
+
+	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 Point3d & p,
+					       const PointGeomInfo & gi)
+  {
+    ;
+  }
+
+  void MeshOptimize2d :: ImproveMesh (Mesh & mesh)
+  {
+    if (!faceindex)
+      {
+	PrintMessage (3, "Smoothing");
+
+	for (faceindex = 1; faceindex <= mesh.GetNFD(); faceindex++)
+	  {
+	    ImproveMesh (mesh);
+	    if (multithread.terminate)
+	      throw NgException ("Meshing stopped");
+	  }
+	faceindex = 0;
+	return;
+      }
+ 
+    CheckMeshApproximation (mesh);
+
+    int i, j, k, surfi3;
+    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;
+    int moveisok;
+
+    PointGeomInfo ngi;
+    Point3d origp;
+
+    Vec3d n1, n2;
+    Vector x(2), xedge(1);
+
+    ARRAY<MeshPoint, PointIndex::BASE> savepoints(mesh.GetNP());
+    uselocalh = mparam.uselocalh;
+
+    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]);
+      }
+
+    loch = mparam.maxh;
+    locmetricweight = metricweight;
+    meshthis = this;
+
+    Opti2SurfaceMinFunction surfminf(mesh);
+    Opti2EdgeMinFunction edgeminf(mesh);
+    Opti2SurfaceMinFunctionJacobian surfminfj(mesh);
+
+    OptiParameters par;
+    par.maxit_linsearch = 8;
+    par.maxit_bfgs = 5;
+
+    /*
+      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;
+
+    for (PointIndex pi = PointIndex::BASE; 
+	 pi < mesh.GetNP()+PointIndex::BASE; pi++)
+
+      if (mesh.PointType(pi) == SURFACEPOINT)
+	{
+	  if (multithread.terminate)
+	    throw NgException ("Meshing stopped");
+	
+	  if (pi ==  3679)
+	    (*testout) << " old: " << mesh[pi] << endl;
+
+
+	  cnt++;
+	  if (cnt % modplot == 0 && writestatus)
+	    {
+	      printeddot = 1;
+	      PrintDot (plotchar);
+	    }
+	
+	  if (elementsonpoint[pi].Size() == 0)
+	    continue;
+	
+	  sp1 = mesh[pi];
+
+	  Element2d & hel = mesh[elementsonpoint[pi][0]];
+
+	  int hpi = 0;
+	  for (j = 1; j <= hel.GetNP(); j++)
+	    if (hel.PNum(j) == pi)
+	      {
+		hpi = j;
+		break;
+	      }
+
+	  gi1 = hel.GeomInfoPi(hpi);
+	  SelectSurfaceOfPoint (sp1, gi1);
+	  
+	  locelements.SetSize(0);
+	  locrots.SetSize (0);
+	  lochs.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;
+		  }
+	      
+	      if (uselocalh)
+		{
+		  Point3d pmid = Center (mesh[bel[0]], mesh[bel[1]], mesh[bel[2]]);
+		  lochs.Append (mesh.GetH(pmid));
+		}
+	    }
+
+	  
+	  GetNormalVector (surfi, sp1, gi1, n);
+	  n.GetNormal (t1);
+	  t2 = Cross (n, t1);
+	  
+	  // save points, and project to tangential plane
+	  for (j = 0; j < locelements.Size(); j++)
+	    {
+	      const Element2d & el = mesh[locelements[j]];
+	      for (k = 0; k < el.GetNP(); k++)
+		savepoints[el[k]] = mesh[el[k]];
+	    }
+
+	  for (j = 0; j < locelements.Size(); j++)
+	    {
+	      const Element2d & el = mesh[locelements[j]];
+	      for (k = 0; k < el.GetNP(); k++)
+		{
+		  PointIndex hpi = el[k];
+		  double lam = n * (mesh[hpi] - sp1);
+		  mesh[hpi] -= lam * n;
+		}
+	    }
+	  
+	  x = 0;
+
+	  if (mixed)
+	    BFGS (x, surfminfj, par, 1e-6);
+	  else
+	    BFGS (x, surfminf, par, 1e-6);
+
+	  origp = mesh[pi];
+	  loci = 1;
+	  fact = 1;
+	  moveisok = 0;
+
+	  // restore other points
+	  for (j = 0; j < locelements.Size(); j++)
+	    {
+	      const Element2d & el = mesh[locelements[j]];
+	      for (k = 0; k < el.GetNP(); k++)
+		{
+		  PointIndex hpi = el[k];
+		  if (hpi != pi) mesh[hpi] = savepoints[hpi];
+		}
+	    }
+	  
+	  
+	  //optimizer loop (if not 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;
+	      fact = fact/2.;
+
+	      if (pi ==  3679)
+		(*testout) << " before proj: " << mesh[pi] << endl;
+
+	      ProjectPoint (surfi, mesh[pi]);
+
+	      if (pi ==  3679)
+		{
+		  (*testout) << " after proj: " << mesh[pi] << endl;
+		  (*testout) << "surfi =" << surfi << endl;
+		}
+	    
+	      moveisok = CalcPointGeomInfo(surfi, ngi, mesh[pi]); 
+	      // point lies on same chart in stlsurface
+	    
+	      if (moveisok)
+		{
+		  for (j = 0; j < locelements.Size(); j++)
+		    mesh[locelements[j]].GeomInfoPi(locrots[j]) = ngi;
+		}
+	      else
+		{
+		  mesh[pi] = origp;
+		}
+	    
+	    }
+
+	  if (pi ==  3679)
+	    (*testout) << " new: " << mesh[pi] << endl;
+	}
+  
+
+    if (printeddot)
+      PrintDot ('\n');
+  
+    CheckMeshApproximation (mesh);
+    mesh.SetNextTimeStamp();
+  }
+
+  void MeshOptimize2d :: GetNormalVector(INDEX /* surfind */, const Point3d & p, Vec3d & nv) const
+  {
+    nv = Vec3d (0, 0, 1);
+  }
+
+  void MeshOptimize2d :: GetNormalVector(INDEX surfind, const Point3d & p, PointGeomInfo & gi, Vec3d & 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 0000000000..61c1910884
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/smoothing3.cpp
@@ -0,0 +1,1546 @@
+#include <mystdlib.h>
+
+#include "meshing.hpp"
+#ifdef SOLIDGEOM
+#include <csg.hpp>
+#endif
+#include <opti.hpp>
+
+
+namespace netgen
+{
+  
+
+  PointFunction1 :: PointFunction1 (Mesh::T_POINTS & apoints, 
+				    const ARRAY<INDEX_3> & afaces,
+				    double ah)
+    : points(apoints), faces(afaces)
+  {
+    h = ah;
+  }
+  
+
+  double PointFunction1 :: Func (const Vector & vp) const
+  {
+    int j;
+    double badness = 0;
+    Point3d pp(vp(0), vp(1), vp(2));
+
+    for (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);
+	badness += bad;
+      }
+ 
+    return badness;
+  }
+
+
+
+  double PointFunction1 :: 
+  FuncDeriv (const Vector & x, const Vector & dir, double & deriv) const
+  {
+    static Vector hx(3);
+    static 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
+  {
+    static Vector hx(3);
+    static double eps = 1e-6;
+
+    hx = x;
+    for (int i = 1; i <= 3; i++)
+      {
+	hx.Elem(i) = x.Get(i) + eps * h;
+	double fr = Func (hx);
+	hx.Elem(i) = x.Get(i) - eps * h;
+	double fl = Func (hx);
+	hx.Elem(i) = x.Get(i);
+
+	g.Elem(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 */
+
+  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 i, nf = faces.Size();
+
+    m.SetSize (nf, 4);
+  
+    for (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;
+    static Vector hv(4);
+    static Vector res;
+    res.SetSize (m.Height());
+
+    for (i = 1;i <= 3; i++)
+      hv.Elem(i) = vp.Get(i);
+    hv.Elem(4) = 1;
+    m.Mult (hv, res);
+
+    for (i = 1; i <= res.Size(); i++)
+      {
+	if (res.Get(i) < 1e-10)
+	  badness += 1e24;
+	else
+	  badness += 1 / res.Get(i);
+      }
+ 
+    return badness;
+  }
+
+
+  double CheapPointFunction1 :: FuncGrad (const Vector & x, Vector & g) const
+  {
+    static Vector hx(3);
+    static double eps = 1e-6;
+
+    hx = x;
+    for (int i = 1; i <= 3; i++)
+      {
+	hx.Elem(i) = x.Get(i) + eps * h;
+	double fr = Func (hx);
+	hx.Elem(i) = x.Get(i) - eps * h;
+	double fl = Func (hx);
+	hx.Elem(i) = x.Get(i);
+
+	g.Elem(i) = (fr - fl) / (2 * eps * h);
+      }
+
+    return Func(x);
+  }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  /* ************* PointFunction **************************** */
+
+
+  class PointFunction 
+  {
+  public:
+    Mesh::T_POINTS & points;
+    const Mesh::T_VOLELEMENTS & elements;
+    TABLE<INDEX,PointIndex::BASE> elementsonpoint;
+    PointIndex actpind;
+    double h;
+  
+  public:
+    PointFunction (Mesh::T_POINTS & apoints, 
+		   const Mesh::T_VOLELEMENTS & aelements);
+  
+    virtual void SetPointIndex (PointIndex aactpind);
+    void SetLocalH (double ah) { h = ah; }
+    double GetLocalH () const { return h; }
+    virtual double PointFunctionValue (const Point3d & pp) const;
+    virtual double PointFunctionValueGrad (const Point3d & pp, Vector & grad) const;
+    virtual double PointFunctionValueDeriv (const Point3d & pp, const Vec3d & dir, double & deriv) const;
+
+    int MovePointToInner ();
+  };
+
+
+  PointFunction :: PointFunction (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++)
+      {
+	if (elements.Get(i).NP() == 4)
+	  for (j = 1; j <= elements.Get(i).NP(); j++)
+	    elementsonpoint.Add (elements.Get(i).PNum(j), i);  
+      }
+  }
+
+  void PointFunction :: SetPointIndex (PointIndex aactpind)
+  {
+    actpind = aactpind; 
+  }  
+
+  double PointFunction :: PointFunctionValue (const Point3d & pp) const
+  {
+    int j;
+    INDEX eli;
+    const Element * el;
+    double badness;
+    //  ARRAY<const Point3d*> p(4);
+    Point3d hp;
+
+    badness = 0;
+
+    hp = points[actpind];
+    points[actpind] = pp;
+
+    for (j = 0; j < elementsonpoint[actpind].Size(); j++)
+      {
+	eli = elementsonpoint[actpind][j];
+	el = &elements.Get(eli);
+	badness += CalcTetBadness (points[el->PNum(1)], 
+				   points[el->PNum(2)], 
+				   points[el->PNum(3)], 
+				   points[el->PNum(4)], -1);
+      }
+  
+    points[actpind] = hp; 
+    return badness;
+  }
+
+
+  double PointFunction :: PointFunctionValueGrad (const Point3d & pp, Vector & grad) const
+  {
+    double f, delta = h * 1e-6;
+    Point3d hpp;
+
+    f = PointFunctionValue (pp);
+
+    /*
+      hpp = pp;
+      hpp.X() = pp.X() + delta;
+      fr = PointFunctionValue (hpp);
+      hpp.X() = pp.X() - delta;
+      fl = PointFunctionValue (hpp);
+      grad.Elem(1) = (fr - fl) / (2 * delta);
+
+      hpp = pp;
+      hpp.Y() = pp.Y() + delta;
+      fr = PointFunctionValue (hpp);
+      hpp.Y() = pp.Y() - delta;
+      fl = PointFunctionValue (hpp);
+      grad.Elem(2) = (fr - fl) / (2 * delta);
+
+      hpp = pp;
+      hpp.Z() = pp.Z() + delta;
+      fr = PointFunctionValue (hpp);
+      hpp.Z() = pp.Z() - delta;
+      fl = PointFunctionValue (hpp);
+      grad.Elem(3) = (fr - fl) / (2 * delta);
+    */
+
+
+
+    // new gradient calculation
+    int j, k;
+    INDEX eli;
+    //  double badness;
+    Point3d hp;
+    Vec3d vgradi, vgrad(0,0,0);
+
+    //  badness = 0;
+
+    hp = points[actpind];
+    points[actpind] = pp;
+
+    for (j = 0; j < elementsonpoint[actpind].Size(); j++)
+      {
+	eli = elementsonpoint[actpind][j];
+	const Element & el = elements.Get(eli);
+
+	for (k = 1; k <= 4; k++)
+	  if (el.PNum(k) == actpind)
+	    {
+	      CalcTetBadnessGrad (points[el.PNum(1)], 
+				  points[el.PNum(2)], 
+				  points[el.PNum(3)], 
+				  points[el.PNum(4)], -1, k, vgradi);
+	      vgrad += vgradi;
+	    }
+      }
+    points[actpind] = hp; 
+
+    for (j = 1; j <= 3; j++)
+      grad.Elem(j) = vgrad.X(j);
+
+    return f;
+  }
+
+
+  double PointFunction :: PointFunctionValueDeriv (const Point3d & pp, const Vec3d & dir,
+						   double & deriv) const
+  {
+    double f;
+    Point3d hpp;
+
+    Vec3d dirn (dir);
+    double ldir = dir.Length();
+
+    int j, k;
+    INDEX eli;
+    //  double badness;
+    Point3d hp;
+    Vec3d vgradi, vgrad(0,0,0);
+
+    //  badness = 0;
+
+    hp = points[actpind];
+    points[actpind] = pp;
+    f = 0;
+
+    for (j = 0; j < elementsonpoint[actpind].Size(); j++)
+      {
+	eli = elementsonpoint[actpind][j];
+	const Element & el = elements.Get(eli);
+
+	for (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);
+
+	      vgrad += vgradi;
+	    }
+      }
+
+    points[actpind] = hp; 
+    deriv = dir * vgrad;
+    return f;
+  }
+
+  int PointFunction :: MovePointToInner ()
+  {
+    int j, k;
+
+    // try point movement 
+    ARRAY<Element2d> faces;
+  
+    for (j = 0; j < elementsonpoint[actpind].Size(); j++)
+      {
+	const Element & el = 
+	  elements.Get(elementsonpoint[actpind][j]);
+      
+	for (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] = hp;
+      }
+    else
+      cout << "no inner point found" << endl;
+  
+    return hi;
+  }
+
+
+
+
+
+
+  class CheapPointFunction : public PointFunction
+  {
+    DenseMatrix m;
+  public:
+    CheapPointFunction (Mesh::T_POINTS & apoints, 
+			const Mesh::T_VOLELEMENTS & aelements);
+    virtual void SetPointIndex (PointIndex aactpind);
+    virtual double PointFunctionValue (const Point3d & pp) const;
+    virtual double PointFunctionValueGrad (const Point3d & pp, Vector & grad) const;
+  };
+
+
+  CheapPointFunction :: CheapPointFunction (Mesh::T_POINTS & apoints, 
+					    const Mesh::T_VOLELEMENTS & aelements)
+    : PointFunction (apoints, aelements)
+  {
+    ;
+  }
+
+
+  void CheapPointFunction :: SetPointIndex (PointIndex aactpind)
+  {
+    actpind = aactpind; 
+
+    int n = elementsonpoint[actpind].Size();
+    int i, j;
+    int pi1, pi2, pi3;
+
+    m.SetSize (n, 4);
+
+    for (i = 0; i < n; i++)
+      {
+	pi1 = 0;
+	pi2 = 0;
+	pi3 = 0;
+
+	const Element & el = elements.Get (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) = 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 CheapPointFunction :: PointFunctionValue (const Point3d & pp) const
+  {
+    static Vector p4(4);
+    static Vector di;
+    int n = m.Height();
+
+    p4.Elem(1) = pp.X();
+    p4.Elem(2) = pp.Y();
+    p4.Elem(3) = pp.Z();
+    p4.Elem(4) = 1;
+
+    di.SetSize (n);
+    m.Mult (p4, di);
+  
+    double sum = 0;
+    for (int i = 1; i <= n; i++)
+      {
+	if (di.Get(i) > 0)
+	  sum += 1 / di.Get(i);
+	else
+	  return 1e16;
+      }
+    return sum;
+  }
+
+
+
+
+  double CheapPointFunction :: PointFunctionValueGrad (const Point3d & pp, Vector & grad) const
+  {
+    static Vector p4(4);
+    static Vector di;
+
+    grad.SetSize (3);
+    int n = m.Height();
+
+    p4.Elem(1) = pp.X();
+    p4.Elem(2) = pp.Y();
+    p4.Elem(3) = pp.Z();
+    p4.Elem(4) = 1;
+
+    di.SetSize (n);
+    m.Mult (p4, di);
+  
+    double sum = 0;
+    grad = 0;
+    for (int i = 1; i <= n; i++)
+      {
+	if (di.Get(i) > 0)
+	  {
+	    double idi = 1 / di.Get(i);
+	    sum += idi;
+	    grad.Elem(1) -= idi * idi * m.Get(i, 1);
+	    grad.Elem(2) -= idi * idi * m.Get(i, 2);
+	    grad.Elem(3) -= idi * idi * m.Get(i, 3);
+	  }
+	else
+	  {
+	    return 1e16;
+	  }
+      }
+    return sum;
+  }
+
+
+
+
+
+
+
+
+  class Opti3FreeMinFunction : public MinFunction
+  { 
+    const PointFunction & pf;
+    Point3d sp1;
+  
+  public:
+    Opti3FreeMinFunction (const PointFunction & apf);
+    void SetPoint (const Point3d & 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
+  {
+    Point3d pp;
+    pp.X() = sp1.X() + x.Get(1);
+    pp.Y() = sp1.Y() + x.Get(2);
+    pp.Z() = sp1.Z() + x.Get(3);
+
+    return pf.PointFunctionValue (pp);
+  }
+  
+  double Opti3FreeMinFunction :: FuncGrad (const Vector & x, Vector & grad) const
+  {
+    Point3d pp;
+    pp.X() = sp1.X() + x.Get(1);
+    pp.Y() = sp1.Y() + x.Get(2);
+    pp.Z() = sp1.Z() + x.Get(3);
+
+    return pf.PointFunctionValueGrad (pp, grad);
+  }
+
+  double Opti3FreeMinFunction :: FuncDeriv (const Vector & x, const Vector & dir, double & deriv) const
+  {
+    Point3d pp;
+    pp.X() = sp1.X() + x.Get(1);
+    pp.Y() = sp1.Y() + x.Get(2);
+    pp.Z() = sp1.Z() + x.Get(3);
+
+    Vec3d vdir;
+    vdir.X() = dir.Get(1);
+    vdir.Y() = dir.Get(2);
+    vdir.Z() = dir.Get(3);
+
+    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();
+    int i, j;
+
+    static Vector hx;
+    hx.SetSize(n);
+
+    double eps = 1e-8;
+    double f, f11, f12, f21, f22;
+
+    f = Func(x);
+
+  
+    for (i = 1; i <= n; i++)
+      {
+	for (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.Elem(i) = x.Get(i) + eps;
+	f11 = Func(hx);
+	hx.Elem(i) = x.Get(i) - 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;
+    double badness;
+    static Vector freegrad(3);
+
+    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> 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;
+  static Vector freegrad(3);
+
+  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 CalcBad (const Mesh::T_POINTS & points, const Element & elem,
+		double h)
+{
+  if (elem.GetType() == TET)
+    return CalcTetBadness (points[elem.PNum(1)], 
+			   points[elem.PNum(2)],  
+			   points[elem.PNum(3)],  
+			   points[elem.PNum(4)], h);  
+  return 0;
+}
+
+
+extern double teterrpow;
+double CalcTotalBad (const Mesh::T_POINTS & points, 
+		     const Mesh::T_VOLELEMENTS & elements)
+{
+  int i;
+  double sum = 0;
+  double elbad;
+  
+  tets_in_qualclass.SetSize(20);
+  for (i = 1; i <= 20; i++)
+    tets_in_qualclass.Elem(i) = 0;
+
+
+  for (i = 1; i <= elements.Size(); i++)
+    {
+      elbad = pow (CalcBad (points, elements.Get(i), 0), 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);  
+    }
+}
+
+void JacobianPointFunction :: SetPointIndex (PointIndex aactpind)
+{
+  actpind = aactpind; 
+}  
+
+
+double JacobianPointFunction :: Func (const Vector & v) const
+{
+  int j;
+  double badness = 0;
+
+  Point3d hp = points.Elem(actpind);
+  points.Elem(actpind) = hp + Vec3d (v.Get(1), v.Get(2), v.Get(3));
+
+  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;
+
+  Point3d hp = points.Elem(actpind);
+  points.Elem(actpind) = hp + Vec3d (x.Get(1), x.Get(2), x.Get(3));
+
+  double 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;
+
+      for (k = 1; k <= 3; k++)
+	{
+	  vdir = Vec3d(0,0,0);
+	  vdir.X(k) = 1;
+
+	  hbad = elements.Get(eli).
+	    CalcJacobianBadnessDirDeriv (points, lpi, vdir, hderiv);
+	  g.Elem(k) += hderiv;
+	  if (k == 1)
+	    badness += hbad;
+	}
+    }
+  
+  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;
+
+  Point3d hp = points.Elem(actpind);
+  points.Elem(actpind) = hp + Vec3d (x.Get(1), x.Get(2), x.Get(3));
+
+  double hderiv;
+  deriv = 0;
+  Vec3d vdir(dir.Get(1), dir.Get(2), dir.Get(3));
+
+  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;
+  
+  /*
+    (*testout) << "bad1 = " << badness << " der = " << deriv << endl;
+
+
+
+    static Vector hx(3);
+    static double eps = 1e-6;
+
+    double dirlen = dir.L2Norm();
+    if (dirlen < 1e-14)
+    {
+    deriv = 0;
+    return Func(x);
+    }
+
+    hx.Set(1, x);
+    hx.Add(eps / dirlen, dir);
+    double fr = Func (hx);
+    hx.Set(1, x);
+    hx.Add(-eps / dirlen, dir);
+    double fl = Func (hx);
+
+    deriv = (fr - fl) / (2 * eps) * dirlen;
+
+
+    (*testout) << "bad2 = " << Func(x) << " der = " << deriv << endl;
+
+
+    return Func(x);
+  */
+}
+
+
+
+
+
+
+
+
+
+
+#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;
+    }
+
+  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).precision(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;
+
+
+
+  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 ('+');
+      
+      //    (*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 (OPTIMIZEGOAL goal)
+{
+  int typ = 1;
+  int j;
+  
+  (*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);
+	  for (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);
+      (*testout) << "Total badness = " << bad1 << endl;
+      PrintMessage (5, "Total badness = ", bad1);
+    }
+  
+  Vector x(3);
+  
+  (*testout).precision(8);
+  
+  int uselocalh = mparam.uselocalh;
+
+
+  PointFunction * pf;
+
+  if (typ == 1)
+    pf = new PointFunction(points, volelements);
+  else
+    pf = new CheapPointFunction(points, volelements);
+
+  //  pf->SetLocalH (h);
+  
+  Opti3FreeMinFunction freeminf(*pf);
+
+  OptiParameters par;
+  par.maxit_linsearch = 20;
+  par.maxit_bfgs = 20;
+
+
+  char * savetask = multithread.task;
+  multithread.task = "Smooth Mesh";
+  
+  for (PointIndex i = PointIndex::BASE; 
+       i < points.Size()+PointIndex::BASE; i++)
+    if (PointType(i) == 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 ('+');
+
+	double lh = GetH(points[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)
+	  {
+	    BFGS (x, freeminf, par);
+	    
+	    points[i].X() += x.Get(1);
+	    points[i].Y() += x.Get(2);
+	    points[i].Z() += x.Get(3);
+	  }
+      }
+  PrintDot ('\n');
+  
+  
+  delete pf;
+
+  multithread.task = savetask;
+
+  if (goal == OPT_QUALITY)
+    {
+      bad1 = CalcTotalBad (points, volelements);
+      (*testout) << "Total badness = " << bad1 << endl;
+      PrintMessage (5, "Total badness = ", bad1);
+    }
+}
+
+
+
+
+// Improve Condition number of Jacobian, any elements  
+void Mesh :: ImproveMeshJacobian (OPTIMIZEGOAL goal)
+{
+  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);
+  
+
+  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));
+    }
+
+
+  char * savetask = multithread.task;
+  multithread.task = "Smooth Mesh Jacobian";
+  
+  for (i = 1; i <= points.Size(); i++)
+    if (PointType(i) == INNERPOINT)
+      {
+
+	(*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 = GetH(points.Get(i));
+	par.typx = lh;
+
+	pf.SetPointIndex (i);
+
+	x = 0;
+	int pok = (pf.Func (x) < 1e10); 
+
+	if (pok)
+	  {
+	    BFGS (x, pf, par);
+
+	    points.Elem(i).X() += x.Get(1);
+	    points.Elem(i).Y() += x.Get(2);
+	    points.Elem(i).Z() += x.Get(3);
+	  }
+	else
+	  {
+	    cout << "el not ok" << endl;
+	  }
+      }
+  PrintDot ('\n');
+  
+
+  multithread.task = savetask;
+}
+
+
+
+
+}
diff --git a/contrib/Netgen/libsrc/meshing/specials.cpp b/contrib/Netgen/libsrc/meshing/specials.cpp
new file mode 100644
index 0000000000..ae9877cc2e
--- /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();
+}
+
+
+
+
+void HelmholtzMesh (Mesh & mesh)
+{
+  int i, j;
+  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++)
+    {
+      Point3d & p = mesh.Point(i);
+      double rold = sqrt (sqr(p.X()) + sqr(p.Y()) + sqr(p.Z()));
+      if (rold < ri) continue;
+
+      double rnew = 1 / (a * rold - b);
+      double fac = rnew / rold;
+      p.X() *= fac;
+      p.Y() *= fac;
+      p.Z() *= fac;
+    }
+}
+}
diff --git a/contrib/Netgen/libsrc/meshing/specials.hpp b/contrib/Netgen/libsrc/meshing/specials.hpp
new file mode 100644
index 0000000000..700ba4596b
--- /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 0000000000..cb28648b6a
--- /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 0000000000..9c0e79d531
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/topology.cpp
@@ -0,0 +1,1595 @@
+#include <mystdlib.h>
+
+#include "meshing.hpp"
+
+namespace netgen
+{
+
+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()
+{
+  int i, j, k;
+
+  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();
+
+  PrintMessage (3, "Update mesh topology");
+
+  (*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;
+
+  (*testout) << "tables deleted" << endl;
+
+  /*
+    generate:
+    vertex to element 
+    vertex to surface element
+    vertex to segment 
+   */
+
+  cnt = 0;
+  for (i = 1; i <= ne; i++)
+    {
+      const Element & el = mesh.VolumeElement(i);
+      int nelv = el.GetNV();
+      for (j = 1; j <= nelv; j++)
+	cnt[el.PNum(j)]++;
+    }
+
+  vert2element = new TABLE<int,PointIndex::BASE> (cnt);
+  for (i = 1; i <= ne; i++)
+    {
+      const Element & el = mesh.VolumeElement(i);
+      int nelv = el.GetNV();
+      for (j = 1; j <= nelv; j++)
+	vert2element->AddSave (el.PNum(j), i);
+    }
+
+  cnt = 0;
+  for (SurfaceElementIndex sei = 0; sei < nse; sei++)
+    {
+      const Element2d & el = mesh[sei];
+      int nelv = el.GetNV();
+      for (j = 0; j < nelv; j++)
+	cnt[el[j]]++;
+    }
+
+  vert2surfelement = new TABLE<int,PointIndex::BASE> (cnt);
+  for (SurfaceElementIndex sei = 0; sei < nse; sei++)
+    {
+      const Element2d & el = mesh[sei];
+      int nelv = el.GetNV();
+      for (j = 0; j < nelv; j++)
+	vert2surfelement->AddSave (el[j], sei+1);
+    }
+
+
+  cnt = 0;
+  for (i = 1; i <= nseg; i++)
+    {
+      const Segment & seg = mesh.LineSegment(i);
+      cnt[seg.p1]++;
+      cnt[seg.p2]++;
+    }
+
+  vert2segment = new TABLE<int,PointIndex::BASE> (cnt);
+  for (i = 1; i <= nseg; i++)
+    {
+      const Segment & seg = mesh.LineSegment(i);
+      vert2segment->AddSave (seg.p1, i);
+      vert2segment->AddSave (seg.p2, i);
+    }
+
+
+
+
+  if (buildedges)
+    {
+      PrintMessage (5, "Update edges ");
+      
+      edges.SetSize(ne);
+      surfedges.SetSize(nse); 
+      segedges.SetSize(nseg);
+
+      for (i = 1; i <= ne; i++)
+	for (j = 0; j < 12; j++)
+	  edges.Elem(i)[j] = 0;
+      for (i = 1; i <= nse; i++)
+	for (j = 0; j < 4; j++)
+	  surfedges.Elem(i)[j] = 0;
+
+      // keep existing edges
+      cnt = 0;
+      for (i = 0; i < edge2vert.Size(); i++)
+	cnt[edge2vert[i][0]]++;
+      TABLE<int,PointIndex::BASE> vert2edge (cnt);
+      for (i = 0; i < edge2vert.Size(); i++)
+	vert2edge.AddSave (edge2vert[i][0], i+1);
+
+      // ensure all coarse grid and intermediate level edges
+      cnt = 0;
+      for (i = 1; i <= mesh.mlbetweennodes.Size(); i++)
+	{
+	  int pa[2];
+	  pa[0] = mesh.mlbetweennodes.Get(i).I1();
+	  pa[1] = mesh.mlbetweennodes.Get(i).I2();
+	  if (pa[0] > pa[1]) Swap (pa[0], pa[1]);
+	  if (pa[0] > 0)
+	    cnt.Elem(pa[0])++;
+	}
+      TABLE<int,PointIndex::BASE> vert2vertcoarse (cnt);
+      for (i = 1; i <= mesh.mlbetweennodes.Size(); i++)
+	{
+	  int pa[2];
+	  pa[0] = mesh.mlbetweennodes.Get(i).I1();
+	  pa[1] = mesh.mlbetweennodes.Get(i).I2();
+	  if (pa[0] > pa[1]) swap (pa[0], pa[1]);
+	  if (pa[0] > 0)
+	    vert2vertcoarse.AddSave1 (pa[0], pa[1]);
+	}
+
+
+      ARRAY<int,PointIndex::BASE> edgenr(nv), edgeflag(nv);
+      for (i = PointIndex::BASE; i < nv+PointIndex::BASE; i++)
+	edgeflag[i] = 0;
+      ned = edge2vert.Size();
+      ARRAY<INDEX_3> missing;
+
+      for (i = 1; i <= nv; i++)
+	{
+	  for (j = 1; j <= vert2edge.EntrySize(i); j++)
+	    {
+	      int ednr = vert2edge.Get(i,j);
+	      int i2 = edge2vert.Get(ednr)[1];
+	      edgeflag.Elem(i2) = i;
+	      edgenr.Elem(i2) = ednr;
+	    }
+	  for (j = 1; j <= vert2vertcoarse.EntrySize(i); j++)
+	    {
+	      int v2 = vert2vertcoarse.Get(i,j);
+	      if (edgeflag.Get(v2) < i)
+		{
+		  ned++;
+		  edgenr.Elem(v2) = ned;
+		  edgeflag.Elem(v2) = i;
+		  missing.Append (INDEX_3(i,v2,ned));
+		}
+	    }
+
+	  for (j = 1; j <= vert2element->EntrySize(i); j++)
+	    {
+	      int elnr = vert2element->Get(i,j);
+	      const Element & el = mesh.VolumeElement (elnr);
+
+	      int neledges = GetNEdges (el.GetType());
+	      const ELEMENT_EDGE * eledges = GetEdges (el.GetType());
+	  
+	      for (k = 0; k < neledges; k++)
+		{
+		  INDEX_2 edge(el.PNum(eledges[k][0]),
+			       el.PNum(eledges[k][1]));
+	      
+		  int edgedir = (edge.I1() > edge.I2());
+		  if (edgedir) swap (edge.I1(), edge.I2());
+	     
+		  if (edge.I1() != i)
+		    continue;
+	     
+		  if (edgeflag.Get (edge.I2()) < i)
+		    {
+		      ned++;
+		      edgenr.Elem(edge.I2()) = ned;
+		      edgeflag.Elem(edge.I2()) = i;
+		    }
+
+		  int edgenum = edgenr.Elem(edge.I2());
+		  if (edgedir) edgenum *= -1;
+		  edges.Elem(elnr)[k] = edgenum;
+		}
+	    }
+
+	  for (j = 1; j <= vert2surfelement->EntrySize(i); j++)
+	    {
+	      int elnr = vert2surfelement->Get(i,j);
+	      const Element2d & el = mesh.SurfaceElement (elnr);
+
+	      int neledges = GetNEdges (el.GetType());
+	      const ELEMENT_EDGE * eledges = GetEdges (el.GetType());
+	  
+	      for (k = 0; k < neledges; k++)
+		{
+		  INDEX_2 edge(el.PNum(eledges[k][0]),
+			       el.PNum(eledges[k][1]));
+	      
+		  int edgedir = (edge.I1() > edge.I2());
+		  if (edgedir) swap (edge.I1(), edge.I2());
+	     
+		  if (edge.I1() != i)
+		    continue;
+	     
+		  if (edgeflag.Get (edge.I2()) < i)
+		    {
+		      ned++;
+		      edgenr.Elem(edge.I2()) = ned;
+		      edgeflag.Elem(edge.I2()) = i;
+		    }
+	      
+		  int edgenum = edgenr.Elem(edge.I2());
+		  if (edgedir) edgenum *= -1;
+		  surfedges.Elem(elnr)[k] = edgenum;
+		}
+	    }
+
+	  for (j = 1; j <= vert2segment->EntrySize(i); j++)
+	    {
+	      int elnr = vert2segment->Get(i,j);
+	      const Segment & el = mesh.LineSegment (elnr);
+
+	      INDEX_2 edge(el.p1, el.p2);
+	      
+	      int edgedir = (edge.I1() > edge.I2());
+	      if (edgedir) swap (edge.I1(), edge.I2());
+	      
+	      if (edge.I1() != i)
+		continue;
+	     
+	      if (edgeflag.Get (edge.I2()) < i)
+		{
+		  ned++;
+		  edgenr.Elem(edge.I2()) = ned;
+		  edgeflag.Elem(edge.I2()) = i;
+		}   
+ 	      int edgenum = edgenr.Elem(edge.I2());
+
+	      if (edgedir) edgenum *= -1;
+	      segedges.Elem(elnr) = edgenum;
+	    }
+	}
+
+
+      edge2vert.SetSize (ned);
+      for (i = 1; i <= ne; i++)
+	{
+	  const Element & el = mesh.VolumeElement (i);
+      
+	  int neledges = GetNEdges (el.GetType());
+	  const ELEMENT_EDGE * eledges = GetEdges (el.GetType());
+	  
+	  for (k = 0; k < neledges; k++)
+	    {
+	      INDEX_2 edge(el.PNum(eledges[k][0]),
+			   el.PNum(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();
+	    }
+	}
+      for (i = 1; i <= nse; i++)
+	{
+	  const Element2d & el = mesh.SurfaceElement (i);
+      
+	  int neledges = GetNEdges (el.GetType());
+	  const ELEMENT_EDGE * eledges = GetEdges (el.GetType());
+	  
+	  for (k = 0; k < neledges; k++)
+	    {
+	      INDEX_2 edge(el.PNum(eledges[k][0]),
+			   el.PNum(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 (i = 1; i <= nseg; i++)
+	{
+	  const Segment & el = mesh.LineSegment (i);
+      
+	  INDEX_2 edge(el.p1, el.p2);
+	  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 (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 (i = 1; i <= edge2vert.Size(); i++)
+	  (*testout) << "edge " << i << ", v1,2 = " << edge2vert.Elem(i)[0] << ", " << edge2vert.Elem(i)[1] << endl;
+      (*testout) << "surfedges:" << endl;
+      for (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;
+
+#ifdef OLD
+  if (buildedges == 2)
+    { // old style with hash-table
+      edges.SetSize(ne);
+      surfedges.SetSize(nse); 
+      INDEX_2_HASHTABLE<int> vert2edge(ne+nse+1);
+      
+      // keep coarse grid edges
+      for (i = 1; i <= ned; i++)
+	{
+	  INDEX_2 edge (edge2vert.Get(i)[0],
+			edge2vert.Get(i)[1]);
+	  edge.Sort ();
+	  vert2edge.Set (edge, i);
+	}
+      
+      
+      
+      for (i = 1; i <= ne; i++)
+	{
+	  const Element & el = mesh.VolumeElement(i);
+	  
+	  int neledges = GetNEdges (el.GetType());
+	  const ELEMENT_EDGE * eledges = GetEdges (el.GetType());
+	  
+	  for (j = 0; j < 12; j++)
+	    edges.Elem(i)[j] = 0;
+	  for (j = 0; j < neledges; j++)
+	    {
+	      int edgenum;
+	      int edgedir;
+	      
+	      INDEX_2 edge(el.PNum(eledges[j][0]),
+			   el.PNum(eledges[j][1]));
+	      
+	      edgedir = (edge.I1() > edge.I2());
+	      if (edgedir) swap (edge.I1(), edge.I2());
+	      
+	      if (vert2edge.Used (edge))
+		edgenum = vert2edge.Get(edge);
+	      else
+		{
+		  ned++;
+		  vert2edge.Set (edge, ned);
+		  edgenum = ned;
+		  /*
+		  edge2vert.SetSize(edge2vert.Size()+1);
+		  edge2vert.Last()[0] = edge.I1();
+		  edge2vert.Last()[1] = edge.I2();
+		  */
+		  edge2vert.Append (edge);
+		}
+	      
+	      if (edgedir) edgenum *= -1;
+	      edges.Elem(i)[j] = edgenum;
+	    }
+	}
+      
+      for (i = 1; i <= nse; i++)
+	{
+	  const Element2d & el = mesh.SurfaceElement(i);
+	  
+	  int neledges = GetNEdges (el.GetType());
+	  const ELEMENT_EDGE * eledges = GetEdges (el.GetType());
+	  
+	  for (j = 0; j < 4; j++)
+	    surfedges.Elem(i)[j] = 0;
+	  for (j = 0; j < neledges; j++)
+	    {
+	      int edgenum;
+	      int edgedir;
+	      
+	      INDEX_2 edge(el.PNum(eledges[j][0]),
+			   el.PNum(eledges[j][1]));
+	      
+	      edgedir = (edge.I1() > edge.I2());
+	      if (edgedir) swap (edge.I1(), edge.I2());
+	      
+	      if (vert2edge.Used (edge))
+		edgenum = vert2edge.Get(edge);
+	      else
+		{
+		  ned++;
+		  vert2edge.Set (edge, ned);
+		  edgenum = ned;
+		  
+		  /*
+		  edge2vert.SetSize(edge2vert.Size()+1);
+		  edge2vert.Last()[0] = edge.I1();
+		  edge2vert.Last()[1] = edge.I2();
+		  */
+		  edge2vert.Append (edge);
+		  if (mesh.GetDimension() == 3 && mesh.GetNE())
+		    cerr << "surface edge: should be in use: "
+			 << edge.I1() << "-" << edge.I2() << endl;
+		}
+	      
+	      if (edgedir) edgenum *= -1;
+	      surfedges.Elem(i)[j] = edgenum;
+	    }
+	}
+      
+      
+      // find edges only in intermediate level
+      
+      for (i = 1; i <= np; i++)
+	{
+	  int parents[2];
+	  
+	  if (i <= mesh.mlbetweennodes.Size())
+	    {
+	      parents[0] = mesh.mlbetweennodes.Get(i).I1();
+	      parents[1] = mesh.mlbetweennodes.Get(i).I2();
+	    }
+	  else
+	    parents[0] = parents[1] = 0;
+	  
+	  if (parents[0] && parents[1])
+	    {
+	      INDEX_2 edge(parents[0], parents[1]);
+	      int edgedir = (edge.I1() > edge.I2());
+	      if (edgedir) swap (edge.I1(), edge.I2());
+	      
+	      if (!vert2edge.Used (edge))
+		{
+		  ned++;
+		  vert2edge.Set (edge, ned);
+		  /*
+		  edge2vert.SetSize(edge2vert.Size()+1);
+		  edge2vert.Last()[0] = edge.I1();
+		  edge2vert.Last()[1] = edge.I2();
+		  */
+		  edge2vert.Append (edge);
+		}
+	    }
+	}
+      
+      /*
+	cout << "edge2vert: ";
+	edge2vert.PrintMemInfo(cout);
+	cout << "edges: ";
+	edges.PrintMemInfo(cout);
+	cout << "hashtable: ";
+	vert2edge.PrintMemInfo(cout);
+      */
+
+      if (mesh.GetDimension() == 2)
+	{
+	  surffaces.SetSize(mesh.GetNSeg());
+	  for (i = 1; i <= mesh.GetNSeg(); i++)
+	    {
+	      const Segment & seg = mesh.LineSegment (i);
+
+	      INDEX_2 edge(seg.p1, seg.p2);
+	      int edgenum;
+	      int edgedir = (edge.I1() > edge.I2());
+	      if (edgedir) swap (edge.I1(), edge.I2());
+	      
+	      if (vert2edge.Used (edge))
+		edgenum = vert2edge.Get(edge);
+	      else
+		{
+		  ned++;
+		  vert2edge.Set (edge, ned);
+		  edgenum = ned;
+		  
+		  /*
+		  edge2vert.SetSize(edge2vert.Size()+1);
+		  edge2vert.Last()[0] = edge.I1();
+		  edge2vert.Last()[1] = edge.I2();
+		  */
+		  edge2vert.Append (edge);
+		}
+	      
+
+	      if (edgedir) edgenum *= -1;
+	      surffaces.Elem(i) = edgenum;
+	    }
+	}
+    }
+
+#endif  
+  
+
+  // generate faces
+  if (buildfaces) //  && mesh.GetDimension() == 3)
+    {
+      PrintMessage (5, "Update faces ");
+
+      faces.SetSize(ne);
+      surffaces.SetSize(nse);
+      
+      // face2vert.SetSize(0);  // keep old faces
+      nfa = face2vert.Size();
+      // INDEX_3_HASHTABLE<int> vert2face(ne+nse+1);
+      INDEX_3_CLOSED_HASHTABLE<int> vert2face(8*ne+2*nse+nfa+2);
+
+      for (i = 1; i <= face2vert.Size(); i++)
+	{
+	  INDEX_3 f;
+	  f.I1() = face2vert.Get(i)[0];
+	  f.I2() = face2vert.Get(i)[1];
+	  f.I3() = face2vert.Get(i)[2];
+	  vert2face.Set (f, i);
+	}
+      
+      for (i = 1; i <= ne; i++)
+	{
+	  const Element & el = mesh.VolumeElement (i);
+	  
+	  int nelfaces = GetNFaces (el.GetType());
+	  const ELEMENT_FACE * elfaces = GetFaces (el.GetType());
+	  
+	  for (j = 0; j < 6; j++)
+	    faces.Elem(i)[j] = 0;
+	  for (j = 0; j < nelfaces; j++)
+	    if (elfaces[j][3] == 0)
+	      
+	      { // triangle
+		
+		int facenum;
+		int 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 (vert2face.Used (face))
+		  facenum = vert2face.Get(face);
+		else
+		  {
+		    nfa++;
+		    vert2face.Set (face, nfa);
+		    facenum = nfa;
+		    
+		    INDEX_4 hface;
+		    face2vert.Append (hface);
+		    // face2vert.SetSize(face2vert.Size()+1);
+		    face2vert.Last()[0] = face.I1();
+		    face2vert.Last()[1] = face.I2();
+		    face2vert.Last()[2] = face.I3();
+		    face2vert.Last()[3] = 0;
+
+		  }
+		
+		faces.Elem(i)[j] = 8*(facenum-1)+facedir+1;
+	      }
+	  
+	    else
+	      
+	      {
+		// quad
+		int facenum;
+		int 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());
+		  }
+		//		face4.Sort();
+		
+		INDEX_3 face(face4.I1(), face4.I2(), face4.I3());
+		
+		if (vert2face.Used (face))
+		  {
+		    facenum = vert2face.Get(face);
+		  }
+		else
+		  {
+		    nfa++;
+		    vert2face.Set (face, nfa);
+		    facenum = nfa;
+
+		    // face2vert.SetSize(face2vert.Size()+1);
+
+		    INDEX_4 hface;
+		    face2vert.Append (hface);
+		    face2vert.Last()[0] = face4.I1();
+		    face2vert.Last()[1] = face4.I2();
+		    face2vert.Last()[2] = face4.I3();
+		    face2vert.Last()[3] = face4.I4();
+
+		  }
+		
+		faces.Elem(i)[j] = 8*(facenum-1)+facedir+1;
+	      }
+	}
+
+      face2surfel.SetSize(nfa+nse);
+      for (i = 1; i <= face2surfel.Size(); i++)
+	face2surfel.Elem(i) = 0;
+
+      for (i = 1; i <= nse; i++)
+	{
+	  const Element2d & el = mesh.SurfaceElement (i);
+	  
+	  const ELEMENT_FACE * elfaces = GetFaces (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]));
+	      
+	      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 (vert2face.Used (face))
+		facenum = vert2face.Get(face);
+	      else
+		{
+		  nfa++;
+		  vert2face.Set (face, nfa);
+		  facenum = nfa;
+		  
+		  // face2vert.SetSize(face2vert.Size()+1);
+		  INDEX_4 hface;
+		  face2vert.Append (hface);
+		  face2vert.Last()[0] = face.I1();
+		  face2vert.Last()[1] = face.I2();
+		  face2vert.Last()[2] = face.I3();
+		  face2vert.Last()[3] = 0;
+		}
+	      
+	      surffaces.Elem(i) = 8*(facenum-1)+facedir+1;
+	      face2surfel.Elem(facenum) = i;
+	    }
+	  
+	  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 (vert2face.Used (face))
+		facenum = vert2face.Get(face);
+	      else
+		{
+		  nfa++;
+		  vert2face.Set (face, nfa);
+		  facenum = nfa;
+		  
+		  // face2vert.SetSize(face2vert.Size()+1);
+		  INDEX_4 hface;
+		  face2vert.Append (hface);
+		  face2vert.Last()[0] = face4.I1();
+		  face2vert.Last()[1] = face4.I2();
+		  face2vert.Last()[2] = face4.I3();
+		  face2vert.Last()[3] = face4.I3();
+		}
+	      
+	      surffaces.Elem(i) = 8*(facenum-1)+facedir+1;
+	      face2surfel.Elem(facenum) = i;
+	    }
+	}
+
+      surf2volelement.SetSize (nse);
+      for (i = 1; i <= nse; i++)
+	{
+	  surf2volelement.Elem(i)[0] = 0;
+	  surf2volelement.Elem(i)[1] = 0;
+	}
+      for (i = 1; i <= ne; i++)
+	for (j = 0; j < 6; j++)
+	  {
+	    int fnum = (faces.Get(i)[j]-1) / 8 + 1;
+	    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());
+
+      /*
+	cout << "face2vert: ";
+	face2vert.PrintMemInfo(cout);
+	cout << "faces: ";
+	faces.PrintMemInfo(cout);
+	cout << "hashtable: ";
+	vert2face.PrintMemInfo(cout);
+      */
+
+
+      ARRAY<char> face_els(nfa), face_surfels(nfa);
+      face_els = 0;
+      face_surfels = 0;
+      ARRAY<int> hfaces;
+      for (i = 1; i <= ne; i++)
+	{
+	  GetElementFaces (i, hfaces);
+	  for (j = 0; j < hfaces.Size(); j++)
+	    face_els[hfaces[j]-1]++;
+	}
+      for (i = 1; i <= nse; i++)
+	face_surfels[GetSurfaceElementFace (i)-1]++;
+
+      if (ne)
+	{
+	  int cnt_err = 0;
+	  for (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++;
+		  (*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;
+		}
+	    }
+	  if (cnt_err)
+	    cout << cnt_err << " elements are not matching !!!" << endl;
+	}
+    }
+  
+  /*
+    for (i = 1; i <= ne; i++)
+    {
+    (*testout) << "Element " << i << endl;
+    (*testout) << "edges: " << endl;
+    for (j = 0; j < 9; j++)
+    (*testout) << edges.Elem(i)[j] << " ";
+    (*testout) << "faces: " << endl;
+    for (j = 0; j < 6; j++)
+    (*testout) << faces.Elem(i)[j] << " ";
+    }
+  */
+  timestamp = NextTimeStamp();
+}
+
+ 
+
+
+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 :: 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 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;
+}
+
+
+
+
+const ELEMENT_EDGE * MeshTopology :: GetEdges (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_FACE * MeshTopology :: GetFaces (ELEMENT_TYPE et)
+{
+  static int trig_faces[1][4] = 
+    { { 1, 2, 3, 0 } };
+  static int quad_faces[1][4] = 
+    { { 1, 2, 3, 4 } };
+
+  static int tet_faces[4][4] =
+    { { 4, 2, 3, 0 },
+      { 4, 3, 1, 0 },
+      { 4, 1, 2, 0 },
+      { 1, 3, 2, 0 } };
+  
+  static 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 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 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;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+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) const
+{
+  int i;
+  int nfa = GetNFaces (mesh.VolumeElement(elnr).GetType());
+  elfaces.SetSize (nfa);
+  for (i = 1; i <= nfa; i++)
+    elfaces.Elem(i) = (faces.Get(elnr)[i-1]-1) / 8 + 1;
+}
+
+void MeshTopology :: GetElementEdgeOrientations (int elnr, ARRAY<int> & eorient) const
+{
+  int i;
+  int ned = GetNEdges (mesh.VolumeElement(elnr).GetType());
+  eorient.SetSize (ned);
+  for (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 i;
+  int nfa = GetNFaces (mesh.VolumeElement(elnr).GetType());
+  forient.SetSize (nfa);
+  for (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 i;
+  //  int ned = GetNEdges (mesh.VolumeElement(elnr).GetType());
+
+  if (mesh.GetDimension()==3 || 1)
+    {
+      if (orient)
+	{
+	  for (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 (i = 0; i < 12; i++)
+	    {
+	      if (!edges.Get(elnr)[i]) return i;
+	      eledges[i] = abs (edges.Get(elnr)[i]);
+	    }
+	}
+      return 12;
+    }
+  else
+    {
+      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 i;
+  //  int nfa = GetNFaces (mesh.VolumeElement(elnr).GetType());
+  if (orient)
+    {
+      for (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 (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> & edges) const
+{
+  ArrayMem<int,4> pi(4);
+  ArrayMem<int,12> eledges;
+
+  edges.SetSize (0);
+  GetFaceVertices (fnr, pi);
+
+  //  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 cntv = 0;
+      for (int j = 0; j < el.GetNV(); j++)
+	for (int k = 0; k < pi.Size(); k++)
+	  if (el[j] == pi[k])
+	    cntv++;
+
+      if (cntv == pi.Size())
+	{
+	  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)
+		edges.Append (eledges[j]);
+	    }
+
+          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);
+    }
+}
+
+}
diff --git a/contrib/Netgen/libsrc/meshing/topology.hpp b/contrib/Netgen/libsrc/meshing/topology.hpp
new file mode 100644
index 0000000000..3c5ac1397f
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/topology.hpp
@@ -0,0 +1,113 @@
+#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;
+  int buildedges;
+  int 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:
+  MeshTopology (const Mesh & amesh);
+  ~MeshTopology ();
+
+  void SetBuildEdges (int be)
+  { buildedges = be; }
+  void SetBuildFaces (int bf)
+  { buildfaces = bf; }
+
+  int HasEdges () const
+  { return buildedges; }
+  int HasFaces () const
+  { return buildedges; }
+
+  void Update();
+
+
+  int GetNEdges () const
+  { return edge2vert.Size(); }
+  int GetNFaces () const
+  { return face2vert.Size(); }
+
+  static int GetNVertices (ELEMENT_TYPE et);
+  static int GetNEdges (ELEMENT_TYPE et);
+  static int GetNFaces (ELEMENT_TYPE et);
+
+  static const Point3d * GetVertices (ELEMENT_TYPE et);
+  static const ELEMENT_EDGE * GetEdges (ELEMENT_TYPE et);
+  static const ELEMENT_FACE * GetFaces (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) 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 fnr, int & v1, int & v2) const;
+  void GetFaceEdges (int fnr, ARRAY<int> & edges) 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;
+
+  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;
+};
+
+#endif
diff --git a/contrib/Netgen/libsrc/meshing/triarls.cpp b/contrib/Netgen/libsrc/meshing/triarls.cpp
new file mode 100644
index 0000000000..923763306d
--- /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 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/zrefine.cpp b/contrib/Netgen/libsrc/meshing/zrefine.cpp
new file mode 100644
index 0000000000..7b4d4804a9
--- /dev/null
+++ b/contrib/Netgen/libsrc/meshing/zrefine.cpp
@@ -0,0 +1,735 @@
+#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)
+  {
+    int i, j;
+
+    // edges selected in csg input file
+    for (i = 1; i <= geom.singedges.Size(); i++)
+      {
+	const SingularEdge & se = *geom.singedges.Get(i);
+	for (j = 1; j <= se.segms.Size(); j++)
+	  {
+	    INDEX_2 i2 = se.segms.Get(j);
+	    singedges.Set (i2, 1);
+	  }
+      }
+
+    // edges interactively selected
+    for (i = 1; i <= mesh.GetNSeg(); i++)
+      {
+	const Segment & seg = mesh.LineSegment(i);
+	if (seg.singedge_left || seg.singedge_right)
+	  {
+	    INDEX_2 i2(seg.p1, seg.p2);
+	    i2.Sort();
+	    singedges.Set (i2, 1);
+	  }
+      }
+  }
+
+
+  /**
+     Convert elements (vol-tets, surf-trigs) into prisms/quads
+  */
+  void MakePrismsSingEdge (Mesh & mesh, INDEX_2_HASHTABLE<int> & singedges)
+  {
+    int i, j, k;
+
+    // volume elements
+    for (i = 1; i <= mesh.GetNE(); i++)
+      {
+	Element & el = mesh.VolumeElement(i);
+	if (el.GetType() != TET) continue;
+
+	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 (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 (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 (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).p1 :
+	      mesh.LineSegment(i).p2;
+	    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, k;
+    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;
+	      
+		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();
+		    double slicefac = slices.Get(slicenr);
+		    double slicefaclast = 
+		      (slicenr == slices.Size()) ? 1 : slices.Get(slicenr+1);
+		    
+		    Point3d np = p1 + (slicefac / slicefaclast) * (p2-p1);
+		    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.p1, el.p2);
+	    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.p1),
+		// 				  mesh.Point (el.p2),
+		// 				  el.surfnr1, el.surfnr2,
+		// 				  el.epgeominfo[0], el.epgeominfo[1],
+		// 				  pb, ngi);
+		// 	      */
+		// 	      pb = Center (mesh.Point (el.p1), mesh.Point (el.p2));
+
+		// 	      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.p2 = pnew;
+	    ns1.epgeominfo[1] = ngi;
+	    ns2.p1 = 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 CSGeometry * geom,
+		    ZRefinementOptions & opt)
+  {
+    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 b/contrib/Netgen/libsrc/occ/Makefile
new file mode 100644
index 0000000000..6fe1d8bc0d
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for open cascade library
+#
+src =  occgeom.cpp occmeshsurf.cpp occgenmesh.cpp
+
+lib = occ
+libpath = libsrc/occ
+#
+#
+include ../makefile.inc
+
diff --git a/contrib/Netgen/libsrc/occ/occgenmesh.cpp b/contrib/Netgen/libsrc/occ/occgenmesh.cpp
new file mode 100644
index 0000000000..822edf3681
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/occgenmesh.cpp
@@ -0,0 +1,1245 @@
+#ifdef OCCGEOMETRY
+
+#include <mystdlib.h>
+#include <occgeom.hpp>
+#include <meshing.hpp>
+
+#include <stlgeom.hpp>
+
+
+namespace netgen
+{
+
+#include "occmeshsurf.hpp"
+
+#define TCL_OK 0
+#define TCL_ERROR 1
+
+extern STLParameters stlparam;
+
+#define DIVIDEEDGESECTIONS 1000
+#define IGNORECURVELENGTH 1e-4
+
+ 
+void DivideEdge (TopoDS_Edge & edge,
+		 ARRAY<MeshPoint> & ps,
+		 ARRAY<double> & params,
+		 Mesh & mesh)
+{
+  double s0, s1;
+  int j;
+  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;
+
+  for (int i = 1; i <= DIVIDEEDGESECTIONS; 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);
+
+      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;
+    }
+}
+ 
+
+
+
+static void FindEdges (OCCGeometry & geom, Mesh & mesh)
+{
+  int i, j;
+  
+  char * savetask = multithread.task;
+  multithread.task = "Edge meshing";
+
+  (*testout) << "edge meshing" << endl;
+
+  TopExp_Explorer exp0, exp01, exp1, exp2, exp3;
+  
+  int nvertices = geom.vmap.Extent();
+  int nedges = geom.emap.Extent();
+  for (i = 1; i <= nvertices; i++)
+    {
+      gp_Pnt pnt = BRep_Tool::Pnt (TopoDS::Vertex(geom.vmap(i)));
+      MeshPoint mp( Point3d(pnt.X(), pnt.Y(), pnt.Z()) );
+
+      /*
+      int exists = 0;
+			
+      for (j = 1; !exists && (j <= mesh.GetNP()); j++)
+	if ((mesh.Point(j)-Point<3>(mp)).Length() < 1e-6)
+	  exists = 1;
+      
+      if (!exists)
+      */
+
+      mesh.AddPoint (mp);
+    }
+  
+  int facenr = 0;
+  int edgenr = 0;
+  
+  int total = 0;
+  int solidnr = 0;
+
+  ARRAY<int> face2solid;
+  face2solid.SetSize (geom.fmap.Extent());
+  face2solid = 0;
+
+  /*
+  for (exp0.Init(geom.shape, TopAbs_SOLID); exp0.More(); exp0.Next())
+    {
+      solidnr++;
+      for (exp01.Init(exp0.Current(), TopAbs_SHELL); exp01.More(); exp01.Next())
+	{
+	  TopoDS_Shape shell = exp01.Current();
+	  for (exp1.Init(shell, TopAbs_FACE); exp1.More(); exp1.Next())
+	    {
+	      TopoDS_Face face = TopoDS::Face(exp1.Current());
+	      facenr = geom.fmap.FindIndex(face);
+	      face2solid[facenr-1] = solidnr;
+	    
+	      for (exp2.Init (face, TopAbs_WIRE); exp2.More(); exp2.Next())
+		{
+		  TopoDS_Shape wire = exp2.Current();
+		  
+		  for (exp3.Init (wire, TopAbs_EDGE); exp3.More(); exp3.Next())
+		    {
+		      total++;
+		    }
+		}
+	    }
+	  }
+      }
+  */
+
+  for (exp0.Init(geom.shape, TopAbs_SOLID); exp0.More(); exp0.Next())
+    {
+      solidnr++;
+      for (exp1.Init(exp0.Current(), TopAbs_FACE); exp1.More(); exp1.Next())
+	{
+	  TopoDS_Face face = TopoDS::Face(exp1.Current());
+	  facenr = geom.fmap.FindIndex(face);
+	  face2solid[facenr-1] = solidnr;
+	}
+      }
+
+
+  for (int i3 = 1; i3 <= geom.fmap.Extent(); i3++)
+    for (exp2.Init (geom.fmap(i3), TopAbs_WIRE); exp2.More(); exp2.Next())
+      for (exp3.Init (exp2.Current(), TopAbs_EDGE); exp3.More(); exp3.Next())
+	total++;
+	   
+
+  int curr = 0;
+
+  
+  solidnr = 0;
+  /*
+  for (exp0.Init(geom.shape, TopAbs_SOLID); exp0.More(); exp0.Next())
+    {
+      solidnr++;
+      for (exp01.Init(exp0.Current(), TopAbs_SHELL); exp01.More(); exp01.Next())
+	{
+	  TopoDS_Shape shell = exp01.Current();
+	  
+	  for (exp1.Init(shell, TopAbs_FACE); exp1.More(); exp1.Next())
+	    {
+	      
+	      TopoDS_Face face = TopoDS::Face(exp1.Current());
+  */
+	      
+  for (int i3 = 1; i3 <= geom.fmap.Extent(); i3++)
+    
+      
+	{
+	  {
+	    {
+	      TopoDS_Face face = TopoDS::Face(geom.fmap(i3));
+	      solidnr = face2solid[i3-1];
+	      
+	      
+	      facenr = geom.fmap.FindIndex (face);
+	      
+	      mesh.AddFaceDescriptor (FaceDescriptor(facenr, solidnr, 0, 0));
+	      Handle(Geom_Surface) occface = BRep_Tool::Surface(face);
+	      
+	      for (exp2.Init (face, TopAbs_WIRE); exp2.More(); exp2.Next())
+		{
+		  TopoDS_Shape wire = exp2.Current();
+		  
+		  for (exp3.Init (wire, TopAbs_EDGE); exp3.More(); exp3.Next())
+		    {
+		      curr++;
+ 
+		      multithread.percent = 100 * curr / (double) total;
+		      if (multithread.terminate) return;
+
+		      TopoDS_Edge edge = TopoDS::Edge (exp3.Current());
+		      if (BRep_Tool::Degenerated(edge)) continue;
+
+		      if (geom.vmap.FindIndex(TopExp::FirstVertex (edge)) == 
+			  geom.vmap.FindIndex(TopExp::LastVertex (edge)))
+			{
+			  GProp_GProps system;
+			  BRepGProp::LinearProperties(edge, system);
+
+			  if (system.Mass() < 1e-5)
+			    {
+			      cout << "ignoring edge " << geom.emap.FindIndex (edge)
+				   << ". closed edge with length < 1e-5" << 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);
+		    
+		      pnums[0] = geom.vmap.FindIndex (TopExp::FirstVertex (edge));
+		      pnums[pnums.Size()-1] = geom.vmap.FindIndex (TopExp::LastVertex (edge));
+
+		      for (i = 1; i <= mp.Size(); i++)
+			{
+			  int exists = 0;
+			
+			  for (j = 1; !exists && (j <= mesh.GetNP()-nvertices); j++)
+			    if ((mesh.Point(nvertices+j)-Point<3>(mp[i-1])).Length() < 1e-6) exists = 1;
+
+			  if (exists)
+			    {
+			      pnums[i] = nvertices+j-1;
+			    }
+			  else
+			    {
+			      mesh.AddPoint (mp[i-1]);
+			      pnums[i] = mesh.GetNP();
+			    }
+			}
+		    
+		      for (i = 1; i <= mp.Size()+1; i++)
+			{
+			  edgenr++;
+			  Segment seg;
+			
+			  seg.p1 = pnums[i-1];
+			  seg.p2 = 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.p1, seg.p2);
+			      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);
+
+			}
+		    }
+		}
+	    }
+	}
+    }
+  mesh.CalcSurfacesOfNode();
+  multithread.task = savetask;
+}  
+
+
+
+
+static void OCCMeshSurface (OCCGeometry & geom, Mesh & mesh, int perfstepsend)
+{
+  int i, j, k;
+  int changed;
+
+  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 = PLANESPACE;
+
+  int notrys = 1;
+
+  int surfmesherror = 0;
+
+  for (k = 1; k <= mesh.GetNFD(); k++)
+    {
+      (*testout) << "mesh face " << k << endl;
+      multithread.percent = 100 * k / (mesh.GetNFD()+1e-10);
+      geom.facemeshstatus[k-1] = -1;
+
+      
+      /*
+      if (k != 138)
+	{
+	  cout << "skipped" << endl;
+	  continue;
+	}
+      */
+    
+      FaceDescriptor & fd = mesh.GetFaceDescriptor(k);
+
+      int oldnf = mesh.GetNSE();
+
+      Box<3> bb = geom.GetBoundingBox();
+ 
+      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.p1 : seg.p2;
+		      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.p1), glob2loc.Get(seg.p2), 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.p1 : seg.p2;
+			  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;
+	      
+		}
+	    }
+	}
+
+
+
+
+
+
+      double maxh = mparam.maxh;
+      mparam.checkoverlap = 0;
+      //      int noldpoints = mesh->GetNP();
+      int noldsurfel = mesh.GetNSE();
+
+      MESHING2_RESULT res;
+
+      try {
+        res = meshing.GenerateMesh (mesh, 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);
+
+    }
+
+  if (surfmesherror)
+    {
+      cout << "WARNING! NOT ALL FACES HAVED 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;
+      cout << endl;
+      cout << "for more information open IGES/STEP Topology Explorer" << endl;
+      throw NgException ("Problem in Surface mesh generation");
+    }
+
+
+  if (multithread.terminate || perfstepsend < MESHCONST_OPTSURFACE)
+    return;
+
+  multithread.task = "Optimizing surface";
+  
+  for (k = 1; k <= mesh.GetNFD(); k++)
+    {
+      (*testout) << "optimize face " << k << endl;
+      multithread.percent = 100 * k / (mesh.GetNFD()+1e-10);
+      
+      FaceDescriptor & fd = mesh.GetFaceDescriptor(k);
+      
+      PrintMessage (1, "Optimize Surface ", k);
+      for (i = 1; i <= mparam.optsteps2d; i++)
+	{
+	  if (multithread.terminate) return;
+	  
+	  {
+	    MeshOptimize2dOCCSurfaces meshopt(geom);
+	    meshopt.SetFaceIndex (k);
+	    meshopt.SetImproveEdges (0);
+	    meshopt.SetMetricWeight (0.2);
+	    meshopt.SetWriteStatus (0);
+	    
+	    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.SetWriteStatus (0);
+	    
+	    meshopt.ImproveMesh (mesh);
+	  }
+	  
+	  {
+	    MeshOptimize2dOCCSurfaces meshopt(geom);
+	    meshopt.SetFaceIndex (k);
+	    meshopt.SetImproveEdges (0);
+	    meshopt.SetMetricWeight (0.2);
+	    meshopt.SetWriteStatus (0);
+	    
+	    meshopt.CombineImprove (mesh);
+	  }
+	  
+	  if (multithread.terminate) return;
+	  {
+	    MeshOptimize2dOCCSurfaces meshopt(geom);
+	    meshopt.SetFaceIndex (k);
+	    meshopt.SetImproveEdges (0);
+	    meshopt.SetMetricWeight (0.2);
+	    meshopt.SetWriteStatus (0);
+	    
+	    meshopt.ImproveMesh (mesh);
+	  }
+	}
+      
+    }
+  
+  
+  mesh.CalcSurfacesOfNode();
+  mesh.Compress();
+
+  multithread.task = savetask;
+}
+
+double ComputeH (double kappa)
+{
+  double hret;
+  kappa *= mparam.curvaturesafety;
+  
+  if (mparam.maxh * kappa < 1)
+    hret = mparam.maxh;
+  else
+    hret = 1 / kappa;
+  
+  if (mparam.maxh < hret)
+    hret = mparam.maxh;
+  
+  return (hret);
+}
+
+
+class Line
+{
+public:
+  Point<3> p0, p1;
+  double 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;
+
+    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 Length ()
+  {
+    return (p1-p0).Length();
+  };
+};
+
+
+
+void RestrictHTriangle (gp_Pnt2d & par0, gp_Pnt2d & par1, gp_Pnt2d & par2,
+			BRepLProp_SLProps * prop, Mesh & mesh, double maxside, int depth, double h = 0)
+{
+  gp_Pnt2d parmid;
+
+  parmid.SetX(0.3*(par0.X()+par1.X()+par2.X()));
+  parmid.SetY(0.3*(par0.Y()+par1.Y()+par2.Y()));
+  
+  if (depth == 0)
+    {
+      prop->SetParameters (parmid.X(), parmid.Y());
+      if (!prop->IsCurvatureDefined())
+	{
+	  (*testout) << "curvature not defined!" << endl;
+	  return;
+	}
+      double curvature = max(fabs(prop->MinCurvature()), fabs(prop->MaxCurvature()));
+      if (curvature < 1e-3)
+	{
+	  (*testout) << "curvature too small (" << curvature << ")!" << endl;
+	  //	  return;
+	}
+      h = ComputeH (curvature+1e-10);
+    }
+  
+  if (h < maxside)
+    {
+      gp_Pnt2d pm0;
+      gp_Pnt2d pm1;
+      gp_Pnt2d pm2;
+      
+      pm0.SetX(0.5*(par1.X()+par2.X())); pm0.SetY(0.5*(par1.Y()+par2.Y()));
+      pm1.SetX(0.5*(par0.X()+par2.X())); pm1.SetY(0.5*(par0.Y()+par2.Y()));
+      pm2.SetX(0.5*(par1.X()+par0.X())); pm2.SetY(0.5*(par1.Y()+par0.Y()));
+
+      RestrictHTriangle (pm0, pm1, pm2, prop, mesh, maxside/2, depth+1, h);
+      RestrictHTriangle (par0, pm1, pm2, prop, mesh, maxside/2, depth+1, h);
+      RestrictHTriangle (par1, pm0, pm2, prop, mesh, maxside/2, depth+1, h);
+      RestrictHTriangle (par2, pm1, pm0, prop, mesh, maxside/2, depth+1, h);
+    }
+  else
+    {
+      prop->SetParameters (parmid.X(), parmid.Y());
+      gp_Pnt pnt = prop->Value();
+      Point3d p3d(pnt.X(), pnt.Y(), pnt.Z());
+      mesh.RestrictLocalH (p3d, h);
+
+      prop->SetParameters (par0.X(), par0.Y());
+      pnt = prop->Value();
+      p3d = Point3d(pnt.X(), pnt.Y(), pnt.Z());
+      mesh.RestrictLocalH (p3d, h);
+
+      prop->SetParameters (par1.X(), par1.Y());
+      pnt = prop->Value();
+      p3d = Point3d(pnt.X(), pnt.Y(), pnt.Z());
+      mesh.RestrictLocalH (p3d, h);
+
+      prop->SetParameters (par2.X(), par2.Y());
+      pnt = prop->Value();
+      p3d = Point3d(pnt.X(), pnt.Y(), pnt.Z());
+      mesh.RestrictLocalH (p3d, h);
+
+      //      (*testout) << "p = " << p3d << ", h = " << h << ", maxside = " << maxside << endl;
+      /*
+      (*testout) << pnt.X() << " " << pnt.Y() << " " << pnt.Z() << endl;
+
+      prop->SetParameters (par0.X(), par0.Y());
+      pnt = prop->Value();
+      (*testout) << pnt.X() << " " << pnt.Y() << " " << pnt.Z() << endl;
+
+      prop->SetParameters (par1.X(), par1.Y());
+      pnt = prop->Value();
+      (*testout) << pnt.X() << " " << pnt.Y() << " " << pnt.Z() << endl;
+
+      prop->SetParameters (par2.X(), par2.Y());
+      pnt = prop->Value();
+      (*testout) << pnt.X() << " " << pnt.Y() << " " << pnt.Z() << endl;
+      */
+    }
+}
+
+
+
+
+int OCCGenerateMesh (OCCGeometry & geom,
+		  Mesh *& mesh,
+		  int perfstepsstart, int perfstepsend,
+		  char * optstr)
+{
+  int i, j;
+
+  multithread.percent = 0;
+
+  if (perfstepsstart <= MESHCONST_ANALYSE)
+    {
+      delete mesh;
+      mesh = new Mesh();
+
+      mesh->SetGlobalH (mparam.maxh);
+
+      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)
+	{
+
+	  char * savetask = multithread.task;
+	  multithread.percent = 0;
+
+	  mesh->SetLocalH (bb.PMin(), bb.PMax(), mparam.grading);
+
+	  int nedges = geom.emap.Extent();
+	  int i;
+
+	  double maxedgelen = 0;
+	  double minedgelen = 1e99;
+
+	  
+	  multithread.task = "Setting local mesh size (elements per edge)";
+
+	  // setting elements per edge
+
+	  for (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;
+	      Handle(Geom_Curve) c = BRep_Tool::Curve(e, s0, s1);
+
+	      maxedgelen = max (maxedgelen, len);
+	      minedgelen = min (minedgelen, len);
+
+	      int j;
+	      int maxj = (int) ceil (localh);
+	      for (j = 0; j <= localh; 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 = 10;
+	  int nsections = 20;
+
+	  for (i = 1; i <= nedges && !multithread.terminate; i++)
+	    {
+	      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);
+	      
+	      int j;
+	      for (j = 1; j <= nsections; j++)
+		//	      for (j = 0; j < nsections; j++)
+		{
+		  double s = s0 + j/(double) nsections * (s1-s0);
+		  prop.SetParameter (s);
+		  double curvature = prop.Curvature();
+
+		  if (curvature >= 1e99) continue;
+
+		  gp_Pnt pnt = c->Value (s);
+
+		  mesh->RestrictLocalH (Point3d(pnt.X(), pnt.Y(), pnt.Z()),
+					ComputeH (fabs(curvature)));		  
+		}
+	    }
+
+	  
+
+	  multithread.task = "Setting local mesh size (face curvature)";
+
+	  // setting face curvature
+	  
+	  int nfaces = geom.fmap.Extent();
+	  
+	  for (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 (j = 1; j <= ntriangles; j++)
+		{
+		  int k;
+		  gp_Pnt p[3];
+		  gp_Pnt2d par[3];
+
+		  for (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]));
+
+		  RestrictHTriangle (par[0], par[1], par[2], &prop, *mesh, maxside, 0);
+		}
+	    }
+
+
+
+	  // setting close edges
+
+	  if (stlparam.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 *= stlparam.resthcloseedgefac;
+
+		  if (mindist < 1e-3) 
+		    {
+		      (*testout) << "extremely small local h: " << mindist
+				 << " --> setting to 1e-3" << endl;
+		      mindist = 1e-3;
+		    }
+
+		  mesh->RestrictLocalHLine(line.p0, line.p1, mindist);
+		}
+	    }
+				   
+
+
+	  multithread.task = savetask;
+
+	}
+    }
+
+
+  if (multithread.terminate || perfstepsend <= MESHCONST_ANALYSE) 
+    return TCL_OK;
+
+  if (perfstepsstart <= MESHCONST_MESHEDGES)
+    {
+      FindEdges (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.p1 == i) seg.p1 = equalto[i-1];
+		if (seg.p2 == i) seg.p2 = equalto[i-1];
+	      }
+	  }
+
+      cout << "Removing degenerated segments" << endl;
+      for (j = 1; j <= mesh->GetNSeg(); j++)
+	{
+	  Segment & seg = mesh->LineSegment(j);
+	  if (seg.p1 == seg.p2)
+	    {
+	      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);
+
+      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 (i = 1; i <= mesh->GetNP(); i++)
+      (*testout) << mesh->Point(i) << endl;
+ 
+  (*testout) << endl << "NSegments: " << mesh->GetNSeg() << endl;
+  for (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 0000000000..e24f52fa0a
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/occgeom.cpp
@@ -0,0 +1,1102 @@
+#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"
+
+
+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 e;
+  for (e.Init(shape, TopAbs_COMPOUND); e.More(); e.Next()) nrc++;
+  for (e.Init(shape, TopAbs_COMPSOLID); e.More(); e.Next()) nrcs++;
+
+  double surfacecont = 0;
+
+  for (int i = 1; i <= fmap.Extent(); i++)
+    {
+      GProp_GProps system;
+      BRepGProp::LinearProperties(fmap(i), system);
+      surfacecont += system.Mass();
+    }
+
+  cout << "Starting geometry healing procedure (tolerance: " << tolerance << ")" << endl
+       << "-----------------------------------" << endl;
+
+  if (fixsmalledges)
+    {
+      cout << endl << "- fixing small edges" << endl;
+
+      Handle(ShapeFix_Wire) sfw;
+      Handle_ShapeBuild_ReShape rebuild = new ShapeBuild_ReShape;
+      rebuild->Apply(shape);
+
+      for (int i = 1; i <= fmap.Extent(); i++)
+	{
+	  TopExp_Explorer exp1;
+	  for (exp1.Init (fmap(i), TopAbs_WIRE); exp1.More(); exp1.Next())
+	    {
+	      TopoDS_Wire oldwire = TopoDS::Wire(exp1.Current());
+	      sfw = new ShapeFix_Wire (oldwire, TopoDS::Face(fmap(i)),tolerance);
+	      sfw->ModifyTopologyMode() = Standard_True;
+
+	      if (sfw->FixSmall (false, tolerance))
+		{
+		  cout << "Fixed small edge in wire " << wmap.FindIndex (oldwire) << endl;
+		  TopoDS_Wire newwire = sfw->Wire();
+		  rebuild->Replace(oldwire, newwire, Standard_False);
+		}
+	      if ((sfw->StatusSmall(ShapeExtend_FAIL1)) ||
+		  (sfw->StatusSmall(ShapeExtend_FAIL2)) ||
+		  (sfw->StatusSmall(ShapeExtend_FAIL3)))
+		cout << "Failed to fix small edge in wire " << wmap.FindIndex (oldwire) << endl;
+
+	      
+	    }
+	}
+
+      shape = rebuild->Apply(shape);
+
+
+
+      {
+      Handle_ShapeBuild_ReShape rebuild = new ShapeBuild_ReShape;
+      rebuild->Apply(shape);
+      TopExp_Explorer exp1;
+      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) << endl;
+		  rebuild->Remove(edge, false);
+		}
+	    }
+	}
+      shape = rebuild->Apply(shape);
+      }
+
+
+      Handle(ShapeFix_Wireframe) sfwf = new ShapeFix_Wireframe;
+      sfwf->SetPrecision(tolerance);
+      sfwf->Load (shape);
+
+      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;
+	}
+  
+
+      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;
+	}
+      
+
+      shape = sfwf->Shape();
+    }
+
+
+
+
+
+  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();
+    }
+
+  if (sewfaces)
+    {
+      cout << endl << "- sewing faces" << endl;
+
+      TopExp_Explorer exp0;
+
+      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";
+    }
+
+  if (makesolids)
+    {  
+      cout << endl << "- making solids" << endl;
+      
+      TopExp_Explorer exp0;
+
+      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;
+		}
+	    }
+	  else
+	    cout << " not possible" << endl;
+	}
+    }
+
+  BuildFMap();
+
+  double newsurfacecont = 0;
+
+  for (int i = 1; i <= fmap.Extent(); i++)
+    {
+      GProp_GProps system;
+      BRepGProp::LinearProperties(fmap(i), 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 (e.Init(shape, TopAbs_COMPOUND); e.More(); e.Next()) nnrc++;
+  for (e.Init(shape, TopAbs_COMPSOLID); e.More(); e.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 << "Totol 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_SOLID);
+       exp0.More(); exp0.Next())
+    {
+      TopoDS_Solid solid = TopoDS::Solid (exp0.Current());
+      
+      if (somap.FindIndex(TopoDS::Solid (exp0.Current())) < 1)
+	{
+	  somap.Add (TopoDS::Solid (exp0.Current()));
+	  
+	  for (exp1.Init(exp0.Current(), TopAbs_SHELL);
+	       exp1.More(); exp1.Next())
+	    {
+	      TopoDS_Shell shell = TopoDS::Shell (exp1.Current().Composed (exp0.Current().Orientation()));
+	      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().Composed(shell.Orientation()));
+		      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().Composed(face.Orientation()));
+			      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().Composed(wire.Orientation()));
+				      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(exp0.Current(), TopAbs_SHELL, TopAbs_SOLID);
+       exp1.More(); exp1.Next())
+    {
+      TopoDS_Shape shell = exp1.Current().Composed (exp0.Current().Orientation());
+      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().Composed(shell.Orientation()));
+	      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 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.SetSize (fmap.Extent());
+  facemeshstatus = 0;
+
+  fvispar.SetSize (fmap.Extent());
+  evispar.SetSize (emap.Extent());
+  vvispar.SetSize (vmap.Extent());
+}
+
+
+
+void OCCGeometry :: SewFaces ()
+{
+  (*testout) << "Trying to sew faces ..." << endl;
+  cout << "Trying to sew faces ..." << flush;
+
+  BRepOffsetAPI_Sewing sewedObj(1);
+  //  BRepOffsetAPI_Sewing sewedObj(healingtolerance);
+
+  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";
+  
+  /*
+  ShapeUpgrade_ShellSewing sewing;
+  TopoDS_Shape sh = sewing.ApplySewing (shape);
+  shape = sh;
+  */
+}
+
+
+
+
+
+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->Apply(shape);
+	  rebuild->Replace(solid, newsolid, Standard_False);
+	  //	  TopoDS_Shape newshape = rebuild->Apply(shape);
+	  
+	  TopoDS_Shape newshape = rebuild->Apply(shape, TopAbs_SHAPE, 1);
+	  shape = newshape;
+	}
+      
+      cout << " done" << endl;
+    }
+  else
+    cout << " not possible" << endl;
+}
+
+
+void OCCGeometry :: BuildVisualizationMesh ()
+{
+  cout << "Preparing visualization (deflection = " << vispar.occdeflection << ") ... " << flush;
+  BRepTools::Clean (shape);
+  BRepMesh_IncrementalMesh::BRepMesh_IncrementalMesh (shape, vispar.occdeflection, true);
+  cout << "done" << endl;
+  
+  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();
+}
+
+
+
+  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;
+  }
+
+
+OCCGeometry * LoadOCC_IGES (const char * filename)
+{
+  OCCGeometry * occgeo;
+  occgeo = new OCCGeometry;
+  
+  IGESControl_Reader reader;
+  
+#ifdef OCC52
+  Standard_Integer stat = reader.ReadFile((char*)filename);
+#else
+  Standard_Integer stat = reader.LoadFile((char*)filename);
+  reader.Clear();
+#endif
+  
+#ifdef OCC52
+  reader.TransferRoots(); // Tranlate IGES -> OCC
+#else
+  reader.TransferRoots(Standard_False); // Tranlate IGES -> OCC
+#endif
+
+  occgeo->shape = reader.OneShape();
+  occgeo->changed = 1;
+  occgeo->BuildFMap();
+  occgeo->BuildVisualizationMesh();
+  PrintContents (occgeo);
+
+  return occgeo;
+}
+
+OCCGeometry * LoadOCC_STEP (const char * filename)
+{
+  OCCGeometry * occgeo;
+  occgeo = new OCCGeometry;
+  
+  STEPControl_Reader reader;
+  Standard_Integer stat = reader.ReadFile((char*)filename);
+  Standard_Integer nb = reader.NbRootsForTransfer();
+  reader.TransferRoots (); // Tranlate STEP -> OCC
+  occgeo->shape = reader.OneShape();
+  occgeo->changed = 1;
+  occgeo->BuildFMap();
+  occgeo->BuildVisualizationMesh();
+  PrintContents (occgeo);
+
+  return occgeo;
+}
+
+char * shapesname[] =
+  {" ", "CompSolids", "Solids", "Shells",
+   "Faces", "Wires", "Edges", "Vertices"};
+
+char * shapename[] =
+  {" ", "CompSolid", "Solid", "Shell",
+   "Face", "Wire", "Edge", "Vertex"};
+
+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;
+
+  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;
+	}
+      
+      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();
+      /*
+      int j;
+      for (j = i+1; j <= emap.Extent(); j++)
+	{
+	  TopoDS_Edge edge2 = TopoDS::Edge (emap(j));
+
+	  if (csm.CheckStripEdges(edge1, edge2, csm.Tolerance(), dmax))
+	    {
+	      if (!edgessamebutnotidentified++)
+		str << "EdgesSameButNotIdentified {Edges same but not identified} ";
+
+	      cnt++;
+	      (*testout) << "Edge " << i << " and edge " << j
+			 << " are on one strip (same but not identified)" << endl;
+	      str << "EdgesSameButNotIdentified/Edge" << cnt << " ";
+	      str << "{Edge " << i << " and Edge " << j << "} ";
+	    }
+	}
+      */
+    }
+
+  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;
+
+  /*
+  for (i = 1; i <= shmap.Extent(); i++)
+    {
+      TopoDS_Shell shell = TopoDS::Shell (shmap(i));
+      if (!shell.Closed()) 
+	cout << "Shell " << i << " is not closed" << endl;
+      if (shell.Infinite()) 
+	cout << "Shell " << i << " is infinite" << endl;
+
+      BRepCheck_Analyzer ba(shell);
+      if (!ba.IsValid ())
+	cout << "Shell " << i << " is not valid" << endl;
+    }
+
+  for (i = 1; i <= somap.Extent(); i++)
+    {
+      TopoDS_Solid solid = TopoDS::Solid (somap(i));
+      if (!solid.Closed()) 
+	cout << "Solid " << i << " is not closed" << endl;
+      if (solid.Infinite()) 
+	cout << "Solid " << i << " is infinite" << endl;
+
+      BRepCheck_Analyzer ba(solid);
+      if (!ba.IsValid ())
+	cout << "Solid " << i << " is not valid" << 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;
+}
+
+}
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/occ/occgeom.hpp b/contrib/Netgen/libsrc/occ/occgeom.hpp
new file mode 100644
index 0000000000..5082ab3b91
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/occgeom.hpp
@@ -0,0 +1,279 @@
+#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 "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 "IGESControl_Reader.hxx"
+#include "STEPControl_Reader.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"
+#include "IGESControl_Writer.hxx"
+#include "STEPControl_Writer.hxx"
+#include "StlAPI_Writer.hxx"
+#include "STEPControl_StepModelType.hxx"
+
+namespace netgen
+{
+
+#include "../visualization/vispar.hpp"
+  //  class VisualizationParameters;
+  //  extern VisualizationParameters vispar;
+
+
+#include "occmeshsurf.hpp"
+
+#define PROJECTION_TOLERANCE 1e-10
+
+
+#define ENTITYISVISIBLE 1
+#define ENTITYISHIGHLIGHTED 2
+#define ENTITYISDRAWABLE 4
+
+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; }
+};
+
+
+
+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;
+}
+
+
+
+#define OCCGEOMETRYVISUALIZATIONNOCHANGE   0
+#define OCCGEOMETRYVISUALIZATIONFULLCHANGE 1
+  // == compute transformation matrices and redraw
+#define OCCGEOMETRYVISUALIZATIONHALFCHANGE 2
+  // == redraw
+
+class OCCGeometry
+{
+  Point<3> center;
+
+public:
+  TopoDS_Shape shape;
+  TopTools_IndexedMapOfShape fmap, emap, vmap, somap, shmap, wmap;
+  Box<3> boundingbox;
+
+  int changed; 
+  ARRAY<int> facemeshstatus;
+
+  ARRAY<EntityVisualizationCode> fvispar, evispar, vvispar;
+
+  double tolerance;
+  bool fixsmalledges;
+  bool fixspotstripfaces;
+  bool sewfaces;
+  bool makesolids;
+
+
+  OCCGeometry()
+  {
+    somap.Clear();
+    shmap.Clear();
+    fmap.Clear();
+    wmap.Clear();
+    emap.Clear();
+    vmap.Clear();
+  }
+
+
+  void BuildFMap();
+
+  Box<3> GetBoundingBox()
+  { return boundingbox; }
+
+  int NrSolids()
+  { return somap.Extent(); }
+
+  void SetCenter()
+  { center = boundingbox.Center(); }
+
+  Point<3> Center()
+  { return center; }
+
+  void 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));
+
+    GeomAPI_ProjectPointOnSurf proj(pnt, BRep_Tool::Surface(TopoDS::Face(fmap(surfi))));
+    if (proj.NbPoints() == 0)
+      {
+	cout << "Projection fails" << endl;
+      }
+    else
+      {
+	pnt = proj.NearestPoint();
+	p = Point<3> (pnt.X(), pnt.Y(), pnt.Z());
+      }
+  }
+
+  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 BuildVisualizationMesh ();
+
+  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();
+
+  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 PrintContents (OCCGeometry * geom);
+
+OCCGeometry * LoadOCC_IGES (const char * filename);
+OCCGeometry * LoadOCC_STEP (const char * filename);
+
+}
+
+#endif
+
+#endif
diff --git a/contrib/Netgen/libsrc/occ/occmeshsurf.cpp b/contrib/Netgen/libsrc/occ/occmeshsurf.cpp
new file mode 100644
index 0000000000..034571c9ae
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/occmeshsurf.cpp
@@ -0,0 +1,592 @@
+#ifdef OCCGEOMETRY
+
+#include <mystdlib.h>
+
+#include <occgeom.hpp>
+#include <meshing.hpp>
+
+
+namespace netgen
+{
+#include "occmeshsurf.hpp"
+
+
+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();
+
+  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;
+    }
+  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;
+    }
+  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));
+
+  //  cout << "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);
+  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(Box3d(abb.PMin(), abb.PMax())), surface(TopoDS::Face(asurf), aprojecttype)
+{
+  ;
+}
+
+
+void Meshing2OCCSurfaces :: DefineTransformation (Point3d & p1, 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, Point3d & p) const
+{
+  Point<3> hp = p;
+  geometry.Project (surfind, hp);
+  p = hp;
+}
+
+void MeshOptimize2dOCCSurfaces :: ProjectPoint2 (INDEX surfind, INDEX surfind2, 
+					      Point3d & 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.X(), p.Y(), p.Z());
+  GeomAPI_ProjectPointOnCurve proj(pnt, c);
+  pnt = proj.NearestPoint();  
+  p.X() = pnt.X();
+  p.Y() = pnt.Y();
+  p.Z() = pnt.Z();
+	
+}
+
+void MeshOptimize2dOCCSurfaces :: 
+GetNormalVector(INDEX surfind, const Point3d & p, PointGeomInfo & geominfo, Vec3d & 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 Point3d & p, Vec3d & n) const
+{
+  //  static int cnt = 0;
+  //  if (cnt++ % 1000 == 0) cout << "GetNV cnt = " << cnt << endl;
+  Standard_Real u,v;
+
+  gp_Pnt pnt(p.X(), p.Y(), p.Z());
+
+  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);
+
+  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 Point3d& p) const
+{
+  Standard_Real u,v;
+
+  gp_Pnt pnt(p.X(), p.Y(), p.Z());
+
+  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);  
+
+  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 Point3d & p1, const Point3d & p2, double secpoint,
+	      int surfi, 
+	      const PointGeomInfo & gi1, 
+	      const PointGeomInfo & gi2,
+	      Point3d & newp, PointGeomInfo & newgi)
+{
+  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;
+    }
+  
+  newp = hnewp;
+}
+
+
+void OCCRefinementSurfaces :: 
+PointBetween (const Point3d & p1, const Point3d & p2, double secpoint,
+ 	      int surfi1, int surfi2, 
+	      const EdgePointGeomInfo & ap1, 
+	      const EdgePointGeomInfo & ap2,
+	      Point3d & newp, EdgePointGeomInfo & newgi)
+{
+  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)
+{
+  if (surfi > 0)
+    geometry.Project (surfi, p);
+};
+
+void OCCRefinementSurfaces :: ProjectToSurface (Point<3> & p, int surfi, PointGeomInfo & gi)
+{
+  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 0000000000..bda8a05a64
--- /dev/null
+++ b/contrib/Netgen/libsrc/occ/occmeshsurf.hpp
@@ -0,0 +1,201 @@
+#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 (Point3d & p1, 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, Point3d & p) const;
+    ///
+    virtual void ProjectPoint2 (INDEX surfind, INDEX surfind2, Point3d & p) const;
+    ///
+    virtual void GetNormalVector(INDEX surfind, const Point3d & p, Vec3d & n) const;
+    ///
+    virtual void GetNormalVector(INDEX surfind, const Point3d & p, PointGeomInfo & gi, Vec3d & n) const;
+
+    
+  virtual int CalcPointGeomInfo(int surfind, PointGeomInfo& gi, const Point3d& p3) const;
+
+};
+
+
+
+class OCCGeometry;
+
+
+class OCCRefinementSurfaces : public Refinement
+{
+  const OCCGeometry & geometry;
+
+public:
+  OCCRefinementSurfaces (const OCCGeometry & ageometry);
+  virtual ~OCCRefinementSurfaces ();
+  
+  virtual void PointBetween (const Point3d & p1, const Point3d & p2, double secpoint,
+			     int surfi, 
+			     const PointGeomInfo & gi1, 
+			     const PointGeomInfo & gi2,
+			     Point3d & newp, PointGeomInfo & newgi);
+
+  virtual void PointBetween (const Point3d & p1, const Point3d & p2, double secpoint,
+			     int surfi1, int surfi2, 
+			     const EdgePointGeomInfo & ap1, 
+			     const EdgePointGeomInfo & ap2,
+			     Point3d & newp, EdgePointGeomInfo & newgi);
+
+  virtual void ProjectToSurface (Point<3> & p, int surfi);
+
+  virtual void ProjectToSurface (Point<3> & p, int surfi, PointGeomInfo & gi);
+};
+
+
+
+#endif
+
+
+
+#endif
diff --git a/contrib/Netgen/libsrc/opti/Makefile b/contrib/Netgen/libsrc/opti/Makefile
new file mode 100644
index 0000000000..d73441553f
--- /dev/null
+++ b/contrib/Netgen/libsrc/opti/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for optimisation library
+#
+src = bfgs.cpp linsearch.cpp linopt.cpp 
+#
+lib = opti
+libpath = libsrc/opti
+#
+#
+include ../makefile.inc
diff --git a/contrib/Netgen/libsrc/opti/bfgs.cpp b/contrib/Netgen/libsrc/opti/bfgs.cpp
new file mode 100644
index 0000000000..31a499a64d
--- /dev/null
+++ b/contrib/Netgen/libsrc/opti/bfgs.cpp
@@ -0,0 +1,367 @@
+/***************************************************************************/
+/*                                                                         */
+/* 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 i, j, k;
+  int n = a.Height();
+  
+  //  (*testout) << "a = " << a << endl;
+
+  l = a;
+
+  for (i = 1; i <= n; i++)
+    {
+      for (j = i; j <= n; j++)
+	{
+	  x = l.Get(i, j);
+
+	  for (k = 1; k < i; k++)
+	    x -= l.Get(i, k) * l.Get(j, k) * d.Get(k); 
+	  
+	  if (i == j)
+	    {
+	      d.Elem(i) = x;
+	    }
+	  else
+	    {
+	      l.Elem(j, i) = x / d.Get(k);
+	    }
+	}
+    }
+
+  for (i = 1; i <= n; i++)
+    {
+      l.Elem(i, i) = 1;
+      for (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);
+    }
+}
+
+void SolveLDLt (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 = 1; j < i; j++)
+	val += p.Get(j) * l.Get(i, j);
+      p.Elem(i) -= val;
+    }
+  for (i = 1; i <= n; i++)
+    p.Elem(i) /= d.Get(i);
+
+  for (i = n; i >= 1; i--)
+    {
+      val = 0;
+      for (j = i+1; j <= n; j++)
+	val += p.Get(j) * l.Get(j, i);
+      p.Elem(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 i, j, n;
+
+  n = l.Height();
+
+  Vector v(n);
+  double t, told, xi;
+
+  told = 1;
+  v = u;
+
+  for (j = 1; j <= n; j++)
+    {
+      t = told + a * sqr (v.Elem(j)) / d.Get(j);
+
+      if (t <= 0) return 1;
+
+      xi = a * v.Elem(j) / (d.Get(j) * t);
+
+      d.Elem(j) *= t / told;
+
+      for (i = j + 1; i <= n; i++)
+	{
+	  v.Elem(i) -= v.Elem(j) * l.Elem(i, j);
+	  l.Elem(i, j) += xi * v.Elem(i);
+	}
+
+      told = t;
+    }
+
+  return 0;
+}
+
+
+double BFGS (
+	     Vector & x,         // i: Startwert
+	     // o: Loesung, falls IFAIL = 0
+	     const MinFunction & fun,
+	     const OptiParameters & par,
+	     double eps
+	     )
+
+
+{
+
+  int i, j, 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 (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 (i = 1; i <= n; i++)
+	    d.Elem(i) = typf/ sqr (typx.Get(i));   // 1;
+	  for (i = 2; i <= n; i++)
+	    for (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);
+
+
+      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 (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 << "update error1" << endl;
+	      ifail = 1;
+	      break;
+	    }
+
+	  if (LDLtUpdate (l, d, -1 / a2, bs) != 0)
+	    {
+	      cerr << "update error2" << endl;
+	      ifail = 1;
+	      break;
+	    }
+	}
+
+      // Calculate stop conditions
+
+      hd = eps * max2 (typf, fabs (f));
+      a1crit = 1;
+      for (i = 1; i <= n; i++)
+	if ( fabs (g.Elem(i)) * max2 (typx.Elem(i), fabs (x.Elem(i))) > 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/opti/linopt.cpp b/contrib/Netgen/libsrc/opti/linopt.cpp
new file mode 100644
index 0000000000..a5d381e6b7
--- /dev/null
+++ b/contrib/Netgen/libsrc/opti/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.Elem(1) = b.Get(i1);
+        rs.Elem(2) = b.Get(i2);
+        rs.Elem(3) = b.Get(i3);
+        
+        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.Elem(1);
+	for (int hi = 2; hi <= res.Size(); hi++)
+	  if (res.Elem(hi) < rmin) rmin = res.Elem(hi);
+        
+        if ( (f < fmin) && rmin >= -1e-8)
+          {
+          fmin = f;
+          x = hx;
+          }
+        }
+  }
+}
diff --git a/contrib/Netgen/libsrc/opti/linsearch.cpp b/contrib/Netgen/libsrc/opti/linsearch.cpp
new file mode 100644
index 0000000000..c847911dae
--- /dev/null
+++ b/contrib/Netgen/libsrc/opti/linsearch.cpp
@@ -0,0 +1,346 @@
+/***************************************************************************/
+/*                                                                         */
+/* 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
+{
+  int n = x.Size();
+  int i, j;
+
+  static Vector xr;
+  static Vector xl;
+  xr.SetSize(n);
+  xl.SetSize(n);
+
+  double eps = 1e-6;
+  double fl, fr;
+  
+  for (i = 1; i <= n; i++)
+    {
+      xr.Set (1, x);
+      xl.Set (1, x);
+      xr.Elem(i) += eps;
+      xl.Elem(i) -= eps;
+
+      fl = Func (xl);
+      fr = Func (xr);
+
+      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 = 1; i <= n; i++)
+    {
+      for (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) =
+	    (f11 + f22 - f12 - f21) / (2 * eps * eps);
+	}
+
+      hx = x;
+      f = Func(x);
+      hx.Elem(i) = x.Get(i) + eps;
+      f11 = Func(hx);
+      hx.Elem(i) = x.Get(i) - eps;
+      f22 = Func(hx);
+
+      hesse.Elem(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;
+    }
+
+  phi1prime = phi0prime;
+
+  //  (*testout) << "phi0prime = " << phi0prime << endl;
+
+  //  it = 100000l;
+  it = 0;
+
+  while (it++ <= par.maxit_linsearch)
+    {
+      //      (*testout) << "alphahat = " << alphahat << endl;
+
+      xneu.Set2 (1, x, alphahat, p);
+
+      // f = fun.FuncGrad (xneu, g);
+      //      f = fun.Func (xneu);
+      f = fun.FuncDeriv (xneu, p, phihatprime);
+
+      //      (*testout) << "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/opti/opti.hpp b/contrib/Netgen/libsrc/opti/opti.hpp
new file mode 100644
index 0000000000..5fa0735bd0
--- /dev/null
+++ b/contrib/Netgen/libsrc/opti/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
+
+
+
+
+  /**  
+       Solvers 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/stlgeom/Makefile b/contrib/Netgen/libsrc/stlgeom/Makefile
new file mode 100644
index 0000000000..007a979cad
--- /dev/null
+++ b/contrib/Netgen/libsrc/stlgeom/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for geometric library
+#
+src = stlgeom.cpp stltopology.cpp stlgeomchart.cpp stlgeommesh.cpp meshstlsurface.cpp stlline.cpp stltool.cpp
+#
+lib = stlgeom
+libpath = libsrc/stlgeom 
+#
+#
+include ../makefile.inc
+#
diff --git a/contrib/Netgen/libsrc/stlgeom/meshstlsurface.cpp b/contrib/Netgen/libsrc/stlgeom/meshstlsurface.cpp
new file mode 100644
index 0000000000..5af12d7aa9
--- /dev/null
+++ b/contrib/Netgen/libsrc/stlgeom/meshstlsurface.cpp
@@ -0,0 +1,1130 @@
+#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, k;
+  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
+  for (i = 1; i <= meshpoints.Size(); i++)
+    {
+      geom.meshpoints.Append(meshpoints.Get(i)); //testing
+
+      int 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.p1 = p1;
+	  seg.p2 = 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.p1);
+	  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.p2);
+	  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.p1), mesh.Point(seg.p2)) < 1e-10)
+	    {
+	      (*testout) << "ERROR: Line segment of length 0" << endl;
+	      (*testout) << "pi1, 2 = " << seg.p1 << ", " << seg.p2 << endl;
+	      (*testout) << "p1, 2 = " << mesh.Point(seg.p1)
+			 << ", " << mesh.Point(seg.p2) << endl;
+	      throw NgException ("Line segment of length 0");
+	    }
+	  
+	  mesh.AddSegment (seg);
+
+
+	  Segment seg2;
+	  seg2.p1 = p2;
+	  seg2.p2 = 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.p1);
+	  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.p2);
+	  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.p1),mesh.Point(seg.p2));
+		    }
+
+		  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);
+		}
+
+	      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.p1, seg.p2);
+		  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.p1, seg.p2);
+		  i2.Sort();
+		  if (openseght.Used (i2))
+		    {
+		      // segment will be split
+		      PrintMessage(7,"Split segment ", int(seg.p1), "-", int(seg.p2));
+	      
+		      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.p2 = newpi;
+		      nseg1.epgeominfo[1] = newgi;
+		      
+		      nseg2.p1 = newpi;
+		      nseg2.epgeominfo[0] = newgi;
+		      
+		      mesh.LineSegment(i) = nseg1;
+		      mesh.AddSegment (nseg2);
+		      
+		      mesh.RestrictLocalH (Center (mesh.Point(nseg1.p1),
+						   mesh.Point(nseg1.p2)),
+					   Dist (mesh.Point(nseg1.p1),
+						 mesh.Point(nseg1.p2)));
+		      mesh.RestrictLocalH (Center (mesh.Point(nseg2.p1),
+						   mesh.Point(nseg2.p2)),
+					   Dist (mesh.Point(nseg2.p1),
+						 mesh.Point(nseg2.p2)));
+		    }
+		}
+
+	    }
+
+	  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
+	  mesh.CheckOverlappingBoundary();
+
+	  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, k;
+  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);
+	
+	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.p1, seg.p2, seg.geominfo[0], seg.geominfo[1]);
+	}
+      
+      
+      PrintMessage(3,"start meshing, trialcnt = ", retrynr);
+
+      /*
+      (*testout) << "start meshing with h = " << h << endl;
+      */
+      meshing.GenerateMesh (mesh, h, fnr);  // face index
+#ifdef OPENGL
+      extern void Render();
+      Render();
+#endif
+    }    
+      
+  
+  mesh.CalcSurfacesOfNode();
+}
+
+
+
+void STLSurfaceOptimization (STLGeometry & geom,
+			     class Mesh & mesh,
+			     MeshingParameters & mparam)
+{
+  PrintFnStart("optimize STL Surface");
+
+
+  MeshOptimizeSTLSurface optmesh(geom);
+  //
+
+  int i, j;
+  /*
+  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 (mparam.elsizeweight);
+
+  PrintMessage(5,"optimize string = ", mparam.optimize2d, " elsizew = ", mparam.elsizeweight);
+
+  for (i = 1; i <= mparam.optsteps2d; i++)
+    for (j = 1; j <= strlen(mparam.optimize2d); j++)
+      {
+	if (multithread.terminate)
+	  break;
+
+	(*testout) << "optimize, before, step = " << mparam.optimize2d[j-1] << mesh.Point (3679) << endl;
+
+	mesh.CalcSurfacesOfNode();
+	switch (mparam.optimize2d[j-1])
+	  {
+	  case 's': 
+	    {
+	      optmesh.EdgeSwapping (mesh, 0);
+	      break;
+	    }
+	  case 'S': 
+	    {
+	      optmesh.EdgeSwapping (mesh, 1);
+	      break;
+	    }
+	  case 'm': 
+	    {
+	      optmesh.ImproveMesh(mesh);
+	      break;
+	    }
+	  case 'c': 
+	    {
+	      optmesh.CombineImprove (mesh);
+	      break;
+	    }
+	  }
+	(*testout) << "optimize, after, step = " << mparam.optimize2d[j-1] << mesh.Point (3679) << endl;
+      }
+
+  geom.surfaceoptimized = 1;
+
+  mesh.Compress();
+  mesh.CalcSurfacesOfNode();
+
+
+}
+
+
+
+MeshingSTLSurface :: MeshingSTLSurface (STLGeometry & ageom)
+  : Meshing2(Box3d (ageom.GetBoundingBox().PMin(),
+		    ageom.GetBoundingBox().PMax())), geom(ageom)
+{
+  ;
+}
+
+void MeshingSTLSurface :: DefineTransformation (Point3d & p1, 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 Point3d & p,
+						     const PointGeomInfo & gi)
+{
+  //  (*testout) << "sel char: " << gi.trignum << endl;
+  
+  geom.SelectChartOfTriangle (gi.trignum);
+  //  geom.SelectChartOfPoint (p);
+}
+
+
+void MeshOptimizeSTLSurface :: ProjectPoint (INDEX surfind, Point3d & p) const
+{
+  Point<3> hp = p;
+  if (!geom.Project (hp))
+    {
+      PrintMessage(7,"project failed");
+      
+      if (!geom.ProjectOnWholeSurface(hp)) 
+	{
+	  PrintMessage(7, "project on whole surface failed");
+	}
+    }
+  p = hp;
+  //  geometry.GetSurface(surfind)->Project (p);
+}
+
+void MeshOptimizeSTLSurface :: ProjectPoint2 (INDEX surfind, INDEX surfind2, Point3d & p) const
+{
+  /*
+  ProjectToEdge ( geometry.GetSurface(surfind), 
+		  geometry.GetSurface(surfind2), p);
+  */
+}
+
+int  MeshOptimizeSTLSurface :: CalcPointGeomInfo(PointGeomInfo& gi, const Point3d& p3) const
+{
+  Point<3> hp = p3;
+  gi.trignum = geom.Project (hp);
+
+  if (gi.trignum)
+    {
+      return 1;
+    }
+
+  return 0;
+  
+}
+
+void MeshOptimizeSTLSurface :: GetNormalVector(INDEX surfind, const Point3d & p, Vec3d & 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 Point3d & p1, const Point3d & p2, double secpoint,
+	       int surfi, 
+	       const PointGeomInfo & gi1, 
+	       const PointGeomInfo & gi2,
+	       Point3d & newp, PointGeomInfo & newgi)
+{
+  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 Point3d & p1, const Point3d & p2, double secpoint,
+	      int surfi1, int surfi2, 
+	      const EdgePointGeomInfo & gi1, 
+	      const EdgePointGeomInfo & gi2,
+	      Point3d & newp, EdgePointGeomInfo & newgi)
+{
+  /*
+  (*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)
+{
+  cout << "RefinementSTLGeometry :: ProjectToSurface not implemented!" << endl;
+}
+
+
+void RefinementSTLGeometry :: ProjectToSurface (Point<3> & p, int surfi,
+						PointGeomInfo & gi)
+{
+  ((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 0000000000..f190d107a5
--- /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);
+
+protected:
+  ///
+  virtual void DefineTransformation (Point3d & p1, 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 Point3d & p,
+				       const PointGeomInfo & gi);
+    ///
+    virtual void ProjectPoint (INDEX surfind, Point3d & p) const;
+    ///
+    virtual void ProjectPoint2 (INDEX surfind, INDEX surfind2, Point3d & p) const;
+    ///
+    virtual int CalcPointGeomInfo(PointGeomInfo& gi, const Point3d& p3) const;
+    ///
+    virtual void GetNormalVector(INDEX surfind, const Point3d & p, Vec3d & n) const;
+};
+
+
+
+
+class RefinementSTLGeometry : public Refinement
+{
+  const STLGeometry & geom;
+
+public:
+  RefinementSTLGeometry (const STLGeometry & ageom);
+  virtual ~RefinementSTLGeometry ();
+  
+  virtual void PointBetween (const Point3d & p1, const Point3d & p2, double secpoint,
+			     int surfi, 
+			     const PointGeomInfo & gi1, 
+			     const PointGeomInfo & gi2,
+			     Point3d & newp, PointGeomInfo & newgi);
+
+  virtual void PointBetween (const Point3d & p1, const Point3d & p2, double secpoint,
+			     int surfi1, int surfi2, 
+			     const EdgePointGeomInfo & ap1, 
+			     const EdgePointGeomInfo & ap2,
+			     Point3d & newp, EdgePointGeomInfo & newgi);
+
+  virtual void ProjectToSurface (Point<3> & p, int surfi);
+  virtual void ProjectToSurface (Point<3> & p, int surfi, PointGeomInfo & gi);
+};
+
+
+
+#endif
+
diff --git a/contrib/Netgen/libsrc/stlgeom/stlgeom.cpp b/contrib/Netgen/libsrc/stlgeom/stlgeom.cpp
new file mode 100644
index 0000000000..2d3a358cfa
--- /dev/null
+++ b/contrib/Netgen/libsrc/stlgeom/stlgeom.cpp
@@ -0,0 +1,3476 @@
+#include <mystdlib.h>
+
+#include <myadt.hpp>
+#include <linalg.hpp>
+#include <gprim.hpp>
+
+#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(), edgedata(*this), selectedmultiedge()
+{
+  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()
+{
+  ;
+}
+
+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 p1,p2;
+  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)), p1, p2);
+	      if (!IsEdge(p1,p2))
+		{
+		  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.Elem(k+1) = 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.Elem(k+1) = 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.Get(1), sol.Get(2), sol.Get(3));
+      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 p1, int p2)
+{
+  STLEdge e(p1,p2);
+  e.SetLeftTrig(GetLeftTrig(p1,p2));
+  e.SetRightTrig(GetRightTrig(p1,p2));
+  return edges.Append(e);
+}
+
+void STLGeometry :: STLDoctorConfirmEdge()
+{
+  StoreEdgeData();
+  if (GetSelectTrig() >= 1 && GetSelectTrig() <= GetNT() && GetNodeOfSelTrig())
+    {
+      if (stldoctor.selectmode == 1)
+	{
+	  int p1 = GetTriangle(GetSelectTrig()).PNum(GetNodeOfSelTrig());
+	  int p2 = GetTriangle(GetSelectTrig()).PNumMod(GetNodeOfSelTrig()+1);
+	  edgedata.Elem(edgedata.GetEdgeNum(p1,p2)).SetStatus (ED_CONFIRMED);
+	}
+      else if (stldoctor.selectmode == 3 || stldoctor.selectmode == 4)
+	{
+	  int i;
+	  for (i = 1; i <= selectedmultiedge.Size(); i++)
+	    {
+	      int p1 = selectedmultiedge.Get(i).i1;
+	      int p2 = selectedmultiedge.Get(i).i2;
+	      edgedata.Elem(edgedata.GetEdgeNum(p1,p2)).SetStatus (ED_CONFIRMED);
+	    }
+	}
+    }
+}
+
+void STLGeometry :: STLDoctorCandidateEdge()
+{
+  StoreEdgeData();
+  if (GetSelectTrig() >= 1 && GetSelectTrig() <= GetNT() && GetNodeOfSelTrig())
+    {
+      if (stldoctor.selectmode == 1)
+	{
+	  int p1 = GetTriangle(GetSelectTrig()).PNum(GetNodeOfSelTrig());
+	  int p2 = GetTriangle(GetSelectTrig()).PNumMod(GetNodeOfSelTrig()+1);
+	  edgedata.Elem(edgedata.GetEdgeNum(p1,p2)).SetStatus (ED_CANDIDATE);
+	}
+      else if (stldoctor.selectmode == 3 || stldoctor.selectmode == 4)
+	{
+	  int i;
+	  for (i = 1; i <= selectedmultiedge.Size(); i++)
+	    {
+	      int p1 = selectedmultiedge.Get(i).i1;
+	      int p2 = selectedmultiedge.Get(i).i2;
+	      edgedata.Elem(edgedata.GetEdgeNum(p1,p2)).SetStatus (ED_CANDIDATE);
+	    }
+	}
+    }
+}
+
+void STLGeometry :: STLDoctorExcludeEdge()
+{
+  StoreEdgeData();
+  if (GetSelectTrig() >= 1 && GetSelectTrig() <= GetNT() && GetNodeOfSelTrig())
+    {
+      if (stldoctor.selectmode == 1)
+	{
+	  int p1 = GetTriangle(GetSelectTrig()).PNum(GetNodeOfSelTrig());
+	  int p2 = GetTriangle(GetSelectTrig()).PNumMod(GetNodeOfSelTrig()+1);
+	  edgedata.Elem(edgedata.GetEdgeNum(p1,p2)).SetStatus(ED_EXCLUDED);
+	}
+      else if (stldoctor.selectmode == 3 || stldoctor.selectmode == 4)
+	{
+	  int i;
+	  for (i = 1; i <= selectedmultiedge.Size(); i++)
+	    {
+	      int p1 = selectedmultiedge.Get(i).i1;
+	      int p2 = selectedmultiedge.Get(i).i2;
+	      edgedata.Elem(edgedata.GetEdgeNum(p1,p2)).SetStatus(ED_EXCLUDED);
+	    }
+	}
+    }
+}
+
+void STLGeometry :: STLDoctorUndefinedEdge()
+{
+  StoreEdgeData();
+  if (GetSelectTrig() >= 1 && GetSelectTrig() <= GetNT() && GetNodeOfSelTrig())
+    {
+      if (stldoctor.selectmode == 1)
+	{
+	  int p1 = GetTriangle(GetSelectTrig()).PNum(GetNodeOfSelTrig());
+	  int p2 = GetTriangle(GetSelectTrig()).PNumMod(GetNodeOfSelTrig()+1);
+	  edgedata.Elem(edgedata.GetEdgeNum(p1,p2)).SetStatus(ED_UNDEFINED);
+	}
+      else if (stldoctor.selectmode == 3 || stldoctor.selectmode == 4)
+	{
+	  int i;
+	  for (i = 1; i <= selectedmultiedge.Size(); i++)
+	    {
+	      int p1 = selectedmultiedge.Get(i).i1;
+	      int p2 = selectedmultiedge.Get(i).i2;
+	      edgedata.Elem(edgedata.GetEdgeNum(p1,p2)).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 pointtree (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);
+      pointtree.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);
+	  
+      pointtree.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);
+
+	  int nodenr;
+	  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 pointtree (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;
+      pointtree.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);
+	  
+      pointtree.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 p1 = GetTriangle(GetSelectTrig()).PNum(GetNodeOfSelTrig());
+      int p2 = GetTriangle(GetSelectTrig()).PNumMod(GetNodeOfSelTrig()+1);
+      if (!IsExternalEdge(p1,p2)) {AddExternalEdge(p1,p2);}
+    }
+}
+
+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 p1 = l->PNum(j);
+	      int p2 = l->PNum(j+1);
+
+	      if (!IsExternalEdge(p1,p2)) {AddExternalEdge(p1,p2);}	      
+	    }
+	}
+    }
+}
+
+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 p1 = l->PNum(j);
+	      int p2 = l->PNum(j+1);
+
+	      if (!IsExternalEdge(p1,p2)) {AddExternalEdge(p1,p2);}	      
+	    }
+	}
+    }
+}
+
+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 p1 = l->PNum(j);
+	      int p2 = l->PNum(j+1);
+
+	      if (!IsExternalEdge(p1,p2)) {AddExternalEdge(p1,p2);}	      
+	    }
+	}
+    }
+}
+
+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 p1 = l->PNum(j);
+	      int p2 = l->PNum(j+1);
+
+	      if (IsExternalEdge(p1,p2)) {DeleteExternalEdge(p1,p2);}	      
+	    }
+	}
+    }
+}
+
+void STLGeometry :: AddExternalEdgesFromGeomLine()
+{
+  StoreExternalEdges();
+  if (GetSelectTrig() >= 1 && GetSelectTrig() <= GetNT())
+    {
+      int p1 = GetTriangle(GetSelectTrig()).PNum(GetNodeOfSelTrig());
+      int p2 = GetTriangle(GetSelectTrig()).PNumMod(GetNodeOfSelTrig()+1);
+
+      if (IsEdge(p1,p2))
+	{
+	  int edgenum = IsEdgeNum(p1,p2);
+	  if (!IsExternalEdge(p1,p2)) {AddExternalEdge(p1,p2);}
+	  
+	  int noend = 1;
+	  int startp = p1;
+	  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 = p2;
+	  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 p1 = GetTriangle(GetSelectTrig()).PNum(GetNodeOfSelTrig());
+      int p2 = GetTriangle(GetSelectTrig()).PNumMod(GetNodeOfSelTrig()+1);
+      if (IsExternalEdge(p1,p2)) {DeleteExternalEdge(p1,p2);}
+    }
+}
+
+void STLGeometry :: DeleteExternalEdgeInVicinity()
+{
+  StoreExternalEdges();
+  if (!stldoctor.showvicinity || vicinity.Size() != GetNT()) {return;}
+
+  int i, j, k, p1, p2;
+  
+  for (i = 1; i <= GetNT(); i++)
+    {
+      if (vicinity.Elem(i))
+	{
+	  for (j = 1; j <= 3; j++)
+	    {
+	      p1 = GetTriangle(i).PNum(j);
+	      p2 = GetTriangle(i).PNumMod(j+1);
+
+	      if (IsExternalEdge(p1,p2))
+		{
+		  DeleteExternalEdge(p1,p2);
+		}
+	    }
+	}
+    }
+}
+
+void STLGeometry :: BuildExternalEdgesFromEdges()
+{
+  StoreExternalEdges();
+
+  if (GetNE() == 0) {PrintWarning("Edges possibly not generated!");}
+
+  int i, p1, p2;
+  externaledges.SetSize(0);
+
+  for (i = 1; i <= GetNE(); i++)
+    {
+      STLEdge e = GetEdge(i);
+      AddExternalEdge(e.PNum(1), e.PNum(2));
+    }
+
+}
+
+
+void STLGeometry :: AddExternalEdge(int p1, int p2)
+{
+  externaledges.Append(twoint(p1,p2));
+}
+
+void STLGeometry :: DeleteExternalEdge(int p1, int p2)
+{
+
+  int i;
+  int found = 0;
+  for (i = 1; i <= NOExternalEdges(); i++)
+    {
+      if ((GetExternalEdge(i).i1 == p1 && GetExternalEdge(i).i2 == p2) ||
+	  (GetExternalEdge(i).i1 == p2 && GetExternalEdge(i).i2 == p1)) {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 p1, int p2)
+{
+  int i;
+  for (i = 1; i <= NOExternalEdges(); i++)
+    {
+      if ((GetExternalEdge(i).i1 == p1 && GetExternalEdge(i).i2 == p2) ||
+	  (GetExternalEdge(i).i1 == p2 && GetExternalEdge(i).i2 == p1)) {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& p1 = GetPoint(tr.PNum(1));
+      const Point3d& p2 = GetPoint(tr.PNum(2));
+      const Point3d& p3 = GetPoint(tr.PNum(3));
+
+      Vec3d normal = Cross (p2-p1, p3-p1);
+      
+      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) 
+    {
+      int i, m;
+      Point<3> p1, p2;
+      for (i = 1; i <= n; i++)
+	{
+	  fin >> p1(0); fin >> p1(1); fin >> p1(2);
+	  fin >> p2(0); fin >> p2(1); fin >> p2(2);
+	  AddMarkedSeg(p1,p2);      
+	}
+    }
+}
+
+void STLGeometry :: SaveMarkedTrigs()
+{
+  PrintFnStart("save marked trigs to file 'markedtrigs.ng'");
+  ofstream fout("markedtrigs.ng");
+
+  int n = GetNT();
+  fout << n << endl;
+
+  int i, m;
+  for (i = 1; i <= n; i++)
+    {
+      fout << IsMarkedTrig(i) << "\n";
+    }
+
+  n = GetNMarkedSegs();
+  fout << n << endl;
+
+  Point<3> p1,p2;
+  for (i = 1; i <= n; i++)
+    {
+      GetMarkedSeg(i,p1,p2);
+      fout << p1(0) << " " << p1(1) << " " << p1(2) << "  ";
+      fout << p2(0) << " " << p2(1) << " " << p2(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 p1, p2;
+  for (j = 1; j <= NONeighbourTrigs(i); j++)
+    {
+      GetTriangle(i).GetNeighbourPoints(GetTriangle(NeighbourTrig(i,j)), p1, p2);
+      
+      if (!IsEdge(p1,p2) && 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 p1, p2;
+  for (i = 1; i <= GetNT(); i++)
+    {
+      found = 0;
+      for (j = 1; j <= NONeighbourTrigs(i); j++)
+	{
+	  GetTriangle(i).GetNeighbourPoints(GetTriangle(NeighbourTrig(i,j)), p1, p2);
+
+	  if (!IsEdge(p1,p2))
+	    {
+              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 p1, p2;
+  
+  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)),p1,p2);
+		      if (Dist(GetPoint(p1),GetPoint(p2)) >= maxlen)
+			{
+			  foundtrig = NeighbourTrig(i,j);
+			  maxlen = Dist(GetPoint(p1),GetPoint(p2));
+			}
+		    }
+		}
+	      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, j, 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 Point3d *trip1[3], *trip2[3];	
+	  Point3d 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 p1, int p2)
+{
+  int i,j;
+  for (i = 1; i <= GetNEPP(p1); i++)
+    {
+      for (j = 1; j <= GetNEPP(p2); j++)
+	{
+	  if (GetEdgePP(p1,i) == GetEdgePP(p2,j)) {return 1;}
+	}
+    }
+  return 0;
+}
+
+int STLGeometry :: IsEdgeNum(int p1, int p2)
+{
+  int i,j;
+  for (i = 1; i <= GetNEPP(p1); i++)
+    {
+      for (j = 1; j <= GetNEPP(p2); j++)
+	{
+	  if (GetEdgePP(p1,i) == GetEdgePP(p2,j)) {return GetEdgePP(p1,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;
+  double ang;
+  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");
+
+  double ang;
+  int i;
+  int t1,t2;
+
+  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, j;
+  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, p1, p2;
+  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),p1,p2);
+		    AddEdge(p1,p2);
+		    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;
+  int edgecnt = 0;
+  int found;
+  int rev; //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 p1, p2;
+	  line->GetSeg(ii,p1,p2);
+	  //	  (*mycout) << "SEG " << p1 << " - " << p2 << endl;
+	}
+    }
+
+  PopStatus();
+}
+
+int STLGeometry :: GetNOBodys()
+{
+  int markedtrigs = 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 (markedtrigs < 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++;
+      markedtrigs++;
+      bodynum.Elem(starttrig) = bodycnt;
+      todolist.Append(starttrig);
+      int p1, p2;
+
+      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;
+		      markedtrigs++;
+		    }
+		}
+	    }
+	  
+	  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 markedtrigs = 0;
+  int starttrig;
+  int laststarttrig = 1;
+  int i, k, nnt;
+  facecnt = 0;
+
+
+  for (i = 1; i <= GetNT(); i++)
+    GetTriangle(i).SetFaceNum(0);
+
+
+  while (markedtrigs < 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++;
+      markedtrigs++;
+      GetTriangle(starttrig).SetFaceNum(facecnt);
+      todolist.Append(starttrig);
+      int p1, p2;
+
+      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,p1,p2);
+		      if (!IsEdge(p1,p2))
+			{
+			  nextlist.Append(nnt);
+			  nt.SetFaceNum(facecnt);
+			  markedtrigs++;
+			}
+		    }
+		}
+	    }
+	  
+	  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);
+      
+      Vec3d ng1 = trig.GeomNormal(points);
+      ng1 /= (ng1.Length() + 1e-24);
+
+      for (j = 1; j <= 3; j++)
+	{ 
+	  int nbt = NeighbourTrig (i, j);
+	  
+	  Vec3d 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 p1, p2, 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, p1, p2);
+		      if (IsEdge(p1,p2)) 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, p1, p2);
+			  if (IsEdge(p1,p2)) 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(p1,p2);
+				  se.SetLeftTrig(tn1);
+				  se.SetRightTrig(tn2);
+				  int edgenum = AddEdge(se);
+				  AddEdgePP(p1,edgenum);
+				  AddEdgePP(p2,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, p1, p2);
+		      if (IsEdge(p1,p2)) 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, p1, p2);
+			if (IsEdge(p1,p2)) 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(p1,p2);
+				se.SetLeftTrig(tn1);
+				se.SetRightTrig(tn2);
+				int edgenum = AddEdge(se);
+				AddEdgePP(p1,edgenum);
+				AddEdgePP(p2,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)), p1, p2);
+			  if (IsEdge(p1,p2)) {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, p1, p2, locindex1, locindex2;
+
+  //(*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, p1, p2);
+	      if (p2 == p) {Swap(p1,p2);}
+	      if (p1 != p) {PrintSysError("In GetSortedTrianglesAroundPoint!!!");}
+	      
+	      for (j = 1; j <= 3; j++) 
+		{
+		  if (at.PNum(j) == p1) {locindex1 = j;};
+		  if (at.PNum(j) == p2) {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;
+  
+  int np = GetNP();
+  double maxerr0, maxerr;
+
+  for (i = 1; i <= np; 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 0000000000..c9bfb1e535
--- /dev/null
+++ b/contrib/Netgen/libsrc/stlgeom/stlgeom.hpp
@@ -0,0 +1,450 @@
+#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 <gprim.hpp>
+#include <meshing.hpp>
+
+
+
+namespace netgen
+{
+  extern int IsInArray(int n, const ARRAY<int>& ia);
+  extern int AddIfNotExists(ARRAY<int>& list, int x);
+
+
+#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
+  {
+    // 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();
+
+
+
+    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> & p1, const Point<3> & p2) 
+    {
+      markedsegs.Append(p1);markedsegs.Append(p2);
+    }
+
+    void GetMarkedSeg(int i, Point<3> & p1, Point<3> & p2) 
+    {
+      p1=markedsegs.Get(i*2-1); 
+      p2=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;
+  };
+ 
+
+#include "meshstlsurface.hpp"
+
+
+  extern int STLMeshingDummy (STLGeometry* stlgeometry, Mesh*& mesh,
+			      int perfstepsstart, int perfstepsend, char* optstring);
+
+
+}
+#endif
diff --git a/contrib/Netgen/libsrc/stlgeom/stlgeomchart.cpp b/contrib/Netgen/libsrc/stlgeom/stlgeomchart.cpp
new file mode 100644
index 0000000000..c2f64f5f1f
--- /dev/null
+++ b/contrib/Netgen/libsrc/stlgeom/stlgeomchart.cpp
@@ -0,0 +1,801 @@
+//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,m,ctl;
+
+  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 mindist, 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++)
+    {
+      int j;
+      //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 j,k, 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);
+  
+  
+  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;
+  STLTriangle tt;
+  
+  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 addedges = 0;
+  int p1, p2, 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;
+
+	      int worked = 0;
+	      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, p1, p2);
+		  if (IsEdge(p1,p2)) 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, p1, p2);
+		  if (IsEdge(p1,p2)) 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 0000000000..863019c04c
--- /dev/null
+++ b/contrib/Netgen/libsrc/stlgeom/stlgeommesh.cpp
@@ -0,0 +1,1592 @@
+//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> & p1, int t1, 
+				     const Point<3> & p2, 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(p1,pbtw);
+	
+	  edgepoints.Add1(edgecnt,pbtw);
+	  edgepointdists.Add1(edgecnt,Dist(pbtw,p1));
+	  edgepointorigines.Add1(edgecnt,0);
+	  edgepointoriginps.Add1(edgecnt,0);
+	}
+    }
+
+  int finished = 0;
+  int endpointorigine = 0;
+  int endpointoriginp = 0;
+  double endpointmindist = 1E50;
+
+  int cnt = 0;
+  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;
+		  int 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;
+	      for (m = 1; m <= divisions; m++)
+		{
+		  const Point3d& p = edgepoints.Get(en,m);
+		  if (Dist(p2,p) + edgepointdists.Get(en,m) < mindist)
+		    {mindist = Dist(p2,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(p2);
+  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(p1);
+
+  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(p1,p2);}
+
+  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 > & points,
+					ARRAY<Point3d > & points3d,
+					ARRAY<INDEX_2> & lines, double h)
+{
+  int i, j;
+  twoint seg, newseg;
+  int zone;
+  int psize;
+  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);
+	      points.Append (p2d);
+	      
+	      lpi = points.Size();
+	      ha_points.Elem(pi) = lpi;
+	    }
+	  else
+	    lpi = ha_points.Get(pi);
+
+	  i2.I(j) = lpi;
+	}
+      lines.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, j, k;
+  
+  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;
+	  int ci = 1;
+	  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, k;
+  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, k;
+  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;
+
+  //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 p1,p2,p3,p4;
+  Point<3> p1p, p2p, p3p, p4p;
+  double mindist, ang;
+  Vec<3> n, ntn;
+  double rzyl, sinang, 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,p1,p2,p3);	    	    
+	      
+	      //checken, ob p1-p2 eine Kante sind
+	      if (IsEdge(p1,p2)) continue;
+	      
+	      p4 = trig.PNum(1) + trig.PNum(2) + trig.PNum(3) - p1 - p2;
+	      
+	      p1p = GetPoint(p1); p2p = GetPoint(p2); 
+	      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(p1) = min2(minh.Elem(p1),localh);
+		  minh.Elem(p2) = min2(minh.Elem(p2),localh);
+		}
+	      
+	      //if (localh < 0.2) {localh = 0.2;}
+	      mesh.RestrictLocalHLine(p1p, p2p, localh);
+	    }
+	  
+	}
+    }
+  PrintMessage(7, "done\nATLAS H: nmin local h=", mincalch);
+  PrintMessage(7, "ATLAS H: max local h=", maxcalch);
+  PrintMessage(7, "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 p1,p2,p3,p4;
+  Point3d p1p, p2p, p3p, p4p;
+  double mindist, ang;
+  Vec3d n, ntn;
+  double rzyl, sinang, 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,p1,p2,p3);	    	    
+	      
+	      //checken, ob p1-p2 eine Kante sind
+	      if (IsEdge(p1,p2)) continue;
+	      
+	      p4 = trig.PNum(1) + trig.PNum(2) + trig.PNum(3) - p1 - p2;
+	      
+	      p1p = GetPoint(p1); p2p = GetPoint(p2); 
+	      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(p1) = min2(minh.Elem(p1),localh);
+		  minh.Elem(p2) = min2(minh.Elem(p2),localh);
+		}
+	      
+	      //if (localh < 0.2) {localh = 0.2;}
+	      mesh.RestrictLocalHLine(p1p, p2p, localh);
+	      
+	      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, p3p2;
+      double mindist = 1E50;
+      
+      PrintMessage(7,"build search tree...");
+      Box3dTree* searchtree = 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);
+
+	  searchtree->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);
+	  searchtree->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 ecnt = 0;
+      int lp1, lp2;
+      int i;
+      Vec3d v1,v2;
+      double rzyl;
+      double mincalch = 1E50;
+      double 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& p1 = GetPoint(GetLine(i)->StartP());
+	  const Point3d& p2 = GetPoint(GetLine(i)->EndP());
+	  
+	  if (l != 0)
+	    {
+	      minhl = min2(minhl,l*linefact);
+	      
+	      mesh.RestrictLocalH(p1, l*linefact);
+	      mesh.RestrictLocalH(p2, 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 & p1 = plimes1.Get(j);
+	  double boxs = mesh.GetH (plimes1.Get(j)) * limessafety;
+
+	  Point3d pmin = p1 - Vec3d (boxs, boxs, boxs);
+	  Point3d pmax = p1 + 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 & p1 = plimes1.Get(j);
+	    double his = mesh.GetH (plimes1.Get(j));
+
+	    double xmin = p1.X() - his * limessafety;
+	    double xmax = p1.X() + his * limessafety;	      
+	    double ymin = p1.Y() - his * limessafety;
+	    double ymax = p1.Y() + his * limessafety;	      
+	    double zmin = p1.Z() - his * limessafety;
+	    double zmax = p1.Z() + his * limessafety;	      
+
+	    for (k = 1; k <= plimes2.Size(); k++)
+	    {
+	    const Point3d & p2 = plimes2.Get(k);
+	    if (p2.X() >= xmin && p2.X() <= xmax &&
+	    p2.Y() >= ymin && p2.Y() <= ymax &&
+	    p2.Z() >= zmin && p2.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;}
+	    }
+	}
+    }
+
+}
+
+
+//void * STLMeshingDummy (void *)
+int STLMeshingDummy (STLGeometry* stlgeometry, Mesh*& mesh,
+			    int perfstepsstart, int perfstepsend, char* optstring)
+{
+  if (perfstepsstart > perfstepsend) return 0;
+
+  multithread.terminate = 0;
+  int success = 1;
+  //int trialcntouter = 0;
+
+  if (perfstepsstart <= MESHCONST_MESHEDGES)
+    {
+
+      mesh = new Mesh();
+      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 (stlparam.resthsurfmeshcurvfac);
+	      mparam.optimize2d = "cmsmSm";
+	      STLSurfaceOptimization (*stlgeometry, *mesh, mparam);
+#ifdef STAT_STREAM
+	      (*statout) << GetTime() << " & ";
+#endif
+
+#ifdef OPENGL
+	      extern void Render();
+	      Render();
+#endif	      
+	    }
+	  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 ();
+	    }
+	  
+	  
+	  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
+
+#ifdef OPENGL
+	  extern void Render();
+	  Render();
+#endif	      
+
+	}
+    }
+  
+
+  return 0;
+}
+
+
+
+}
diff --git a/contrib/Netgen/libsrc/stlgeom/stlline.cpp b/contrib/Netgen/libsrc/stlgeom/stlline.cpp
new file mode 100644
index 0000000000..0fe01967e6
--- /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, 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 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, en;
+  int j, i, k;
+  int oldend;
+  int newend = 1;
+  int pnew, ennew;
+
+  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 0000000000..70393ca060
--- /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/stltool.cpp b/contrib/Netgen/libsrc/stlgeom/stltool.cpp
new file mode 100644
index 0000000000..c19fed4ca0
--- /dev/null
+++ b/contrib/Netgen/libsrc/stlgeom/stltool.cpp
@@ -0,0 +1,1288 @@
+#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;
+      double nearest = 1E50;
+      int fi = 0;
+      int j;
+      
+      for (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 i, 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 0000000000..278a7ce4ee
--- /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 0000000000..1d5315fbec
--- /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), 
+    neighbourtrigs(), trigsperpoint()
+{
+  ;
+}
+
+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)
+{
+  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)
+{
+  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)
+{ 
+  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)
+{
+  int 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;
+
+      int 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, j, 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;
+	      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> & trias) const
+{
+  if (searchtree)
+
+    searchtree -> GetIntersecting (box.PMin(), box.PMax(), trias);
+  
+  else
+    {    
+      int i;
+      Box<3> box1 = box;
+      box1.Increase (1e-4);
+
+      trias.SetSize(0);
+   
+      int nt = GetNT();
+      for (i = 1; i <= nt; i++)
+	{
+	  if (box1.Intersect (GetTriangle(i).box))
+	    {
+	      trias.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 j = 0,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 0000000000..80e5a68178
--- /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);
+  void SaveBinary (const char* filename, const char* aname);
+  void SaveSTLE (const char * filename); // 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/visualization/Makefile b/contrib/Netgen/libsrc/visualization/Makefile
new file mode 100644
index 0000000000..a6302092d9
--- /dev/null
+++ b/contrib/Netgen/libsrc/visualization/Makefile
@@ -0,0 +1,13 @@
+#
+# Makefile for visualization library
+#
+src = stlmeshing.cpp mvdraw.cpp vscsg.cpp vsmesh.cpp vsocc.cpp vssolution.cpp meshdoc.cpp
+#
+lib = vis
+libpath = libsrc/visualization
+#
+#
+include ../makefile.inc
+#
+
+
diff --git a/contrib/Netgen/libsrc/visualization/meshdoc.cpp b/contrib/Netgen/libsrc/visualization/meshdoc.cpp
new file mode 100644
index 0000000000..4c0f064c05
--- /dev/null
+++ b/contrib/Netgen/libsrc/visualization/meshdoc.cpp
@@ -0,0 +1,615 @@
+#include <mystdlib.h>
+
+#include <meshing.hpp>
+
+#include "incvis.hpp"
+
+
+
+namespace netgen
+{
+#include "mvdraw.hpp"
+#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 ()
+{
+  int i, j, k;
+
+  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, k;
+ 
+
+  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);
+  
+  static float matcol0[] = { 0.0f, 0.0f, 0.0f, 1.0f };
+  static float matcol1[] = { 1.0f, 1.0f, 1.0f, 1.0f };
+  static float matcolsel[] = { 1.0f, 0.0f, 0.0f, 1.0f };
+  static float matcolnosel[] = { 0.0f, 1.0f, 0.0f, 1.0f };
+  
+  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.p1);
+      const Point3d & p2 = mesh->Point(seg.p2);
+
+      if (edgedist.Get(seg.p1) <= markedgedist &&
+	  edgedist.Get(seg.p2) <= 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, 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);
+
+  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.p1 == selpoint && seg.p2 == selpoint2 ||
+	  seg.p2 == selpoint && seg.p1 == 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.p1), edgedist.Get(seg.p2));
+	  edist++;
+
+	  if (edgedist.Get(seg.p1) > edist)
+	    {
+	      edgedist.Elem(seg.p1) = edist;
+	      changed = 1;
+	    }
+	  if (edgedist.Get(seg.p2) > edist)
+	    {
+	      edgedist.Elem(seg.p2) = edist;
+	      changed = 1;
+	    }
+	}	    
+    }
+  while (changed);
+}
+
+int VisualSceneMeshDoctor :: IsSegmentMarked (int segnr) const
+{
+  const Segment & seg = mesh->LineSegment(segnr);
+  return (edgedist.Get(seg.p1) <= markedgedist &&
+	  edgedist.Get(seg.p2) <= markedgedist);
+}
+}
diff --git a/contrib/Netgen/libsrc/visualization/meshdoc.hpp b/contrib/Netgen/libsrc/visualization/meshdoc.hpp
new file mode 100644
index 0000000000..5cc11aef78
--- /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 0000000000..265dc970b4
--- /dev/null
+++ b/contrib/Netgen/libsrc/visualization/mvdraw.cpp
@@ -0,0 +1,1335 @@
+#include <mystdlib.h>
+#include <myadt.hpp>
+#include <meshing.hpp>
+#include <csg.hpp>
+#include <geometry2d.hpp>
+#include <stlgeom.hpp>
+
+#include "incvis.hpp"
+
+
+
+namespace netgen
+{
+
+#include "mvdraw.hpp"
+
+  Point3d VisualScene :: center;
+  double VisualScene :: rad;
+  GLdouble VisualScene :: backcolor;
+  GLuint VisualScene :: fontbase = 0;
+
+  // 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];
+
+
+
+  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;
+
+
+
+  extern STLGeometry * stlgeometry;
+  extern AutoPtr<SplineGeometry2d> geometry2d;
+  extern AutoPtr<Mesh> mesh;
+  extern ARRAY<SpecialPoint> specpoints;
+
+
+  VisualScene :: VisualScene ()
+  {
+    changeval = -1;
+    backcolor = 0;
+  }
+
+
+  VisualScene :: ~VisualScene()
+  {
+    ;
+  }
+
+
+  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.95;
+	  GLdouble pvx2 = deltax, pvy2 = -deltay, pvz2 = 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);
+	  glScaled (exp (float (-deltay)/100), 
+		    exp (float (-deltay)/100), 
+		    exp (float (-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 h, double hmin, double hmax,
+				     int logscale)
+  {
+    double value;
+
+    if (!logscale)
+      value = (h - hmin) / (hmax - hmin);
+    else
+      {
+	if (hmax <= 0) hmax = 1;
+	if (hmin <= 0) hmin = 1e-4 * hmax;
+	value = (log(fabs(h)) - log(hmin)) / (log(hmax) - log(hmin));
+      }
+
+    if (!invcolor)
+      value = 1 - value;
+
+    glTexCoord1f ( 0.999 * value + 0.001);
+    // 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)
+  {
+
+    static const double colp[][3] =
+      {
+	{ 1, 0, 0 },
+	{ 1, 1, 0 },
+	{ 0, 1, 0 },
+	{ 0, 1, 1 },
+	{ 0, 0, 1 },
+      };
+  
+
+
+    if (ntexcols != 1024)
+      {
+	ntexcols = 1024;
+	
+	// glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
+	glGenTextures (1, &coltexname);
+	glBindTexture (GL_TEXTURE_1D, coltexname);
+	glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+	glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+	glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
+	
+	for (int level = 0; level <= 11; level++)
+	  {
+	    ncols = 2048 >> level;
+	    cout << "ncols = " << ncols << endl;
+
+	    colortexture = new GLubyte[4*ncols+12];
+
+	    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];
+		for (int j = 0; j < 3; j++)
+		  col[j] = (1-r) * colp[iv][j] + r * colp[iv+1][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);
+		
+		if (ncols > 20)
+		  if ( i % (ncols / 10) == 0)
+		    {
+		      colortexture[4*i] = GLubyte (0);
+		      colortexture[4*i+1] = GLubyte (0);
+		      colortexture[4*i+2] = GLubyte (0);
+		      colortexture[4*i+4] = GLubyte (0);
+		      colortexture[4*i+5] = GLubyte (0);
+		      colortexture[4*i+6] = GLubyte (0);
+		    }
+	      }
+	    
+	    glTexImage1D (GL_TEXTURE_1D, level, 4, ncols, 0, GL_RGBA, GL_UNSIGNED_BYTE, colortexture);
+	  }
+      }
+    
+    glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, typ);  // DECAL or MODULATE
+    glBindTexture (GL_TEXTURE_1D, coltexname);
+  }
+  */
+
+
+  void VisualScene :: CreateTexture (int ncols, int linear, int typ)
+  {
+    if (ncols < 2) ncols = 2;
+
+    if (linear) ncols = 32;
+    else   ncols = 8;
+
+    if (ntexcols != ncols) 
+      {
+	if (colortexture) 
+	  {
+	    glDeleteTextures (1, &coltexname);
+	    delete colortexture;
+	  }
+      
+	ntexcols = ncols;
+      
+	colortexture = new GLubyte[4*ncols+12];
+
+	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];
+	    for (int j = 0; j < 3; j++)
+	      col[j] = (1-r) * colp[iv][j] + r * colp[iv+1][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);
+
+	glGenTextures (1, &coltexname);
+	glBindTexture (GL_TEXTURE_1D, coltexname);
+
+	glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+	glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+	glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+	glTexImage1D (GL_TEXTURE_1D, 0, 4, ncols, 0, GL_RGBA, GL_UNSIGNED_BYTE, colortexture);
+      }
+
+    glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, typ);  // DECAL or MODULATE
+
+    glBindTexture (GL_TEXTURE_1D, coltexname);
+    if (linear)
+      {
+	glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+	glTexParameteri (GL_TEXTURE_1D, 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);
+      }
+  }
+
+
+
+
+
+  /*
+  void VisualScene :: CreateTexture (int ncols, int linear, int typ)
+  {
+    if (ncols < 2) ncols = 2;
+
+    if (linear) ncols = 32;
+    else   ncols = 8;
+
+    if (ntexcols != ncols) 
+      {
+	if (colortexture) 
+	  {
+	    glDeleteTextures (1, &coltexname);
+	    delete colortexture;
+	  }
+      
+	ntexcols = ncols;
+      
+	colortexture = new GLubyte[4*ncols+12];
+
+	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];
+	    for (int j = 0; j < 3; j++)
+	      col[j] = (1-r) * colp[iv][j] + r * colp[iv+1][j];
+	  
+	    colortexture[4*i+4] = GLubyte (255 * col[0]);
+	    colortexture[4*i+5] = GLubyte (255 * col[1]);
+	    colortexture[4*i+6] = GLubyte (255 * col[2]);
+	    colortexture[4*i+7] = GLubyte(255);
+	  }
+	for (int j = 0; j < 4; j++)
+	  {
+	    colortexture[j] = colortexture[4+j];
+	    colortexture[ncols*4+4+j] = colortexture[ncols*4+j];
+	  }
+
+	//      glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
+
+	glGenTextures (1, &coltexname);
+	glBindTexture (GL_TEXTURE_1D, coltexname);
+
+	glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+	glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+	glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+	glTexImage1D (GL_TEXTURE_1D, 0, 4, ncols, 0, GL_RGBA, GL_UNSIGNED_BYTE, colortexture+4);
+	int bcol[] = { 0, 0, -1, -1 };
+	glTexParameteriv (GL_TEXTURE_1D, GL_TEXTURE_BORDER_COLOR, bcol);
+      }
+
+    glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, typ);  // DECAL or MODULATE
+
+    glBindTexture (GL_TEXTURE_1D, coltexname);
+    if (linear)
+      {
+	glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+	glTexParameteri (GL_TEXTURE_1D, 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);
+      }
+  }
+  */
+
+
+
+
+
+  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;
+
+    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);
+	// glTexCoord1f ( 0.999 * (x-minx) / (maxx-minx) + 0.001);
+
+	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 (strlen (buf), GL_UNSIGNED_BYTE, 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);
+
+    GLfloat textcol[3] = { 1 - backcolor,
+			   1 - backcolor,
+			   1 - backcolor };
+    glColor3fv (textcol);
+
+    glLineWidth (1.0f);
+
+    float len = 1;
+    glBegin(GL_LINES);
+    glVertex3f (0.0f, 0.0f, 0.0f);
+    glVertex3f (len, 0.0f, 0.0f);
+    glVertex3f (0.0f, 0.0f, 0.0f);
+    glVertex3f (0.0f, len, 0.0f);
+    glVertex3f (0.0f, 0.0f, 0.0f);
+    glVertex3f (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 (strlen (buf), GL_UNSIGNED_BYTE, buf);
+    glRasterPos3d (0.0f, len, 0.0f);
+    sprintf (buf, "y");
+    glCallLists (strlen (buf), GL_UNSIGNED_BYTE, buf);
+    glRasterPos3d (0.0f, 0.0f, len);
+    sprintf (buf, "z");
+    glCallLists (strlen (buf), GL_UNSIGNED_BYTE, 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 (-6.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[20];
+
+    glRasterPos3d (0.0f, 0.0f, 0.0f);
+    sprintf (buf, "Netgen 4.4");
+    glCallLists (strlen (buf), GL_UNSIGNED_BYTE, buf);
+
+    glPopAttrib ();
+
+    glMatrixMode (GL_PROJECTION); 
+    glPopMatrix();
+    glMatrixMode (GL_MODELVIEW); 
+    glPopMatrix();
+    glEnable (GL_DEPTH_TEST);
+  }
+
+
+
+
+
+  /* *********************** 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++)
+	  glVertex3f (points[j](0), points[j](1), 0);
+	glEnd(); 
+      }
+
+    glColor3f (1, 0, 0);
+
+    for (int i = 1; i <= geometry2d->GetSplines().Size(); i++)
+      {
+	int other = geometry2d->GetSplines().Get(i)->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++)
+	      {
+		glVertex3f (points[j](0), points[j](1), 0);
+		glVertex3f (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();
+  }
+
+
+
+
+
+
+
+
+
+
+  /* *********************** 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 ();
+  }
+
+
+
+
+
+
+
+
+
+
+
+
+  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.p1].X() );
+	    glVertex3dv ( &(*mesh)[seg.p2].X() );
+	  }
+	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.p1);
+	    const Point3d p2 = mesh -> Point (seg.p2);
+
+	    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);
+	  }
+      
+	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 (strlen (buf), GL_UNSIGNED_BYTE, 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).p1));
+	for (int i = 1; i <= mesh->GetNSeg(); i++)
+	  {
+	    box.AddPoint (mesh->Point (mesh->LineSegment(i).p1));
+	    box.AddPoint (mesh->Point (mesh->LineSegment(i).p2));
+	  }
+      }
+    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/visualization/mvdraw.hpp b/contrib/Netgen/libsrc/visualization/mvdraw.hpp
new file mode 100644
index 0000000000..5665533979
--- /dev/null
+++ b/contrib/Netgen/libsrc/visualization/mvdraw.hpp
@@ -0,0 +1,370 @@
+#ifndef FILE_MVDRAW
+#define FILE_MVDRAW
+
+#include "vispar.hpp"
+
+/*
+
+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 drawededges;
+  int drawedpoints;
+  int drawedpointnrs;
+  int drawedtangents;
+  int drawededgenrs;
+
+  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;
+
+  bool whitebackground;
+  int stereo;
+  bool usedispllists;
+  bool drawcoordinatecross;
+  bool drawcolorbar;
+  bool drawnetgenlogo;
+
+  
+public:
+  VisualizationParameters();
+};
+extern VisualizationParameters vispar;
+*/
+
+
+
+
+
+
+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 Point3d center;
+  static double rad;
+
+  static float lookatmat[16];
+  static float transmat[16];
+  static float rotmat[16];
+  static float centermat[16];
+  static float transformationmat[16];
+
+  GLdouble clipplane[4];
+
+  int changeval;
+  static GLdouble backcolor;
+
+public:
+  static GLuint fontbase;
+  static GLubyte * colortexture;
+  static GLuint coltexname;
+  static int ntexcols;
+  // static bool linear_colors;
+  int invcolor;
+
+
+public:
+  VisualScene ();
+  virtual ~VisualScene();
+
+  virtual void BuildScene (int zoomall = 0);
+  virtual void DrawScene ();
+  
+  void CalcTransformationMatrices();
+  void StandardRotation (const char * dir);
+  void ArbitraryRotation (const ARRAY<double> & alpha, const ARRAY<Vec3d> & vec);
+  void ArbitraryRotation (const double alpha, const Vec3d & vec);
+
+  void MouseMove(int oldx, int oldy,
+		 int newx, int newy,
+		 char mode);
+
+  void LookAt (const Point<3> & cam, const Point<3> & obj,
+	       const Point<3> & camup);
+
+  void SetClippingPlane ();
+
+  virtual void MouseDblClick (int px, int py);
+
+  void SetLight ();
+  static void SetBackGroundColor (double col)
+    { backcolor = col; }
+
+  void CreateTexture (int ncols, int linear, int typ = GL_DECAL);
+  void DrawColorBar (double minval, double maxval, int logscale = 0, bool linear = 1);
+  void DrawCoordinateCross ();
+  void DrawNetgenLogo ();
+  void SetOpenGlColor(double val, double valmin, double valmax, int logscale = 0);
+};
+
+
+class VisualSceneGeometry : public VisualScene
+{
+  ARRAY<int> trilists;
+  int selsurf;
+public:
+  VisualSceneGeometry ();
+  virtual ~VisualSceneGeometry ();
+
+  virtual void SelectSurface (int aselsurf);
+  virtual void BuildScene (int zoomall = 0);
+  virtual void DrawScene ();
+};
+
+
+
+class VisualSceneSTLGeometry : public VisualScene
+{
+  ARRAY<int> trilists;
+  
+public:
+  VisualSceneSTLGeometry ();
+  virtual ~VisualSceneSTLGeometry ();
+
+  virtual void BuildScene (int zoomall = 0);
+  virtual void DrawScene ();
+};
+
+
+class VisualSceneGeometry2d : public VisualScene
+{
+public:
+  VisualSceneGeometry2d ();
+  virtual ~VisualSceneGeometry2d ();
+
+  virtual void BuildScene (int zoomall = 0);
+  virtual void DrawScene ();
+};
+
+
+#ifdef OCCGEOMETRY
+class VisualSceneOCCGeometry : public VisualScene
+{
+  ARRAY<int> trilists;
+  ARRAY<int> linelists;
+  int selsurf;
+public:
+  VisualSceneOCCGeometry ();
+  virtual ~VisualSceneOCCGeometry ();
+
+  virtual void BuildScene (int zoomall = 0);
+  virtual void DrawScene ();
+  virtual void MouseDblClick (int px, int py);
+};
+#endif
+
+
+
+
+#ifdef STEP
+class VisualSceneSTEPGeometry : public VisualScene
+{
+  ARRAY<int> gllists;
+  
+public:
+  VisualSceneSTEPGeometry ();
+  virtual ~VisualSceneSTEPGeometry ();
+
+  virtual void BuildScene (int zoomall = 0);
+  virtual void DrawScene ();
+};
+#endif
+
+
+class VisualSceneSTLMeshing : public VisualScene
+{
+  ARRAY<int> trilists;
+  int selecttrig, nodeofseltrig;
+
+public:
+  VisualSceneSTLMeshing ();
+  virtual ~VisualSceneSTLMeshing ();
+
+  virtual void BuildScene (int zoomall = 0);
+  virtual void DrawScene ();
+  virtual void MouseDblClick (int px, int py);
+
+  int seltria;
+};
+
+
+
+
+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 pointnumberlist;
+
+  int tetlist;
+  int prismlist;
+  int pyramidlist;
+  int hexlist;
+
+  int badellist;
+  int identifiedlist;
+  int domainsurflist;
+
+  int vstimestamp, selecttimestamp;
+  int filledtimestamp;
+  int linetimestamp;
+  int pointnumbertimestamp;
+
+  int tettimestamp;
+  int prismtimestamp;
+  int pyramidtimestamp;
+  int hextimestamp;
+
+  int badeltimestamp;
+  int identifiedtimestamp;
+  int domainsurftimestamp;
+
+  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; }
+private:
+  void BuildFilledList();
+  void BuildLineList();
+  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);
+
+
+
+#endif
+
diff --git a/contrib/Netgen/libsrc/visualization/soldata.hpp b/contrib/Netgen/libsrc/visualization/soldata.hpp
new file mode 100644
index 0000000000..44e0d0a56e
--- /dev/null
+++ b/contrib/Netgen/libsrc/visualization/soldata.hpp
@@ -0,0 +1,45 @@
+#ifndef FILE_SOLDATA
+#define FILE_SOLDATA
+
+
+using namespace std;
+
+class 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 GetSurfValue (int /* selnr */,
+			     double /* lam1 */, double /* lam2 */, 
+			     double * /* values */)
+  { return false; }
+
+  void SetMultiDimComponent (int mc)
+  { multidimcomponent = mc; }
+};
+
+
+#endif
+
diff --git a/contrib/Netgen/libsrc/visualization/stlmeshing.cpp b/contrib/Netgen/libsrc/visualization/stlmeshing.cpp
new file mode 100644
index 0000000000..45adac9d75
--- /dev/null
+++ b/contrib/Netgen/libsrc/visualization/stlmeshing.cpp
@@ -0,0 +1,1074 @@
+#include <mystdlib.h>
+#include <myadt.hpp>
+
+#include <linalg.hpp>
+#include <stlgeom.hpp>
+
+#include <meshing.hpp>
+#include <visual.hpp>
+
+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, hmax;
+
+  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.2, 0.2, 0.2, 1 };
+	  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 0000000000..3a35294182
--- /dev/null
+++ b/contrib/Netgen/libsrc/visualization/vispar.hpp
@@ -0,0 +1,89 @@
+#ifndef FILE_VISPAR
+#define FILE_VISPAR
+
+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 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;
+
+  bool whitebackground;
+  int stereo;
+  bool usedispllists;
+  bool drawcoordinatecross;
+  bool drawcolorbar;
+  bool drawnetgenlogo;
+
+  bool use_center_coords;
+  double centerx,centery,centerz;
+
+  
+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 0000000000..3e5910b351
--- /dev/null
+++ b/contrib/Netgen/libsrc/visualization/visual.hpp
@@ -0,0 +1,26 @@
+#ifndef FILE_VISUAL
+#define FILE_VISUAL
+
+/* *************************************************************************/
+/* File:   visual.hpp                                                       */
+/* Author: Joachim Schoeberl                                               */
+/* Date:   02. Dec. 01                                                     */
+/* *************************************************************************/
+
+/* 
+
+Visualization
+
+*/
+
+#include <incvis.hpp>
+
+namespace netgen
+{
+#include "mvdraw.hpp"
+#include "soldata.hpp"
+#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 0000000000..d17c0c38e2
--- /dev/null
+++ b/contrib/Netgen/libsrc/visualization/vscsg.cpp
@@ -0,0 +1,199 @@
+#include <mystdlib.h>
+#include "incvis.hpp"
+
+#include <myadt.hpp>
+#include <meshing.hpp>
+#include <csg.hpp>
+#include <stlgeom.hpp>
+
+
+namespace netgen
+{
+#include "mvdraw.hpp"
+
+/* *********************** Draw Geometry **************** */
+
+
+
+
+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);
+
+  glPopMatrix();
+
+  glDisable(GL_CLIP_PLANE0);
+
+  DrawCoordinateCross ();
+  DrawNetgenLogo ();  
+
+  glFinish();  
+
+}
+
+
+void VisualSceneGeometry :: BuildScene (int zoomall)
+{
+  int i, j, k;
+  
+  Box<3> box;
+  int hasp = 0;
+  for (i = 0; i < geometry->GetNTopLevelObjects(); i++)
+    {
+      const TriangleApproximation & ta =
+	*geometry->GetTriApprox(i);
+      if (!&ta) continue;
+
+      for (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 (i = 0; i < trilists.Size(); i++)
+    glDeleteLists (trilists[i], 1);
+  trilists.SetSize(0);
+
+  for (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 (j = 0; j < ta.GetNT(); j++)
+	    {
+	      
+	      for (k = 0; k < 3; k++)
+		{
+		  int pi = ta.GetTriangle(j)[k];
+		  glNormal3f (ta.GetNormal (pi)(0),
+			      ta.GetNormal (pi)(1),
+			      ta.GetNormal (pi)(2));
+		  glVertex3f (ta.GetPoint(pi)(0),
+			      ta.GetPoint(pi)(1),
+			      ta.GetPoint(pi)(2));
+		}
+	    }
+	  glEnd ();
+	}
+      glEndList ();
+    }
+
+}
+
+
+
+
+
+}
diff --git a/contrib/Netgen/libsrc/visualization/vsmesh.cpp b/contrib/Netgen/libsrc/visualization/vsmesh.cpp
new file mode 100644
index 0000000000..c53e5223be
--- /dev/null
+++ b/contrib/Netgen/libsrc/visualization/vsmesh.cpp
@@ -0,0 +1,3114 @@
+#include <mystdlib.h>
+#include "incvis.hpp"
+
+
+#include <myadt.hpp>
+#include <meshing.hpp>
+#include <csg.hpp>
+#include <stlgeom.hpp>
+
+namespace netgen
+{
+
+#include "mvdraw.hpp"
+
+
+  // #define FAST3DELEMENTS
+
+
+
+  extern AutoPtr<Mesh> mesh;
+  extern STLGeometry * stlgeometry;
+  VisualSceneMesh vsmesh;
+
+
+
+  VisualSceneMesh :: VisualSceneMesh ()
+    : VisualScene()
+  {
+    filledlist = 0;
+    linelist = 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();
+    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;
+  }
+
+  VisualSceneMesh :: ~VisualSceneMesh ()
+  {
+    ;
+  }
+
+  
+  // ARRAY<Point3d> drawel;
+
+  void VisualSceneMesh :: DrawScene ()
+  {
+    if (!mesh) 
+      {
+	VisualScene::DrawScene();
+	return;
+      }
+    
+    lock = NULL;
+    
+    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);
+  
+  
+    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 ();
+	  }
+	glCallList (filledlist);
+      }
+
+    if (vispar.drawbadels)
+      glCallList (badellist);
+
+    if (vispar.drawprisms)
+      {
+	BuildPrismList ();
+	static float prismcol[] = { 1.0f, 1.0f, 0.0f, 1.0f };
+	glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, prismcol);
+	glLineWidth (1.0f);
+	glCallList (prismlist);
+      }
+
+    if (vispar.drawpyramids)
+      {
+	BuildPyramidList ();
+	static float pyramidcol[] = { 1.0f, 1.0f, 0.0f, 1.0f };
+	glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, pyramidcol);
+	glLineWidth (1.0f);
+	glCallList (pyramidlist);
+      }
+
+    if (vispar.drawhexes)
+      {
+	BuildHexList ();
+	static float hexcol[] = { 1.0f, 0.0f, 0.0f, 1.0f };
+	glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, hexcol);
+	glLineWidth (1.0f);
+	glCallList (hexlist);
+      }
+
+    if (vispar.drawtets)
+      {
+	BuildTetList ();
+	static float tetcol[] = { 1.0f, 1.0f, 0.0f, 1.0f };
+	glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, tetcol);
+	glLineWidth (1.0f);
+	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 ();
+
+	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)
+      {
+	GLfloat matcoledge[] = { 0, 0, 1, 1 };
+	GLfloat matcolsingedge[] = { 1, 0, 1, 1 };
+
+	glEnable (GL_POLYGON_OFFSET_LINE);
+	glPolygonOffset (1, -1);
+	glLineWidth (2);
+      
+
+	for (int i = 1; i <= mesh->GetNSeg(); i++)
+	  {
+	    const Segment & seg = mesh->LineSegment(i);
+	    const Point3d & p1 = (*mesh)[seg.p1];
+	    const Point3d & p2 = (*mesh)[seg.p2];
+
+	    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.edgenr == seledge)
+	      glLineWidth(5);
+	    else
+	      glLineWidth(2);
+
+	    if (mesh->GetCurvedElements().IsHighOrder()) {
+
+	      int j;
+	      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));
+		}
+	      
+	      glEnd();
+	      
+	    } else {
+	      
+	      glBegin (GL_LINES);
+	      glVertex3f (p1.X(), p1.Y(), p1.Z());
+	      glVertex3f (p2.X(), p2.Y(), p2.Z());
+	      glEnd();
+
+	    }
+	  }
+
+	glLineWidth (2);
+	glDisable (GL_POLYGON_OFFSET_LINE);
+      }
+
+
+    if (selpoint > 0 && selpoint <= mesh->GetNP())
+      {
+	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();
+
+    if (vispar.colormeshsize)
+      DrawColorBar (minh, maxh, 1);
+
+    DrawCoordinateCross ();
+    DrawNetgenLogo ();
+
+    if (lock)
+      {
+	lock -> UnLock();
+	delete lock;
+      }
+
+    glFinish();  
+  }
+
+
+  void VisualSceneMesh :: BuildScene (int zoomall)
+  {
+    if (!mesh)
+      {
+	VisualScene::BuildScene (zoomall);
+	return;
+      }
+      
+    int i, j;
+	
+	
+    Point3d pmin, pmax;
+    static double oldrad = 0;
+	
+    ARRAY<Element2d> faces;
+
+    int meshtimestamp = mesh->GetTimeStamp();
+    if (meshtimestamp > vstimestamp || zoomall)
+      {
+	mesh->GetBox (pmin, pmax, SURFACEPOINT);
+	
+
+	if (selpoint >= 1 && zoomall == 2)
+	  center = mesh->Point (selpoint);
+	else if (vispar.use_center_coords && zoomall == 2)
+	  {
+	    center.X() = vispar.centerx; center.Y() = vispar.centery; center.Z() = vispar.centerz;
+	  }
+	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 > 1.5 * 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 (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);
+	    }
+
+	if (vispar.drawedgenumbers)
+	  {
+	    const MeshTopology & top = mesh->GetTopology();
+	    for (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);
+	      }
+	  }      
+
+
+	if (vispar.drawfacenumbers)
+	  {
+	    const MeshTopology & top = mesh->GetTopology();
+	    ARRAY<int> v;
+	    for (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);
+	      }
+	  }      
+
+
+	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 (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 (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 (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 (i = 1; i <= mesh->GetNE(); i++)
+	  {
+	    Element el = mesh->VolumeElement (i);
+	    int hascp = 0;
+	    for (j = 1; j <= el.GetNP(); j++)
+	      if (el.PNum(j) == vispar.centerpoint)
+		hascp = 1;
+
+	    if (hascp)
+	      {
+		(*testout) << "draw el " << i << " : ";
+		for (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 (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 (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 (i = 1; i <= mesh->GetNSE(); i++)
+	  {
+	    Element2d el = mesh->SurfaceElement(i);
+	    if (!el.BadElement())
+	      continue;
+
+	    int drawel = 1;
+	    for (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 (j = 0; j < 6; j++)
+		    {
+		      glVertex3dv (&mesh->Point (el.PNum(lines[j][0])).X());
+		      glVertex3dv (&mesh->Point (el.PNum(lines[j][0])).X());
+		    }
+		  glEnd();
+		  break;
+		}
+
+	      case QUAD6:
+		{
+		  int lines[6][2] = {
+		    { 1, 5 }, { 2, 5 },
+		    { 3, 6 }, { 4, 6 },
+		    { 1, 4 }, { 2, 3 } };
+	      
+		  glBegin (GL_LINES);
+	    
+		  for (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++)
+	INDEX_2_HASHTABLE<int> & idpts = 
+	  mesh->GetIdentifications().GetIdentifiedPoints();
+	if (&idpts)
+	  for (i = 1; i <= idpts.GetNBags(); i++)
+	    for (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 ();
+      }
+
+    vstimestamp = meshtimestamp;
+  }
+
+
+
+
+  void VisualSceneMesh :: BuildFilledList()
+  {
+    // clock_t starttime, endtime;
+    // starttime = clock();
+
+    if (!lock)
+      {
+	lock = new NgLock (mesh->Mutex());
+	lock -> Lock();
+      }
+
+    filledtimestamp = NextTimeStamp();
+
+    if (filledlist)
+      glDeleteLists (filledlist, 1);
+
+    filledlist = glGenLists (1);
+    glNewList (filledlist, GL_COMPILE);
+
+
+    bool checkvicinity = (stlgeometry != NULL) && stldoctor.showvicinity;
+
+    glEnable (GL_NORMALIZE);
+      
+    glLineWidth (1.0f);
+  
+    Vector locms;
+
+    if (vispar.colormeshsize)
+      {
+	glEnable (GL_COLOR_MATERIAL);
+	locms.SetSize (mesh->GetNP());
+	double maxh = -1;
+	double minh = 1e99;
+	for (int i = 1; i <= locms.Size(); i++)
+	  {
+	    Point3d p = mesh->Point(i);
+	    locms.Elem(i) = mesh->GetH (p);
+	    if (locms.Elem(i) > maxh) maxh = locms.Elem(i);
+	    if (locms.Elem(i) < minh) minh = locms.Elem(i);
+	  }
+	if (!locms.Size())
+	  { minh = 1; maxh = 10; }
+      }
+    else
+      glDisable (GL_COLOR_MATERIAL);
+
+
+    GLfloat matcol[] = { 0, 1, 0, 1 };
+    GLfloat matcolsel[] = { 1, 0, 0, 1 };
+
+    CurvedElements & curv = mesh->GetCurvedElements();
+    int hoplotn = 1 << vispar.subdivisions; 
+
+	
+    for (int col = 1; col <= 2; col++)
+      {
+	if (col == 2)
+	  glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, matcolsel);
+	else
+	  glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, matcol);
+
+
+	for (SurfaceElementIndex sei = 0; sei < mesh->GetNSE(); sei++)
+	  {
+	    const Element2d & el = (*mesh)[sei];
+	    
+	    bool drawel = !el.IsDeleted();
+
+	    if (checkvicinity)
+	      for (int j = 0; j < el.GetNP(); j++)
+		if (!stlgeometry->Vicinity(el.GeomInfoPi(j+1).trignum))
+		  drawel = 0;
+
+	    if (!drawel)
+	      continue;
+
+	    if (vispar.colormeshsize && col == 2)
+	      continue;
+	    if (!vispar.colormeshsize &&
+		(col == 2) != (el.GetIndex() == selface))
+	      continue;
+
+	    glLoadName (sei+1);
+
+	    switch (el.GetType())
+	      {
+	      case TRIG:
+		{
+		  if (curv.IsHighOrder() && curv.IsSurfaceElementCurved(sei))
+		    {
+		      Point<2> xr[3];
+		      Point<3> xg;
+		      Vec<3> dx, dy, n;
+
+		      glBegin (GL_TRIANGLES);
+
+		      for (int i = 0; i < hoplotn; i++)
+			for (int j = 0; j < hoplotn-i; j++)
+			  for (int k = 0; k < 2; k++)
+			    {
+			      if (k == 0)
+				{
+				  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/hoplotn; xr[2](1) = (double)(j+1)/hoplotn;
+				} 
+			      else
+				{
+				  if (j == hoplotn-i-1) continue;
+				  xr[0](0) = (double)(i+1)/hoplotn; xr[0](1) = (double)    j/hoplotn;
+				  xr[1](0) = (double)(i+1)/hoplotn; xr[1](1) = (double)(j+1)/hoplotn;
+				  xr[2](0) = (double)    i/hoplotn; xr[2](1) = (double)(j+1)/hoplotn;
+				};
+			      
+			      for (int l=0; l<3; 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_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);
+		      glNormal3dv (n);
+
+		      if (vispar.colormeshsize)
+			{
+			  SetOpenGlColor  (locms.Get(el[0]), minh, maxh, 1);
+			  glVertex3dv (lp0);
+			  SetOpenGlColor  (locms.Get(el[1]), minh, maxh, 1);
+			  glVertex3dv (lp1);
+			  SetOpenGlColor  (locms.Get(el[2]), minh, maxh, 1);
+			  glVertex3dv (lp2);
+			}
+		      else
+			{
+			  glVertex3dv (lp0);
+			  glVertex3dv (lp1);
+			  glVertex3dv (lp2);
+			}
+
+		      glEnd();
+		    }
+		  
+		  break;
+		}
+	      case QUAD:
+		{
+		  // cout << "BuildFilledList: QUAD" << endl;
+		  // CurvedElements & curv = mesh->GetCurvedElements();
+		  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);
+		      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++)
+		    {
+		      Point3d & lp1 = mesh->Point (el.PNum(trigs[j][0]));
+		      Point3d & lp2 = mesh->Point (el.PNum(trigs[j][1]));
+		      Point3d & lp3 = mesh->Point (el.PNum(trigs[j][2]));
+		      Vec3d n = Cross (Vec3d (lp1, lp2), Vec3d (lp1, lp3));
+		      glNormal3dv (&n.X());
+
+		      glVertex3dv (&lp1.X());
+		      glVertex3dv (&lp2.X());
+		      glVertex3dv (&lp3.X());
+		    }
+		  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++)
+		    {
+		      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++)
+		    {
+		      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 ();
+
+    // endtime = clock();
+    // cout << "BuildFillList time = " << double(endtime - starttime)/CLOCKS_PER_SEC << endl;
+  }
+
+
+  void VisualSceneMesh :: BuildLineList()
+  {
+    SurfaceElementIndex sei;
+
+    if (!lock)
+      {
+	lock = new NgLock (mesh->Mutex());
+	lock -> Lock();
+      }
+
+    linetimestamp = NextTimeStamp();
+
+
+    bool checkvicinity = (stlgeometry != NULL) && stldoctor.showvicinity;
+
+    if (linelist)
+      glDeleteLists (linelist, 1);
+  
+    linelist = glGenLists (1);
+    glNewList (linelist, GL_COMPILE);
+
+
+    glLineWidth (1.0f);
+
+    int hoplotn = 1 << vispar.subdivisions; 
+  
+    for (sei = 0; sei < mesh->GetNSE(); sei++)
+      {
+	const Element2d & el = (*mesh)[sei];
+      
+	bool drawel = !el.IsDeleted();
+	if (checkvicinity)
+	  for (int j = 0; j < el.GetNP(); j++)
+	    if (!stlgeometry->Vicinity(el.GeomInfoPi(j+1).trignum))
+	      drawel = 0;
+
+	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);
+
+		  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 ();
+  }
+
+  void VisualSceneMesh :: BuildPointNumberList()
+  {
+    ;
+  }
+
+
+
+
+
+
+  inline long int Fact (int n)
+  {
+    long int res = 1;
+    for (int i = 2; i <= n; i++)
+      res *= i;
+    return res;
+  }
+  inline int Binom (int n, int i)
+  {
+    return Fact(n) / Fact(i) / Fact(n-i);
+  }
+  
+  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) = Binom (order, j) * pow (x, j) * pow (1-x, order-j);
+	  }
+
+	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()
+  {
+
+
+#ifdef FAST3DELEMENTS
+    
+
+    cout << "start fast test" << endl;
+
+    int i, j;
+    ARRAY<Element2d> faces;
+
+    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);
+
+  
+    BitArray shownode(mesh->GetNP());
+    if (vispar.clipenable)
+      {
+	shownode.Clear();
+	for (i = 1; i <= shownode.Size(); i++)
+	  {
+	    Point3d p = mesh->Point(i);
+	  
+	    double val =
+	      p.X() * clipplane[0] +
+	      p.Y() * clipplane[1] +
+	      p.Z() * 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 }
+      };
+
+
+    ARRAY<int> elfaces;
+
+    const MeshTopology & top = mesh->GetTopology();
+    CurvedElements & curv = mesh->GetCurvedElements();
+
+    ARRAY<int> displayface(top.GetNFaces());
+    for (i = 0; i < top.GetNFaces(); i++)
+      displayface[i] = -1;
+    
+
+    for (i = 1; i <= mesh->GetNE(); i++)
+      {
+	if (vispar.drawtetsdomain > 0 &&
+	    vispar.drawtetsdomain != mesh->VolumeElement(i).GetIndex())
+	  continue;
+
+	Element el = (*mesh)[(ElementIndex) (i-1)];
+
+	if (el.GetType() == TET)
+	  {
+	    if (el.PNum(1))
+	      {
+		bool drawtet = 1;
+		for (j = 1; j <= el.GetNP(); j++)
+		  if (!shownode.Test(el.PNum(j)))
+		    drawtet = 0;
+		if (!drawtet) continue;
+		
+		{
+		  top.GetElementFaces (i, elfaces);
+		  
+		  for (j = 0; j < 4; j++)
+		    displayface[elfaces[j]-1] *= -1;
+		}
+	      }
+	  }
+      }
+
+    
+    for (i = 1; i <= mesh->GetNE(); i++)
+      {
+	if (vispar.drawtetsdomain > 0 &&
+	    vispar.drawtetsdomain != mesh->VolumeElement(i).GetIndex())
+	  continue;
+
+	if (mesh->VolumeElement(i).GetType() == TET)
+	  {
+	    // copy to be thread-safe
+	    Element el = mesh->VolumeElement (i);
+	    el.GetSurfaceTriangles (faces);
+
+	    if (el.PNum(1))
+	      {
+		bool drawtet = 1;
+		for (j = 1; j <= el.GetNP(); j++)
+		  if (!shownode.Test(el.PNum(j)))
+		    drawtet = 0;
+		if (!drawtet) continue;
+
+		int ind = el.GetIndex() % 4;
+		if (vispar.drawmetispartition && (el.GetPartition()!=-1))
+		  ind = el.GetPartition() % 4;
+		(*testout) << "ind = " << ind << endl;
+		glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, tetcols[ind]);
+
+
+		top.GetElementFaces (i, elfaces);
+
+		glBegin (GL_TRIANGLES);
+
+		for (j = 0; j < faces.Size(); j++)
+		  {
+		    if (displayface[elfaces[j]-1] == -1) continue;
+
+		    Element2d & face = faces.Elem(j+1);
+
+		    if (curv.IsHighOrder() && curv.IsElementCurved(i-1))
+		      {
+			int hoplotn = 1 << vispar.subdivisions; 
+			// int hoplotn = curv.GetNVisualSubsecs();
+			    
+			const Point3d * facepoint = MeshTopology :: GetVertices (TET);
+			const ELEMENT_FACE * elface = MeshTopology :: GetFaces(TET);
+                    
+			Vec<3> x0,x1,d0,d1;
+			Point<3> xg;
+			x0 = facepoint[face.PNum(3)-1] - facepoint[face.PNum(1)-1];
+			x1 = facepoint[face.PNum(2)-1] - facepoint[face.PNum(1)-1];
+			x0.Normalize();
+			x1.Normalize();
+
+			for (int m0 = 0; m0 < hoplotn; m0++)
+			  for (int m1 = 0; m1 < hoplotn-m0; m1++)
+			    for (int k = 0; k < 2; k++)
+			      {
+				Vec<3> dx, dy, dz, n;
+				Point<4> la[3];
+				int l;
+				for (l = 0; l<3; l++) la[l] = Point<4>(0.,0.,0.,0.);
+
+				if (k == 0)
+				  {
+				    la[0](face.PNum(1)-1) = (m0  )/(double)hoplotn;
+				    la[0](face.PNum(2)-1) = (m1  )/(double)hoplotn;
+				    la[0](face.PNum(3)-1) = 1-la[0](face.PNum(1)-1)-la[0](face.PNum(2)-1);
+
+				    la[1](face.PNum(1)-1) = (m0+1)/(double)hoplotn;
+				    la[1](face.PNum(2)-1) = (m1  )/(double)hoplotn;
+				    la[1](face.PNum(3)-1) = 1-la[1](face.PNum(1)-1)-la[1](face.PNum(2)-1);
+
+				    la[2](face.PNum(1)-1) = (m0  )/(double)hoplotn;
+				    la[2](face.PNum(2)-1) = (m1+1)/(double)hoplotn;
+				    la[2](face.PNum(3)-1) = 1-la[2](face.PNum(1)-1)-la[2](face.PNum(2)-1);
+				  } else
+				    {
+				      if (m1 == hoplotn-m0-1) continue;
+				      la[0](face.PNum(1)-1) = (m0+1)/(double)hoplotn;
+				      la[0](face.PNum(2)-1) = (m1+1)/(double)hoplotn;
+				      la[0](face.PNum(3)-1) = 1-la[0](face.PNum(1)-1)-la[0](face.PNum(2)-1);
+
+				      la[1](face.PNum(1)-1) = (m0  )/(double)hoplotn;
+				      la[1](face.PNum(2)-1) = (m1+1)/(double)hoplotn;
+				      la[1](face.PNum(3)-1) = 1-la[1](face.PNum(1)-1)-la[1](face.PNum(2)-1);
+
+				      la[2](face.PNum(1)-1) = (m0+1)/(double)hoplotn;
+				      la[2](face.PNum(2)-1) = (m1  )/(double)hoplotn;
+				      la[2](face.PNum(3)-1) = 1-la[2](face.PNum(1)-1)-la[2](face.PNum(2)-1);
+				    }
+
+				for (l = 0; l<3; l++)
+				  {
+				    Mat<3,3> dxdxi;
+				    Point<3> xr( la[l](0), la[l](1), la[l](2) );
+				    curv.CalcElementTransformation (xr, 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);
+				      }
+
+				    d0 = x0(0)*dx + x0(1)*dy + x0(2)*dz;
+				    d1 = x1(0)*dx + x1(1)*dy + x1(2)*dz;
+				    n = Cross (d0, d1);
+				    glNormal3d ( n(0),  n(1),  n(2));
+				    glVertex3d (xg(0), xg(1), xg(2));
+				  }
+			      }
+
+		      } else {
+			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, lp3), Vec3d (lp1, lp2));
+			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();
+	      }
+
+	  }
+      }
+    glEndList ();
+
+
+
+
+#else
+
+    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);
+
+
+
+    int i, j, k, l;
+    ARRAY<Element2d> faces;
+
+  
+    BitArray shownode(mesh->GetNP());
+    if (vispar.clipenable)
+      {
+	shownode.Clear();
+	for (i = 1; i <= shownode.Size(); i++)
+	  {
+	    Point3d p = mesh->Point(i);
+	  
+	    double val =
+	      p.X() * clipplane[0] +
+	      p.Y() * clipplane[1] +
+	      p.Z() * 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 }
+      };
+
+
+    CurvedElements & curv = mesh->GetCurvedElements();
+
+    for (ElementIndex ei = 0; ei < mesh->GetNE(); ei++)
+      {
+	i = ei + 1;
+
+	if (vispar.drawtetsdomain > 0 &&
+	    vispar.drawtetsdomain != mesh->VolumeElement(i).GetIndex())
+	  continue;
+
+	const Element & el = (*mesh)[ei];
+
+	if (el.GetType() == TET && !el.IsDeleted())
+	  {
+
+	    bool drawtet = 1;
+	    for (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;
+	    glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, tetcols[ind]);
+	    
+	    
+	    if (curv.IsHighOrder() && curv.IsElementCurved(ei))
+	      {
+		const ELEMENT_FACE * faces = MeshTopology :: GetFaces (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);
+		  }		    
+	      }
+
+
+	    else
+
+	      {
+		el.GetSurfaceTriangles (faces);
+
+		Point3d c;
+		if (vispar.shrink < 1)
+		  c = Center (Center (mesh->Point (el.PNum(1)),
+				      mesh->Point (el.PNum(2))),
+			      Center (mesh->Point (el.PNum(3)),
+				      mesh->Point (el.PNum(4))));
+
+
+		glBegin (GL_TRIANGLES);
+
+		for (j = 0; j < faces.Size(); j++)
+		  {
+		    const Element2d & face = faces[j];
+
+		    /*
+		      if (curv.IsHighOrder() && curv.IsElementCurved(i-1))
+		      {
+		      int hoplotn = 1 << vispar.subdivisions; 
+		      // int hoplotn = curv.GetNVisualSubsecs();
+			    
+		      const Point3d * facepoint = MeshTopology :: GetVertices (TET);
+		      const ELEMENT_FACE * elface = MeshTopology :: GetFaces(TET);
+                    
+		      Vec<3> x0,x1,d0,d1;
+		      Point<3> xg;
+		      x0 = facepoint[face.PNum(3)-1] - facepoint[face.PNum(1)-1];
+		      x1 = facepoint[face.PNum(2)-1] - facepoint[face.PNum(1)-1];
+		      x0.Normalize();
+		      x1.Normalize();
+
+		      for (int m0 = 0; m0 < hoplotn; m0++)
+		      for (int m1 = 0; m1 < hoplotn-m0; m1++)
+		      for (k = 0; k < 2; k++)
+		      {
+		      Vec<3> dx, dy, dz, n;
+		      Point<4> la[3];
+					
+		      for (l = 0; l<3; l++) la[l] = Point<4>(0.,0.,0.,0.);
+
+		      if (k == 0)
+		      {
+		      la[0](face.PNum(1)-1) = (m0  )/(double)hoplotn;
+		      la[0](face.PNum(2)-1) = (m1  )/(double)hoplotn;
+		      la[0](face.PNum(3)-1) = 1-la[0](face.PNum(1)-1)-la[0](face.PNum(2)-1);
+
+		      la[1](face.PNum(1)-1) = (m0+1)/(double)hoplotn;
+		      la[1](face.PNum(2)-1) = (m1  )/(double)hoplotn;
+		      la[1](face.PNum(3)-1) = 1-la[1](face.PNum(1)-1)-la[1](face.PNum(2)-1);
+
+		      la[2](face.PNum(1)-1) = (m0  )/(double)hoplotn;
+		      la[2](face.PNum(2)-1) = (m1+1)/(double)hoplotn;
+		      la[2](face.PNum(3)-1) = 1-la[2](face.PNum(1)-1)-la[2](face.PNum(2)-1);
+		      } else
+		      {
+		      if (m1 == hoplotn-m0-1) continue;
+		      la[0](face.PNum(1)-1) = (m0+1)/(double)hoplotn;
+		      la[0](face.PNum(2)-1) = (m1+1)/(double)hoplotn;
+		      la[0](face.PNum(3)-1) = 1-la[0](face.PNum(1)-1)-la[0](face.PNum(2)-1);
+
+		      la[1](face.PNum(1)-1) = (m0  )/(double)hoplotn;
+		      la[1](face.PNum(2)-1) = (m1+1)/(double)hoplotn;
+		      la[1](face.PNum(3)-1) = 1-la[1](face.PNum(1)-1)-la[1](face.PNum(2)-1);
+
+		      la[2](face.PNum(1)-1) = (m0+1)/(double)hoplotn;
+		      la[2](face.PNum(2)-1) = (m1  )/(double)hoplotn;
+		      la[2](face.PNum(3)-1) = 1-la[2](face.PNum(1)-1)-la[2](face.PNum(2)-1);
+		      }
+
+		      for (l = 0; l<3; l++)
+		      {
+		      Mat<3,3> dxdxi;
+		      Point<3> xr( la[l](0), la[l](1), la[l](2) );
+		      curv.CalcElementTransformation (xr, 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);
+		      }
+
+		      d0 = x0(0)*dx + x0(1)*dy + x0(2)*dz;
+		      d1 = x1(0)*dx + x1(1)*dy + x1(2)*dz;
+		      n = Cross (d0, d1);
+		      glNormal3d ( n(0),  n(1),  n(2));
+		      glVertex3d (xg(0), xg(1), xg(2));
+		      }
+		      }
+
+		      } else 
+		    */
+		    {
+		      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 ();
+    
+#endif
+  }
+
+
+
+
+  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 :: GetFaces (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.999, 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 :: GetFaces (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 };
+    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 j;
+	    int i = ei + 1;
+
+	    CurvedElements & curv = mesh->GetCurvedElements();
+	    if (curv.IsHighOrder() && curv.IsElementCurved(ei))
+	      {
+
+		const ELEMENT_FACE * faces = MeshTopology :: GetFaces (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.999, 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);
+	  
+		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, pyramidcol);
+		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)
+  {
+    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];
+	/*
+	  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.p1 == selpoint && seg.p2 == selpoint2 || 
+		seg.p2 == selpoint && seg.p1 == selpoint2)
+	      {
+		seledge = seg.edgenr;
+		cout << "seledge = " << seledge << endl;
+	      }
+	  }
+      
+      }
+    else
+      {
+	selface = -1;
+	selelement = -1;
+	selpoint = -1;
+	selpoint2 = -1;
+      }
+
+    glDisable(GL_CLIP_PLANE0);
+
+    selecttimestamp = NextTimeStamp();
+  }
+
+
+
+
+
+}
+
+
+
+
+
+
+
+
+
+  
diff --git a/contrib/Netgen/libsrc/visualization/vsocc.cpp b/contrib/Netgen/libsrc/visualization/vsocc.cpp
new file mode 100644
index 0000000000..12cfc5e179
--- /dev/null
+++ b/contrib/Netgen/libsrc/visualization/vsocc.cpp
@@ -0,0 +1,743 @@
+#ifdef OCCGEOMETRY
+
+
+#include <mystdlib.h>
+#include <myadt.hpp>
+#include <meshing.hpp>
+
+// #include <csg.hpp>
+// #include <stlgeom.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 "BRepMesh.hxx"
+// #include "BRepMesh_IncrementalMesh.hxx"
+
+#include "incvis.hpp"
+
+
+namespace netgen
+{
+#include "mvdraw.hpp"
+
+
+extern OCCGeometry * occgeometry;
+
+
+
+
+/* *********************** Draw OCC Geometry **************** */
+
+
+VisualSceneOCCGeometry :: VisualSceneOCCGeometry ()
+  : VisualScene()
+{
+  trilists.SetSize(0);
+  linelists.SetSize(1);
+
+}
+
+VisualSceneOCCGeometry :: ~VisualSceneOCCGeometry ()
+{
+  ;
+}
+
+void VisualSceneOCCGeometry :: DrawScene ()
+{
+  // int i, j, k;
+
+  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);
+
+
+  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);
+
+  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)
+    {
+      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++)
+    {
+      glLoadName (i);
+      float mat_col[4];
+      mat_col[3] = 1;
+
+      if (!occgeometry->fvispar[i-1].IsHighlighted())
+	{
+	  mat_col[0] = 0.2;
+	  mat_col[1] = 0.2;
+	  mat_col[2] = 0.8;
+	}
+      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);
+
+      TopoDS_Face face = TopoDS::Face(occgeometry->fmap(i));
+      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);
+	  for (int k = 1; k <= 3; k++)
+	    {
+	      uv = (triangulation -> UVNodes())(triangle(k));
+	      prop.SetParameters (uv.X(), uv.Y());
+
+	      pnt = (triangulation -> Nodes())(triangle(k)).Transformed(loc);
+
+	      //	      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);
+		}
+		    
+	      if (face.Orientation() == TopAbs_REVERSED) n *= -1;
+	      glNormal3f (n.X(), n.Y(), n.Z());
+	      glVertex3f (pnt.X(), pnt.Y(), pnt.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);
+  
+  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);
+
+
+  //  selecttimestamp = NextTimeStamp();
+}
+
+
+
+
+
+
+}
+
+
+
+#endif
+
+
diff --git a/contrib/Netgen/libsrc/visualization/vssolution.cpp b/contrib/Netgen/libsrc/visualization/vssolution.cpp
new file mode 100644
index 0000000000..62b6faab83
--- /dev/null
+++ b/contrib/Netgen/libsrc/visualization/vssolution.cpp
@@ -0,0 +1,3005 @@
+#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;
+
+
+  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 = 0;
+    isolinelist = 0;
+    clipplane_isolinelist = 0;
+    surface_vector_list = 0;
+    cone_list = 0;
+
+    surfeltimestamp = GetTimeStamp();
+    surfellinetimestamp = GetTimeStamp();
+    clipplanetimestamp = GetTimeStamp();
+    solutiontimestamp = GetTimeStamp();
+    fieldlinestimestamp = GetTimeStamp();
+    surface_vector_timestamp = GetTimeStamp();
+    AddVisualizationScene ("solution", &vssolution);
+  }
+  
+  VisualSceneSolution :: ~VisualSceneSolution ()
+  {
+    ClearSolutionData();
+  }
+
+  void VisualSceneSolution :: AddSolutionData (SolData * sd)
+  {
+    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;
+	    }
+	  }
+	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);
+    int i, j, k;
+
+    ofstream ost(filename);
+    for (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;
+	}
+      
+      ost << endl;
+      for (j = 0; j < sol.size; j++)
+	{
+	  for (k = 0; k < sol.components; k++)
+	    ost << sol.data[j*sol.dist+k] << " ";
+	  ost << "\n";
+	}
+    }
+  }
+  
+
+
+
+  void VisualSceneSolution :: DrawScene ()
+  {
+    if (!mesh) 
+      {
+	VisualScene::DrawScene();      
+	return;
+      }
+
+    static NgLock mem_lock(mem_mutex);
+    mem_lock.Lock();
+
+    NgLock meshlock (mesh->Mutex(), 1);
+
+    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);
+    glPolygonOffset (2, 10);
+    glEnable (GL_POLYGON_OFFSET_FILL);
+
+    glEnable (GL_COLOR_MATERIAL);
+
+
+    if (usetexture)
+      glEnable (GL_TEXTURE_1D);
+
+
+    if (vispar.drawfilledtrigs || vispar.drawtetsdomain > 0 || vispar.drawdomainsurf > 0)
+      {
+	SetClippingPlane ();
+	
+	glCallList (surfellist);
+	glCallList (surface_vector_list);
+      
+	glDisable(GL_CLIP_PLANE0);
+      }
+
+    if (showclipsolution)
+      glCallList (clipplanelist);
+
+
+    if (draw_fieldlines)
+      {
+	BuildFieldLinesPlot ();
+
+	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);
+	      }
+	  }
+      }
+
+    if (usetexture)
+      glDisable (GL_TEXTURE_1D);
+
+
+
+    glDisable (GL_POLYGON_OFFSET_FILL);
+    
+    glDisable (GL_COLOR_MATERIAL);
+    
+    
+    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();
+  }
+  
+
+
+  static void 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];
+	  }
+      }
+  }
+
+
+  static void 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)
+  {
+    int i, j, k;
+ 
+    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 || scalfunction == -1)
+    {
+      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;
+	    }
+	}
+
+      if (surfellist)
+	glDeleteLists (surfellist, 1);
+      
+      surfellist = glGenLists (1);
+      glNewList (surfellist, GL_COMPILE);
+      
+	  DrawSurfaceElements();
+      
+      glEndList ();
+      
+      surfeltimestamp = max2 (solutiontimestamp, mesh->GetTimeStamp());
+    }
+
+
+  if (mesh->GetTimeStamp() > surfellinetimestamp ||
+      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)      
+	{
+	  // lock->UnLock();
+	  NgLock mlock (mesh->Mutex(), 0);
+	  mlock.UnLock();
+	  mesh->BuildElementSearchTree();
+	  mlock.Lock();
+
+	  // lock->Lock();
+	}
+
+      if (clipplanelist)
+	glDeleteLists (clipplanelist, 1);
+      
+
+      clipplanelist = glGenLists (1);
+      glNewList (clipplanelist, GL_COMPILE);
+      
+      if (vispar.clipenable && clipsolution == 1 && sol)
+	{
+	  glDisable(GL_CLIP_PLANE0);
+	  
+	  ARRAY<ClipPlaneTrig> cpt;
+	  GetClippingPlaneTrigs (cpt);
+	  
+	  glNormal3d (-clipplane[0], -clipplane[1], -clipplane[2]);
+	  
+	  glBegin (GL_TRIANGLES);
+	  for (i = 0; i < cpt.Size(); i++)
+	    DrawClipPlaneTrig (sol, scalcomp, cpt[i], 0); // 2*subdivisions);
+	  glEnd();
+
+	  glEnable(GL_CLIP_PLANE0);
+	}
+      
+      
+      if (vispar.clipenable && clipsolution == 2 && vsol)
+	{
+	  if (autoscale)
+	    GetMinMax (vecfunction, 0, minval, maxval);
+
+
+	  bool drawelem;
+	  ARRAY<ClipPlanePoint> cpp;
+	  GetClippingPlaneGrid (cpp);
+
+	  for (i = 0; i < cpp.Size(); i++)
+	    {
+	      const ClipPlanePoint & p = cpp[i];
+	      double values[6];
+	      Vec3d v;
+
+	      drawelem = GetValues (vsol, p.elnr, p.lam1, p.lam2, p.lam3, values);
+	      RealVec3d (values, v, vsol->iscomplex, imag_part);
+
+	      double val = v.Length();
+
+	      // "drawelem": added 07.04.2004 (FB)
+	      if (drawelem && val > 1e-10 * maxval)
+		{
+		  v *= (rad / val / gridsize * 0.5);
+		  
+		  SetOpenGlColor  (val, minval, maxval, logscale);
+		  DrawCone (p.p, p.p+v, rad / gridsize * 0.2);
+		}
+	    }
+	}
+
+      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();
+
+      if (sol)
+	{
+	  glBegin (GL_LINES);
+	  
+	  for (SurfaceElementIndex sei = 0; sei < nse; sei++)
+	    {
+	      const Element2d & el = (*mesh)[sei];
+	      
+	      if (el.GetType() == TRIG || el.GetType() == TRIG6)
+		{
+		  Point<3> lp1, lp2, lp3;
+		  if (!mesh->GetCurvedElements().IsHighOrder())
+		    {
+		      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 (mesh->GetCurvedElements().IsHighOrder())
+			  mesh->GetCurvedElements().
+			    CalcSurfaceTransformation (xref, sei, points[ii]);
+			else
+			  points[ii] = lp3 + x * (lp1-lp3) + y * (lp2-lp3);
+			
+			if (deform)
+			  {
+			    Vec<3> def;
+			    GetSurfDeformation (sei, x, y, def);
+			    points[ii] += def;
+			  }
+			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]],
+				      minval, maxval, numisolines);
+			if (ix < n-iy-1) 
+			  DrawIsoLines (points[index[3]], points[index[4]], points[index[5]],
+				      values[index[3]], values[index[4]], values[index[5]],
+					minval, maxval, numisolines);
+		      }    
+		}
+	      
+	      
+	      if (el.GetType() == QUAD || el.GetType() == QUAD6 || el.GetType() == QUAD8 )
+		{
+		  Point<3> lpi[4];
+		  Vec<3> vx, vy, vtwist, def;
+		  if (!mesh->GetCurvedElements().IsHighOrder())
+		    {
+		      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 (mesh->GetCurvedElements().IsHighOrder())
+			  mesh->GetCurvedElements().
+			    CalcSurfaceTransformation (xref, sei, points[ii]);
+			else
+			  points[ii] = lpi[0] + x * vx + y * vy + x*y * vtwist;
+			
+			if (deform)
+			  {
+			    GetSurfDeformation (sei, x, y, def);
+			    points[ii] += def;
+			  }
+		      }
+		  
+		  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],
+				      minval, maxval, numisolines);
+			DrawIsoLines (points[ii+1], points[ii+n+2], points[ii+n+1],
+				      values[ii+1], values[ii+n+2], values[ii+n+1],
+				      minval, maxval, numisolines);
+		      }	
+		}
+	    }
+	  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;
+	  GetClippingPlaneTrigs (cpt);
+	  bool drawelem;
+	  
+	  glNormal3d (-clipplane[0], -clipplane[1], -clipplane[2]);
+	  
+	  if (numisolines)
+	    for (i = 0; i < cpt.Size(); i++)
+	      {
+		const ClipPlaneTrig & trig = cpt[i];
+		double vali[3];
+		for (j = 0; j < 3; j++)
+		  drawelem = GetValue (sol, trig.elnr, 
+				       trig.points[j].lami(0),
+				       trig.points[j].lami(1),
+				     trig.points[j].lami(2), scalcomp, vali[j]);
+		
+		if ( drawelem )
+		  DrawIsoLines (trig.points[0].p,
+				trig.points[1].p,
+				trig.points[2].p,
+				vali[0], vali[1], vali[2], minval, maxval, numisolines);
+	      }
+	  glEndList ();
+	}
+      glEnd();
+      
+
+    }
+  
+  clipplanetimestamp = max2 (vispar.clipplanetimestamp, solutiontimestamp);
+  }
+  
+
+  void VisualSceneSolution :: BuildFieldLinesPlot ()
+  {
+    if (fieldlinestimestamp >= solutiontimestamp) 
+      return;
+    fieldlinestimestamp = solutiontimestamp;
+    
+
+    if (fieldlineslist)
+      glDeleteLists (fieldlineslist, num_fieldlineslists);
+
+    if (vecfunction == -1)
+      return;
+
+
+    const SolData * vsol = soldata[vecfunction];
+
+    num_fieldlineslists = (vsol -> iscomplex) ? 100 : 1;
+
+    Point3d pmin, pmax;
+    mesh->GetBox (pmin, pmax);
+    double lami[3];
+    int i;
+    bool drawelem;
+
+    fieldlineslist = glGenLists (num_fieldlineslists);
+
+    for (int ln = 0; ln < num_fieldlineslists; ln++)
+      {
+	glNewList (fieldlineslist + ln, GL_COMPILE);      
+
+	double phi = 2*M_PI*ln / num_fieldlineslists;
+	double phaser = cos(phi);
+	double phasei = sin(phi);
+
+	for (i = 1; i <= num_fieldlines / num_fieldlineslists+1; i++)
+	  {
+	    Point3d p (pmin.X() + double (rand()) / RAND_MAX * (pmax.X()-pmin.X()),
+		       pmin.Y() + double (rand()) / RAND_MAX * (pmax.Y()-pmin.Y()),
+		       pmin.Z() + double (rand()) / RAND_MAX * (pmax.Z()-pmin.Z()));
+	    
+	    ElementIndex elnr = mesh->GetElementOfPoint (p, lami)-1;
+	    (*testout) << "p = " << p << "; elnr = " << elnr << endl;
+	    if (elnr != -1)
+	      {
+		Vec3d v;
+		double values[6];
+		drawelem = GetValues (vsol, elnr, lami[0], lami[1], lami[2], values);
+		RealVec3d (values, v, vsol->iscomplex, phaser, phasei);
+		
+		double val = v.Length();
+		
+		if (!fieldlines_randomstart ||
+		    (double (rand()) / RAND_MAX) < (val / maxval))
+		  {
+		    int i;
+		    Point3d p0 = p; 
+		    v *= (rad / val * 0.02);
+		    SetOpenGlColor  (val, minval, maxval, logscale);
+		    
+		    Point3d p2 = p + v; 
+		    cout << " p " << p << endl; 
+		    // "drawelem": added 07.04.2004 (FB)
+		    if ( drawelem ) DrawCylinder (p, p2, rad * 0.003);
+		    p = p2;  		
+		    
+		    for(i=0;i<20;i++) 
+		      {
+			ElementIndex elnr = mesh->GetElementOfPoint (p, lami)-1;
+			
+			if (elnr != -1)
+			  {
+			    drawelem = GetValues (vsol, elnr, lami[0], lami[1], lami[2], values);
+			    RealVec3d (values, v, vsol->iscomplex, phaser, phasei);
+			    val = v.Length();
+			    v *= (rad / val * 0.02);
+			    
+			    SetOpenGlColor  (val, minval, maxval, logscale);
+			    p2 = p +v; 
+			    // "drawelem": added 07.04.2004 (FB)
+			    if ( drawelem ) DrawCylinder (p, p2, rad * 0.003);
+			    p = p2;  	
+			  }
+			else break; 
+		      } 
+		    p=p0; 
+		    for(i=0;i<20;i++) 
+		      {
+			ElementIndex elnr = mesh->GetElementOfPoint (p, lami)-1;
+
+			if (elnr != -1)
+			  {
+			    drawelem = GetValues (vsol, elnr, lami[0], lami[1], lami[2], values);
+			    RealVec3d (values, v, vsol->iscomplex, phaser, phasei);
+			    
+			    val = v.Length();
+			    v *= (rad / val * 0.02);
+			    
+			    SetOpenGlColor  (val, minval, maxval, logscale);
+			    p2 = p - v; 
+			    // "drawelem": added 07.04.2004 (FB)
+			    if ( drawelem ) DrawCylinder (p, p2, rad * 0.003);
+			    p = p2;  	
+			  }
+			else break; 
+		      } 
+		  }		  
+	      }
+	  }
+	glEndList ();
+      }
+  }
+  
+
+
+
+  void  VisualSceneSolution :: DrawSurfaceElements ()
+  {
+    const SolData * sol = NULL;
+    const SolData * vsol = NULL;
+    bool drawelem = 0;
+    
+    if (scalfunction != -1)
+      sol = soldata[scalfunction];
+    if (vecfunction != -1)
+      vsol = soldata[vecfunction];
+    
+    if (mesh->GetTimeStamp () > solutiontimestamp)
+      {
+	sol = NULL;
+	vsol = NULL;
+      }
+
+    glLineWidth (1.0f);
+
+    if (!sol || !sol->draw_surface)
+      glDisable (GL_TEXTURE_1D);
+        
+    Point<3> points[1100];
+    Vec<3> nvs[1100];
+    double values[1100];
+
+    int nse = mesh->GetNSE();
+
+    glBegin (GL_TRIANGLES);
+
+    // glColor3f (0.4, 0.4, 0.4);
+    // glColor3d (0.8, 0.8, 0.8);
+    glColor3d (1.0, 1.0, 1.0);
+
+    for(SurfaceElementIndex sei = 0; sei < nse; sei++)
+      {
+	const Element2d & el = (*mesh)[sei];
+
+	if(vispar.drawdomainsurf > 0 &&
+	   ((mesh->GetDimension() == 3 && 
+	     vispar.drawdomainsurf != mesh->GetFaceDescriptor(el.GetIndex()).DomainIn() &&
+	     vispar.drawdomainsurf != mesh->GetFaceDescriptor(el.GetIndex()).DomainOut()) ||
+	    (mesh->GetDimension() == 2 && el.GetIndex() != vispar.drawdomainsurf))) continue;
+	
+	
+	if ( el.GetType() == TRIG || el.GetType() == TRIG6 )
+	  {
+	    Point<3> p1, p2, p3;
+	    if (!mesh->GetCurvedElements().IsHighOrder())
+	      {
+		GetPointDeformation (el[0]-1, p1, sei);
+		GetPointDeformation (el[1]-1, p2, sei);
+		GetPointDeformation (el[2]-1, p3, sei);
+	      }
+	    
+	    int n = 1 << subdivisions;
+	    int ii = 0;
+	    for (int iy = 0; iy <= n; iy++)
+	      for (int ix = 0; ix <= n-iy; ix++)
+		{
+		  double x = double(ix) / n;
+		  double y = double(iy) / n;
+		  
+		  if (sol && sol->draw_surface) 
+		    drawelem = GetSurfValue (sol, sei, x, y, scalcomp, values[ii]);
+
+		  Point<2> xref(x,y);
+		  Mat<3,2> dxdxi;
+
+		  if (mesh->GetCurvedElements().IsHighOrder())
+		    {
+		      mesh->GetCurvedElements().
+			CalcSurfaceTransformation (xref, sei, points[ii], dxdxi);
+		      nvs[ii] = Cross (dxdxi.Col(0), dxdxi.Col(1));
+		      nvs[ii].Normalize();
+		    }
+		  else
+		    {
+		      points[ii] = p3 + x * (p1-p3) + y * (p2-p3);
+		      nvs[ii] = Cross (p2-p1, p3-p1);
+		      nvs[ii].Normalize();
+		    }
+		  
+		  if (deform)
+		    {
+		      Vec<3> def;
+		      GetSurfDeformation (sei, x, y, def);
+		      points[ii] += def;
+		    }
+		  ii++;
+		}
+
+	    ii = 0;
+	    for (int iy = 0; iy < n; iy++, ii++)
+	      for (int ix = 0; ix < n-iy; ix++, ii++)
+		{
+		  double x = double(ix) / n;
+		  double y = double(iy) / n;
+		  
+		  int index[] = { ii, ii+1, ii+n-iy+1,
+				  ii+1, ii+n-iy+2, ii+n-iy+1 };
+		  
+		  int np = (ix == n-iy-1) ? 3 : 6;
+		  for (int j = 0; j < np; j++)
+		    {
+		      if (sol && sol->draw_surface && drawelem)
+			SetOpenGlColor  (values[index[j]], minval, maxval, logscale);
+		      else
+			glColor3f (0.4f, 0.4f, 0.4f);
+		      
+		      glNormal3dv (nvs[index[j]]);
+		      glVertex3dv (points[index[j]]);
+		    }
+		}	
+	  }
+      }
+    glEnd ();
+    
+      
+    
+  
+    
+    glBegin (GL_QUADS);
+    for (SurfaceElementIndex sei = 0; sei < nse; sei++)
+      {
+	const Element2d & el = (*mesh)[sei];
+
+	if(vispar.drawdomainsurf > 0 &&
+	   ((mesh->GetDimension() == 3 && 
+	     vispar.drawdomainsurf != mesh->GetFaceDescriptor(el.GetIndex()).DomainIn() &&
+	     vispar.drawdomainsurf != mesh->GetFaceDescriptor(el.GetIndex()).DomainOut()) ||
+	    (mesh->GetDimension() == 2 && el.GetIndex() != vispar.drawdomainsurf))) continue;
+
+	if ( el.GetType() == QUAD || el.GetType() == QUAD6 )
+	  {
+	    Point<3> lpi[4];
+	    Vec<3> vx, vy, vtwist;
+	    
+	    if (!mesh->GetCurvedElements().IsHighOrder())
+	      {
+		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]);
+	      }
+	    Vec<3> nv = Cross (lpi[1]-lpi[0], Center (lpi[2],lpi[3]) - lpi[0]);
+	    nv.Normalize();
+	    glNormal3dv (nv);
+
+	    int n = 1 << subdivisions;
+	    int ii = 0;
+	    int ix, iy;
+	    for (iy = 0; iy <= n; iy++)
+	      for (ix = 0; ix <= n; ix++)
+		{
+		  double x = double(ix) / n;
+		  double y = double(iy) / n;
+		
+		  Point<2> xref(x,y);
+		  Mat<3,2> dxdxi;
+
+		  if (sol && sol->draw_surface) 
+		    drawelem = GetSurfValue (sol, sei, x, y, scalcomp, values[ii]);
+
+		  if (mesh->GetCurvedElements().IsHighOrder())
+		    {
+		      mesh->GetCurvedElements().
+			CalcSurfaceTransformation (xref, sei, points[ii], dxdxi);
+		      nvs[ii] = Cross (dxdxi.Col(0), dxdxi.Col(1));
+		      nvs[ii].Normalize();
+		    }
+		  else
+		    {
+		      points[ii] = lpi[0] + x * vx + y * vy + x*y * vtwist;
+		      nvs[ii] = Cross (vx, vy);
+		      nvs[ii].Normalize();
+		    }
+		  
+		  if (deform)
+		    {
+		      Vec<3> def;
+		      GetSurfDeformation (sei, x, y, def);
+		      points[ii] += def;
+		    }
+
+		  ii++;
+		}
+	  
+	    ii = 0;
+	    for (iy = 0; iy < n; iy++, ii++)
+	      for (ix = 0; ix < n; ix++, ii++)
+		{
+		  double x = double(ix) / n;
+		  double y = double(iy) / n;
+		  
+		  int index[] = { ii, ii+1, ii+n+2, ii+n+1 };
+		  
+		  for (int j = 0; j < 4; j++)
+		    {
+		      if (sol && sol->draw_surface && drawelem)
+			SetOpenGlColor  (values[index[j]], minval, maxval, logscale);
+		      else
+			glColor3f (0.4f, 0.4f, 0.4f);
+		      
+		      glNormal3dv (nvs[index[j]]);
+		      glVertex3dv (points[index[j]]);
+		    }
+		}	
+	  }
+      }
+    glEnd();
+    
+    if (usetexture)
+      glEnable (GL_TEXTURE_1D);
+  }
+
+
+  // Bernstein Pol B_{n,i}(x) = n! / i! / (n-i)! (1-x)^{n-i} x^i
+  static  double Bernstein (int n, int i, double x)
+  {
+    int j;
+    double val = 1;
+    for (j = 1; j <= i; j++)
+      val *= x / j;
+    for (j = 1; j <= n-i; j++)
+      val *= (1-x) / j;
+    for (j = 1; j <= n; j++)
+      val *= j;
+    return val;
+  }
+
+  void  VisualSceneSolution :: DrawSurfaceElementLines ()
+  {
+    int i, j, k, l;
+    SurfaceElementIndex sei;
+
+    /*
+    int p = 4;
+    DenseMatrix mat(p+1,p+1), invmat(p+1,p+1);
+    for (i = 0; i <= p; i++)
+      for (j = 0; j <= p; j++)
+	mat.Elem(i+1,j+1) = Bernstein(p, i, double(j)/p);
+    CalcIo
+nverse (mat, invmat);
+    */
+
+    glLineWidth (1.0f);
+    glNormal3d (1, 0, 0);
+
+    int nse = mesh->GetNSE();
+
+    for (sei = 0; sei < nse; sei++)
+      {
+	Element2d & el = (*mesh)[sei];
+
+	int nv;
+	if (el.GetType() == TRIG || el.GetType() == TRIG6)
+	  nv = 3;
+	else 
+	  nv = 4;
+	
+	Point<3> p1, p2, p3, p4;
+	if (!mesh->GetCurvedElements().IsHighOrder())
+	  {
+	    p1 = (*mesh)[el[0]];
+	    p2 = (*mesh)[el[1]];
+	    p3 = (*mesh)[el[2]];
+	    if (nv == 4)
+	      p4 = (*mesh)[el[3]];
+	  }
+
+
+	// glBegin (GL_LINE_LOOP);
+	int n = 1 << subdivisions;
+	// n = p;
+
+	Point<3> pnt;
+	for (k = 0; k < nv; k++)
+	  {
+	    Point<2> p0;
+	    Vec<2> vtau;
+	    if (nv == 3)
+	      switch (k)
+		{
+		case 0:
+		  p0 = Point<2> (0,0);
+		  vtau = Vec<2> (1,0);
+		  break;
+		case 1:
+		  p0 = Point<2> (1,0);
+		  vtau = Vec<2> (-1,1);
+		  break;
+		case 2:
+		  p0 = Point<2> (0,1);
+		  vtau = Vec<2> (0,-1);
+		  break;
+		}
+	    else
+	      switch (k)
+		{
+		case 0:
+		  p0 = Point<2> (0,0);
+		  vtau = Vec<2> (1,0);
+		  break;
+		case 1:
+		  p0 = Point<2> (1,0);
+		  vtau = Vec<2> (0,1);
+		  break;
+		case 2:
+		  p0 = Point<2> (1,1);
+		  vtau = Vec<2> (-1,0);
+		  break;
+		case 3:
+		  p0 = Point<2> (0,1);
+		  vtau = Vec<2> (0,-1);
+		  break;
+		}
+
+	    
+	    glBegin (GL_LINE_STRIP);
+	    Point<3> pts[33], pts2[33];
+	    if (n > 32) cerr << "too many subdivisions, code 433425" << endl;
+
+	    for (int ix = 0; ix <= n; ix++)
+	      {
+		Point<2> p = p0 + (double(ix) / n) * vtau;
+		double x = p(0);
+		double y = p(1);
+
+		if (mesh->GetCurvedElements().IsHighOrder())
+		  mesh->GetCurvedElements().
+		    CalcSurfaceTransformation (p, sei, pnt);
+		else
+		  {
+		    if (nv == 3)
+		      pnt = p3 + x * (p1-p3) + y * (p2-p3);
+		    else
+		      pnt = p1 + x * (p2-p1) + y * (p4-p1) + x*y * ( (p1-p2)+(p3-p4) );
+		  }
+		
+		if (deform)
+		  {
+		    Vec<3> def;
+		    GetSurfDeformation (sei, x, y, def);
+		    pnt += def;
+		  }
+		
+		glVertex3dv (pnt);
+
+		pts[ix] = pnt;
+	      }
+	    glEnd ();
+
+
+
+
+
+	    /*
+	    // convert from point-values to Bernstein basis
+	    for (i = 0; i < 3; i++)
+	      for (j = 0; j <= p; j++)
+		{
+		  pts2[j](i) = 0;
+		  for (l = 0; l <= p; l++)
+		    pts2[j](i) += invmat(l,j) * pts[l](i);
+		}
+
+
+	    glMap1d(GL_MAP1_VERTEX_3, 0.0, 1.0, 3, n+1, &pts2[0](0));
+	    glEnable(GL_MAP1_VERTEX_3);
+
+	    int steps = 1 << subdivisions;
+
+// 	    glBegin (GL_LINE_STRIP);
+// 	    for (int hi = 0; hi <= 10; hi++)
+// 	      glEvalCoord1d (double(hi)/10.0);
+// 	    glEnd ();
+
+	    glMapGrid1d (steps, 0.0, 1.0);
+	    glEvalMesh1(GL_LINE, 0, steps);
+	    glDisable(GL_MAP1_VERTEX_3);
+	    */
+	  }
+	
+      }
+    
+  }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+void  VisualSceneSolution :: DrawSurfaceVectors ()
+{
+  int j, k;
+  int dir, dir1, dir2;
+  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);
+
+  double s, t;
+
+  // draw surface cones
+  //  if (0)
+  /*
+  if (vsol->soltype==SOL_SURFACE_ELEMENT ||
+      vsol->soltype==SOL_SURFACE_NONCONTINUOUS ||
+      vsol->soltype==SOL_VIRTUALFUNCTION)
+  */
+
+  if (vsol->draw_surface && showsurfacesolution)
+    {
+      int nse = mesh->GetNSE();
+      for (sei = 0; sei < nse; sei++)
+	{
+	  const Element2d & el = (*mesh)[sei];
+	  
+	  if (el.GetType() != TRIG && el.GetType() != TRIG6) continue;
+	  
+	  Point<3> lp[3];
+	  Point<2> p2d[3];
+	  /*
+	  for (k = 0; k < 3; k++)
+	    lp[k] = mesh->Point (el[k]);
+	  */
+	  lp[0] = mesh->Point(el[2]);
+	  lp[1] = mesh->Point(el[0]);
+	  lp[2] = mesh->Point(el[1]);
+
+
+	  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);
+			// "drawelem": added 07.04.2004 (FB)
+			if ( drawelem ) DrawCone (cp, cp+4*v, 0.8*rad / gridsize);
+
+
+			/*
+			v /= val;
+			
+			glPushMatrix();
+			glTranslated (cp(0), cp(1), cp(2));
+
+			double l = 2*rad/gridsize;
+			double r = 0.8*rad/gridsize;
+			glScaled (l, l, l);
+			
+			double phi = acos (v(2));
+			glRotated (-180/M_PI*phi, v(1), -v(0), 0);
+
+			glCallList (cone_list);
+			glPopMatrix();
+			*/
+		      }
+		  }
+	}
+    }
+}
+
+
+
+
+void VisualSceneSolution :: 
+DrawIsoLines (const Point3d & p1, 
+	      const Point3d & p2, 
+	      const Point3d & p3,
+	      double val1, double val2, double val3,
+	      double minval, double maxval, int n)
+{
+  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 Point3d & p1, 
+	       const Point3d & p2, 
+	       const Point3d & p3,
+	       const Point3d & p4,
+	       double val1, double val2, double val3, double val4,
+	       double minval, double maxval, int n)
+{
+  if (val1 > val2) 
+    DrawIsoLines2 (p2, p1, p3, p4, val2, val1, val3, val4, minval, maxval, n);
+  if (val3 > val4) 
+    DrawIsoLines2 (p1, p2, p4, p3, val1, val2, val4, val3, minval, maxval, n);
+
+  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+1; 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)
+	{
+	  Point3d lp1 = p1 + lam1 * Vec3d (p1, p2);
+	  Point3d lp2 = p3 + lam2 * Vec3d (p3, p4);
+	  glVertex3d (lp1.X(), lp1.Y(), lp1.Z());
+	  glVertex3d (lp2.X(), lp2.Y(), lp2.Z());
+	  glVertex3d (lp2.X(), lp2.Y(), lp2.Z());
+	  glVertex3d (lp1.X(), lp1.Y(), lp1.Z());
+	}
+    }
+}
+
+
+
+
+
+
+
+void VisualSceneSolution :: 
+GetMinMax (int funcnr, int comp, double & minv, double & maxv) const
+{
+  int i, j;
+  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": added 07.04.2004 (FB)
+	      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++)
+	    {
+	      // "considerElem": added 07.04.2004 (FB)
+	      considerElem = GetSurfValue (sol, i, 0.333, 0.333, comp, val);
+	      if (considerElem)
+		{
+		  if (val > maxv || !hasit)
+		    maxv = val;
+		  if (val < minv || !hasit)
+		    minv = val;
+		  hasit = true;
+		}
+	    }
+	}
+    }
+}
+
+
+
+
+
+bool VisualSceneSolution :: 
+GetValues (const SolData * data, ElementIndex elnr, 
+	   double lam1, double lam2, double lam3,
+	   double * values) const
+{
+  bool ok;
+  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 :: 
+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]);
+
+      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;
+	    switch (data->components)
+	      {
+	      case 1: d = 1; break;
+	      case 3: d = 2; break;
+	      case 6: d = 3; break;
+	      }
+	    int ci;
+	    for (ci = 0; ci < d; ci++)
+	      val += sqr (values[ci]);
+	    for (ci = d; ci < data->components; ci++)
+	      val += 2*sqr (values[ci]);
+	    val = sqrt (val);
+	    break;
+	  }
+
+	case FUNC_MISES:
+	{
+	    int d;
+	    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;
+	    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 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];
+	int np, i;
+	
+	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;
+	    }	    
+	  }
+
+	for (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];
+	int np, i;
+
+	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 (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;
+      }
+    }
+  return 0;
+}
+
+
+
+
+bool VisualSceneSolution :: 
+GetSurfValues (const SolData * data, SurfaceElementIndex selnr, 
+	       double lam1, double lam2, 
+	       double * values) const
+{
+  bool ok;
+  switch (data->soltype)
+    {
+    case SOL_VIRTUALFUNCTION:
+      {
+	ok = data->solclass->GetSurfValue (selnr, lam1, lam2, values);
+	break;
+      }
+    default:
+      {
+	for (int i = 0; i < data->components; i++)
+	  ok = GetSurfValue (data, selnr, lam1, lam2, i+1, values[i]);
+      }
+    }
+  return ok;
+}
+
+
+
+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]);
+
+      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;
+	    switch (data->components)
+	      {
+	      case 1: d = 1; break;
+	      case 3: d = 2; break;
+	      case 6: d = 3; break;
+	      }
+	    int ci;
+	    for (ci = 0; ci < d; ci++)
+	      val += sqr (values[ci]);
+	    for (ci = d; ci < data->components; ci++)
+	      val += 2*sqr (values[ci]);
+	    val = sqrt (val);
+	    break;
+	  }
+
+	case FUNC_MISES:
+	{
+	    int d;
+	    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;
+	    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 ok;	
+
+
+      /*
+      int ci;
+      double val = 0;
+      for (ci = 1; ci <= data->components; ci++)
+	val += sqr (GetSurfValue (data, selnr, lam1, lam2, ci));
+      return sqrt (val);
+      */
+    }
+
+
+  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;
+		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, i;
+	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;
+		  }
+		}
+	      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;
+		  }
+		}
+	      break;
+	    }
+	  }
+	
+	int base;
+	if (order == 1)
+	  base = 4 * selnr;
+	else 
+	  base = 9 * selnr;
+
+	for (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;
+}
+
+
+void VisualSceneSolution :: 
+GetDeformation (ElementIndex elnr, double lam1, double lam2, double lam3,
+		Vec<3> & def) const
+{
+  if (deform && vecfunction != -1)
+    {
+      GetValues (soldata[vecfunction], elnr, lam1, lam2, lam3, &def(0));
+      def *= scaledeform;
+
+      if (soldata[vecfunction]->dist == 2) def(2) = 0;
+    }
+  else
+    def = 0;
+}
+
+
+void VisualSceneSolution :: 
+GetSurfDeformation (SurfaceElementIndex elnr, double lam1, double lam2, 
+		    Vec<3> & def) const
+{
+  if (deform && vecfunction != -1)
+    {
+      GetSurfValues (soldata[vecfunction], elnr, lam1, lam2,  &def(0));
+      def *= scaledeform;
+
+      if (soldata[vecfunction]->dist == 2) def(2) = 0;
+    }
+  else
+    def = 0;
+}
+
+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)
+{
+  //  cout << "get clipplane trigs" << endl;
+
+  int ii, j, k, l;
+  ElementIndex ei;
+
+  int np = mesh->GetNP();
+  int ne = mesh->GetNE();
+
+  ARRAY<double> nodevals(np);
+  
+  for (int i = 0; i < np; i++)
+    {
+      Point<3> p;
+      GetPointDeformation(i, p);
+      nodevals[i] =
+	p(0) * clipplane[0] +
+	p(1) * clipplane[1] +
+	p(2) * clipplane[2] +
+	clipplane[3];
+    }
+
+  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<Point3d> pointsloc;
+
+
+  int n = 1 << subdivisions;
+  ARRAY<Point<3> > grid((n+1)*(n+1)*(n+1));
+  ARRAY<Point<3> > locgrid((n+1)*(n+1)*(n+1));
+  ARRAY<double> val((n+1)*(n+1)*(n+1));
+
+
+  for (ei = 0; ei < ne; ei++)
+    {
+      ELEMENT_TYPE type = (*mesh)[ei].GetType();
+      if (type == HEX || type == PRISM || type == TET || type == PYRAMID)
+	{
+	  const Element & el = (*mesh)[ei];
+
+	  Vector shape(el.GetNP());
+
+	  int ii = 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;
+		  if (type == PRISM)
+		    ploc = Point<3> (double(ix) / n * (1-double(iy)/n), double(iy) / n, double(iz) / n);
+		  if (type == TET)
+		    ploc = Point<3> (double(ix) / n * (1-double(iy)/n) * (1-double(iz)/n), 
+				     double(iy) / n * (1-double(iz)/n), 
+				     double(iz) / n);
+		  if (type == HEX)
+		    ploc = Point<3> (double(ix) / n, double(iy) / n, double(iz) / n);
+		  if (type == PYRAMID)
+		    ploc = Point<3> (double(ix) / n * (1-double(iz)/n),
+				     double(iy) / n * (1-double(iz)/n),
+				     double(iz)/n);
+
+		  Point<3> pglob;
+		  
+		  if (mesh->GetCurvedElements().IsHighOrder())
+		    {
+		      mesh->GetCurvedElements().
+			CalcElementTransformation (ploc, ei, pglob);
+		    }
+		  else
+		    {
+		      el.GetShapeNew (ploc, shape);
+		      for (int j = 0; j < 3; j++)
+			{
+			  pglob(j) = 0;
+			  for (int k = 0; k < el.GetNP(); k++)
+			    pglob(j) += shape(k) * (*mesh)[el[k]].X(j+1);
+			}
+		    }
+		  
+		  locgrid[ii] = ploc;
+		  grid[ii] = pglob;
+		  val[ii] = 
+		    pglob(0) * clipplane[0] + 
+		    pglob(1) * clipplane[1] + 
+		    pglob(2) * clipplane[2] + 
+		    clipplane[3];
+		}
+	  
+	  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 };
+		  
+		  int tets[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 (int ii = 0; ii < 6; ii++)
+		    {
+		      int teti[4];
+		      for (int k = 0; k < 4; k++)
+			teti[k] = pi[tets[ii][k]-1];
+
+		      for (j = 0; j < 4; j++)
+			nodevali[j] = val[teti[j]];
+	  
+		      cntce = 0;
+		      for (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]];
+				      for (l = 0; l < 3; l++)
+					cpt.points[k].lami(l) = 
+					  edgelam[ednr]     * p1(l) + 
+					  (1-edgelam[ednr]) * p2(l);
+				    }
+				  
+				  trigs.Append (cpt);
+				}
+			    }
+			}
+		    }
+		}
+	}
+
+      else
+	{
+
+      // const Element & el = mesh->VolumeElement(i);
+
+      (*mesh)[ei].GetTets (loctets);
+      (*mesh)[ei].GetTetsLocal (loctetsloc);
+      // (*mesh)[ei].GetNodesLocal (pointsloc);
+      (*mesh)[ei].GetNodesLocalNew (pointsloc);
+
+      for (ii = 0; ii < loctets.Size(); ii++)
+	{
+	  const Element & el = loctets[ii];
+	  
+	  for (j = 0; j < 4; j++)
+	    nodevali[j] = nodevals.Get(el[j]);
+	  
+	  cntce = 0;
+	  for (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, p2;
+		  GetPointDeformation (el[lpi1]-1, p1);
+		  GetPointDeformation (el[lpi2]-1, p2);
+		  
+		  edgep[j] = p1 + (1-edgelam[j]) * (p2-p1);
+		  
+		  cntce++;
+		  cpe3 = cpe2;
+		  cpe2 = cpe1;
+		  cpe1 = j;
+		  if (cntce >= 3)
+		    {
+		      ClipPlaneTrig cpt;
+		      cpt.elnr = ei;
+
+		      for (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 = pointsloc.Get (loctetsloc[ii][pi1]);
+			  Point<3> p2 = pointsloc.Get (loctetsloc[ii][pi2]);
+			  for (l = 0; l < 3; l++)
+			    cpt.points[k].lami(l) = 
+			      edgelam[ednr]     * p1(l) + 
+			      (1-edgelam[ednr]) * p2(l);
+			}
+
+		      trigs.Append (cpt);
+		    }
+		}
+	    }
+	}
+	}
+      
+    }
+}
+
+void VisualSceneSolution :: GetClippingPlaneGrid (ARRAY<ClipPlanePoint> & pts)
+{
+  int i, j, k;
+  int np = mesh->GetNV();
+  int ne = mesh->GetNE();
+  
+  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();
+  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);
+
+  int elnr;
+  double lami[3];
+
+  //  cout << "getclippingplanegrid. xoffset = " << xoffset << ", yoffset = ";
+  //  cout << yoffset << endl;
+
+  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)
+      //  for (xi1 = xi1mid-rad; xi1 <= xi1mid+rad; xi1 += rad / gridsize)
+      //    for (xi2 = xi2mid-rad; xi2 <= xi2mid+rad; xi2 += rad / gridsize)
+      {
+	Point3d hp = p + xi1 * t1 + xi2 * t2;
+
+	elnr = mesh->GetElementOfPoint (hp, lami)-1;
+
+	if (elnr != -1)
+	  {
+	    ClipPlanePoint cpp;
+	    cpp.p = hp;
+	    cpp.elnr = elnr;
+	    cpp.lam1 = lami[0];
+	    cpp.lam2 = lami[1];
+	    cpp.lam3 = lami[2];
+	    pts.Append (cpp);
+	  }
+      }
+};
+
+
+
+void VisualSceneSolution ::
+SetOpenGlColor(double h, double hmin, double hmax, int logscale)
+{
+  double value;
+
+  if (!logscale)
+    value = (h - hmin) / (hmax - hmin);
+  else
+    {
+      if (hmax <= 0) hmax = 1;
+      if (hmin <= 0) hmin = 1e-4 * hmax;
+      value = (log(fabs(h)) - log(hmin)) / (log(hmax) - log(hmin));
+    }
+
+  if (!invcolor)
+    value = 1 - value;
+
+  if (usetexture)
+    {
+      glTexCoord1f ( 0.999 * value + 0.001);
+      return;
+    };
+
+  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 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);
+  double phi;
+  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);
+  double phi;
+  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)
+{
+  ;
+}
+
+
+void VisualSceneSolution :: 
+DrawClipPlaneTrig (const SolData * sol, 
+		   int comp,
+		   const ClipPlaneTrig & trig, 
+		   int level)
+{
+  int j;
+  double val;
+  if (level <= 0)
+    for (j = 0; j < 3; j++)
+      {
+	Point<3> p;
+	if (mesh->GetCurvedElements().IsHighOrder())
+	  {
+	    mesh->GetCurvedElements().
+	      CalcElementTransformation (trig.points[j].lami, trig.elnr, p);
+	  }
+	else
+	  p = trig.points[j].p;
+	
+	if (deform)
+	  {
+	    Vec<3> def;
+	    GetDeformation (trig.elnr, 		    
+			    trig.points[j].lami(0),
+			    trig.points[j].lami(1),
+			    trig.points[j].lami(2), def);
+	    p += def;
+	  }
+
+	
+	// TODO: consider return value (bool: draw/don't draw element)
+	GetValue (sol, trig.elnr, 
+		  trig.points[j].lami(0),
+		  trig.points[j].lami(1),
+		  trig.points[j].lami(2), scalcomp, val);
+      
+	SetOpenGlColor  (val, minval, maxval, logscale);
+	glVertex3dv (p);
+      }
+  else
+    {
+      Point<3> newp = Center (trig.points[1].p, trig.points[2].p);
+      Point<3> newlami = Center (trig.points[1].lami, trig.points[2].lami);
+      ClipPlaneTrig t1, t2;
+      t1.elnr = t2.elnr = trig.elnr;
+      t1.points[0].p = newp;
+      t1.points[0].lami = newlami;
+      t1.points[1] = trig.points[2];
+      t1.points[2] = trig.points[0];
+      t2.points[0].p = newp;
+      t2.points[0].lami = newlami;
+      t2.points[1] = trig.points[0];
+      t2.points[2] = trig.points[1];
+      DrawClipPlaneTrig (sol, comp, t1, level-1);
+      DrawClipPlaneTrig (sol, comp, t2, level-1);
+    }
+}
+
+
+
+
+
+int Ng_Vis_Set (ClientData clientData,
+		Tcl_Interp * interp,
+		int argc, tcl_const char *argv[])
+
+{
+  int i;
+  if (argc >= 2)
+    {
+      if (strcmp (argv[1], "parameters") == 0)
+	{
+	  vssolution.imag_part = 
+	    atoi (Tcl_GetVar (interp, "visoptions.imaginary", 0));	  
+	  vssolution.usetexture = 
+	    atoi (Tcl_GetVar (interp, "visoptions.usetexture", 0));	  
+	  vssolution.invcolor = 
+	    atoi (Tcl_GetVar (interp, "visoptions.invcolor", 0));	  
+
+	  vssolution.clipsolution = 0;
+
+	  if (strcmp (Tcl_GetVar (interp, "visoptions.clipsolution", 0), 
+		      "scal") == 0)
+	    vssolution.clipsolution = 1;
+	  if (strcmp (Tcl_GetVar (interp, "visoptions.clipsolution", 0), 
+		      "vec") == 0)
+	    vssolution.clipsolution = 2;
+	    
+	  // SZ const -> tcl_const
+	  tcl_const char * scalname =  
+	    Tcl_GetVar (interp, "visoptions.scalfunction", 0);
+	  // SZ const -> tcl_const 
+	  tcl_const char * vecname = 
+	    Tcl_GetVar (interp, "visoptions.vecfunction", 0);
+	  
+	  vssolution.scalfunction = -1;
+	  vssolution.vecfunction = -1;
+
+	  int pointpos; // SZ 
+	  char * pch;
+	  pch=strchr(scalname,'.');
+	  pointpos = int(pch-scalname+1);
+
+	  for (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 (strcmp (vssolution.soldata[i]->name, vecname) == 0)
+		vssolution.vecfunction = i;
+	    }
+
+	  
+	  tcl_const char * evalname = 
+	    Tcl_GetVar (interp, "visoptions.evaluate", 0);
+	  
+	  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", 0));
+
+	  vssolution.xoffset = 
+	    atof (Tcl_GetVar (interp, "visoptions.xoffset", 0));
+
+	  //	  cout << "x-offset:" << vssolution.xoffset << endl;
+
+	  vssolution.yoffset = 
+	    atof (Tcl_GetVar (interp, "visoptions.yoffset", 0));
+
+	  vssolution.autoscale = 
+	    atoi (Tcl_GetVar (interp, "visoptions.autoscale", 0));
+
+	  /*
+	  vssolution.linear_colors = 
+	    atoi (Tcl_GetVar (interp, "visoptions.lineartexture", 0));
+	  */
+	  vssolution.logscale = 
+	    atoi (Tcl_GetVar (interp, "visoptions.logscale", 0));
+
+	  vssolution.mminval = 
+	    atof (Tcl_GetVar (interp, "visoptions.mminval", 0));
+	  vssolution.mmaxval = 
+	    atof (Tcl_GetVar (interp, "visoptions.mmaxval", 0));
+
+	  vssolution.showclipsolution = 
+	    atoi (Tcl_GetVar (interp, "visoptions.showclipsolution", 0));
+	  vssolution.showsurfacesolution = 
+	    atoi (Tcl_GetVar (interp, "visoptions.showsurfacesolution", 0));
+	  vssolution.lineartexture = 
+	    atoi (Tcl_GetVar (interp, "visoptions.lineartexture", 0));
+	  vssolution.numtexturecols = 
+	    atoi (Tcl_GetVar (interp, "visoptions.numtexturecols", 0));
+
+	  vssolution.multidimcomponent = 
+	    atoi (Tcl_GetVar (interp, "visoptions.multidimcomponent", 0));
+
+	  vssolution.draw_fieldlines = 
+	    atoi (Tcl_GetVar (interp, "visoptions.drawfieldlines", 0));
+	  vssolution.num_fieldlines = 
+	    atoi (Tcl_GetVar (interp, "visoptions.numfieldlines", 0));
+	  vssolution.fieldlines_randomstart =
+	    atoi (Tcl_GetVar (interp, "visoptions.fieldlinesrandomstart", 0));
+	    
+	  
+	  vssolution.deform =
+	    atoi (Tcl_GetVar (interp, "visoptions.deformation", 0));
+	  vssolution.scaledeform =
+	    atof (Tcl_GetVar (interp, "visoptions.scaledeform1", 0)) *
+	    atof (Tcl_GetVar (interp, "visoptions.scaledeform2", 0));
+
+
+	  if (atoi (Tcl_GetVar (interp, "visoptions.isolines", 0)))
+	    vssolution.numisolines = atoi (Tcl_GetVar (interp, "visoptions.numiso", 0));
+	  else
+	    vssolution.numisolines = 0;
+
+	  vssolution.subdivisions = 
+	    atoi (Tcl_GetVar (interp, "visoptions.subdivisions", 0));
+	  vssolution.UpdateSolutionTimeStamp();
+	}
+      
+      if (argc >= 3 && strcmp (argv[1], "time") == 0)
+	{
+	  vssolution.time = double (atoi (argv[2])) / 1000;
+	  vssolution.solutiontimestamp = NextTimeStamp();
+	  // cout << "time = " << vssolution.time << endl;
+	}
+
+    }
+  return TCL_OK;
+}
+
+int Ng_Vis_Field (ClientData clientData,
+		  Tcl_Interp * interp,
+		  int argc, tcl_const char *argv[])
+{
+  int i;
+  static 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;
+}
+}
diff --git a/contrib/Netgen/libsrc/visualization/vssolution.hpp b/contrib/Netgen/libsrc/visualization/vssolution.hpp
new file mode 100644
index 0000000000..d20654b5fc
--- /dev/null
+++ b/contrib/Netgen/libsrc/visualization/vssolution.hpp
@@ -0,0 +1,220 @@
+#ifndef FILE_VSSOLUTION
+#define FILE_VSSOLUTION
+
+
+
+
+
+extern int Ng_Vis_Set (ClientData clientData,
+		       Tcl_Interp * interp,
+		       int argc, tcl_const char *argv[]);
+
+class VisualSceneSolution : public VisualScene
+{
+  class ClipPlaneTrig
+  {
+  public:
+    struct ps 
+    {
+      Point<3> lami;
+      Point<3> p;
+    };
+    ps points[3];
+    ElementIndex elnr;
+  };
+
+  class ClipPlanePoint
+  {
+  public:
+    ElementIndex elnr;
+    double lam1, lam2, lam3;
+    Point<3> p;
+  };
+
+
+  int surfellist;
+  int linelist;
+  int clipplanelist;
+  int isolinelist;
+  int clipplane_isolinelist;
+  int surface_vector_list;
+  int cone_list;
+
+  bool draw_fieldlines;
+  int num_fieldlines;
+  bool fieldlines_randomstart;
+  int fieldlineslist;
+  int num_fieldlineslists;
+
+  int surfeltimestamp, clipplanetimestamp, solutiontimestamp;
+  int surfellinetimestamp;
+  int fieldlinestimestamp, surface_vector_timestamp;
+  double minval, maxval;
+
+  
+
+  NgLock *lock;
+
+public:
+
+  enum EvalFunc { 
+    FUNC_ABS = 1, 
+    FUNC_ABS_TENSOR = 2,
+    FUNC_MISES = 3, 
+    FUNC_MAIN = 4
+  };
+  EvalFunc 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;
+  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;
+
+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);
+private:
+  void GetMinMax (int funcnr, int comp, double & minv, double & maxv) const;
+
+  void GetClippingPlaneTrigs (ARRAY<ClipPlaneTrig> & trigs);
+  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 GetSurfValue (const SolData * data, SurfaceElementIndex elnr,
+		     double lam1, double lam2, 
+		     int comp, double & val) const;
+  bool GetValues (const SolData * data, ElementIndex elnr, 
+		  double lam1, double lam2, double lam3,
+		  double * values) const;
+  bool GetSurfValues (const SolData * data, SurfaceElementIndex elnr,
+		      double lam1, double lam2, 
+		      double * values) const;
+
+  void GetDeformation (ElementIndex elnr, double lam1, double lam2, double lam3,
+		       Vec<3> & def) const;
+  void GetSurfDeformation (SurfaceElementIndex selnr, double lam1, double lam2,
+			   Vec<3> & def) const;
+
+  void GetPointDeformation (int pnum, Point<3> & p, SurfaceElementIndex elnr = -1) const;
+
+  /// draw elements (build lists)
+  void DrawSurfaceElements ();
+  void DrawSurfaceElementLines ();
+  void DrawSurfaceVectors ();
+  
+  void DrawIsoLines (const Point3d & p1, 
+		     const Point3d & p2, 
+		     const Point3d & p3,
+		     double val1, double val2, double val3,
+		     double minval, double maxval, int n);
+
+  // draw isolines between lines (p1,p2) and (p3,p4)
+  void DrawIsoLines2 (const Point3d & p1, 
+		      const Point3d & p2, 
+		      const Point3d & p3,
+		      const Point3d & p4,
+		      double val1, double val2, double val3, double val4,
+		      double minval, double maxval, int n);
+		     
+
+  void DrawClipPlaneTrig (const SolData * sol, 
+			  int comp,
+			  const ClipPlaneTrig & trig, 
+			  int level);
+			  
+  void SetOpenGlColor(double val, double valmin, double valmax, int logscale = 0);
+
+  
+  friend int Ng_Vis_Set (ClientData clientData,
+			 Tcl_Interp * interp,
+			 int argc, tcl_const char *argv[]);
+
+
+
+};
+
+
+extern VisualSceneSolution vssolution;
+
+
+
+
+#endif
+
diff --git a/contrib/Netgen/nglib_addon.cpp b/contrib/Netgen/nglib_addon.cpp
new file mode 100644
index 0000000000..391e036012
--- /dev/null
+++ b/contrib/Netgen/nglib_addon.cpp
@@ -0,0 +1,105 @@
+// small additions to the netgen interface library for Gmsh
+
+#include "meshing.hpp"
+#include "mystdlib.h"
+
+namespace nglib {
+#include "nglib.h"
+}
+
+using namespace netgen;
+
+#include <iostream>
+#include "Message.h"
+
+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] == ' '))){
+      // ignore these messages
+    }
+    else{
+      if(!strncmp(txt, "ERROR", 5))
+	Msg(FATAL, 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] = ' ';
+      if(!index && txt[0] == ' '){
+	// skip initial spaces
+      }
+      else{
+	index++;
+      }
+    }
+    return 0; 
+  }
+};
+
+// replaces the standard Ng_Init
+void NgAddOn_Init ()
+{
+  //mycout = &cout;
+  //myerr = &cerr;
+  //testout = new ofstream ("test.out");
+
+  mycout = new ostream(new mystreambuf());
+  myerr = new ostream(new mystreambuf());
+  testout = new ofstream ("/dev/null");
+}
+
+// generates volume mesh from surface mesh, without optimization
+Ng_Result NgAddOn_GenerateVolumeMesh (Ng_Mesh * mesh, Ng_Meshing_Parameters * mp)
+{
+  Mesh * m = (Mesh*)mesh;
+  
+  
+  MeshingParameters mparam;
+  mparam.maxh = mp->maxh;
+  mparam.meshsizefilename = mp->meshsize_filename;
+
+  m->CalcLocalH();
+
+  MeshVolume (mparam, *m);
+  //RemoveIllegalElements (*m);
+  //OptimizeVolume (mparam, *m);
+
+  return NG_OK;
+}
+
+// optimizes an existing 3D mesh
+Ng_Result NgAddOn_OptimizeVolumeMesh (Ng_Mesh * mesh, Ng_Meshing_Parameters * mp)
+{
+  Mesh * m = (Mesh*)mesh;
+
+  MeshingParameters mparam;
+  mparam.maxh = mp->maxh;
+  mparam.meshsizefilename = mp->meshsize_filename;
+
+  m->CalcLocalH();
+
+  //MeshVolume (mparam, *m);
+  RemoveIllegalElements (*m);
+  OptimizeVolume (mparam, *m);
+
+  return NG_OK;
+}
+
+}
diff --git a/contrib/Netgen/nglib_addon.h b/contrib/Netgen/nglib_addon.h
new file mode 100644
index 0000000000..32c39b147f
--- /dev/null
+++ b/contrib/Netgen/nglib_addon.h
@@ -0,0 +1,8 @@
+#ifndef _NGLIB_ADDON_H_
+#define _NGLIB_ADDON_H_
+
+void NgAddOn_Init();
+Ng_Result NgAddOn_GenerateVolumeMesh(Ng_Mesh * mesh, Ng_Meshing_Parameters * mp);
+Ng_Result NgAddOn_OptimizeVolumeMesh(Ng_Mesh * mesh, Ng_Meshing_Parameters * mp);
+
+#endif
diff --git a/contrib/Netgen/nglib_addon.o b/contrib/Netgen/nglib_addon.o
new file mode 100644
index 0000000000000000000000000000000000000000..3dd308e5258043078f7ccddbe02f1b94ba438b36
GIT binary patch
literal 291636
zcmezO_SZQE1_mJp2w-GjU|?ooV41_fpa2qKWME)10WmN!cOU};%MJzx?f?b`26ias
z2dRsXFG;N^0kI%BK0d@XA_T%lkj!r&`WcuQ7z8*N7#Ok|7+B!a@$m(j$;FkqN%=X&
zC8bG*m}Y>~GCg2m;Ml>yz`)ADARNKKz!1~GzydN0WR607d~SY9d}dx|NqkyqUNS>`
zyo+OqBS;E{*&Z-3fGp*n!N34=hnNKe0|O`wIAJOvW|X8B<>EBs3Dk@^P%|8$W^f_Q
zNGdLdaTpk|xC0!9AbXKifthfz`1s`fyyBAh<ov`OkPH^TykKBp@L^!!UI2+hF%M|G
za)Z=^Fs2#k@xl&@YcU1}?l%y>fOLT|vKhrCMVWc&U}2D7FwF3Q*vkWsAgD4B#RMWi
z7?M6fK8eo*B@@iJWlvyWV4T6gz#{>1hu94U1_r4H1{NfDq$elGr&c7V7L>%7Bqm{o
z2geTv28MP92Hp)IJF(&T_|%N}w4%h^RER95J2)E{7#MvR82CWxf{B4ad<6pogF*%a
z1Cl%Pa^fNBG`^su2us{@onc^L&|qNTZ-Mv)ViyAg0~-SagE)v6AD@#*z>F`Tw9UZ4
zzXoE41jrpAy`X%j2r&bkK8i|{OY)1rX$(DHz-f#Dn%6*DFmQZ)N@{Tl+z1BD@IWyG
zo;J}80>!fg1A~+U0|OH)1A|cw1B(MFkAlJsR91kp$8iP*#^VeO41x>{jG(yHMKc45
zE1AH+zyJz=Bryht8V;sq`y2m%sF|x=QBxybF@ceRV~XN`28%=n3l9d?qYbhQA}3@S
z80woJgXCInFf!D5FtGL3w>++Zs!6m^WMpVyWZ<w!U|`q{GK+(GnO_5g0^IC5$`v)W
z7-nZS$S{bUkYQk`U;G-`Y(o&=gMqcPe#vX79n7`(>~NaTz*_6Uw5*1Mc^Sxq2@L=L
z*S9b*_{?Ka&~Ij7blArrW0S?eV3Ew#;laSX0l{WDqTkHG;IofG!TbOJHx>;H3>FCt
zj0r9O|9k)czXHl<fbwU6`~$LUf8zf)76~jW^)Hx~SubE<fQH!u4+hpyn|KBWP&hU?
zajuxWn1RK@iGgGCVg`;0&I}Bb7c=loUd$jcc`<{?<i!jU7Rd}U9t=zeC+}fU0O_+x
zV3^S0#JK^aXF?1Eg9igED=3_6>ZB_?7+7X{Ffed<Ffi0W)Uv2FIC0Ja*+2OZNG;1v
zkT{8UyW+N+r4z|+Sq}yViJEHZiu%K!D?AvObnBCES9mZm9tGKHk-)T~!HIJV)E$A=
zi<u-q?tsK2(?eT(1_qG2MorEgp!jKU;#^>Thk>KflYs?f9@xLK3>_0<m?S{zCde`r
z%#dX$nIOwhF+rB0W`Zn3!vtA|mIf!z853g|7#g$wf3b98P-x&};IMFF;Hi;`)B}ar
z#26+C4+iFmAbabNfZeHTGoOJ0Y)1>5g2e&`0}lqqkDxer*vD|CA%=mWA?g2@ni?sQ
zhGqtlgcb%C4+f?{4+e%EAa{WL+u+2x1gzhQfn!1(D4dxCE$SG2JQx@~Js22NkkqZP
zsAG@;`vt6y@gz(hBo0asNOEgIaS1XXq#xwo1}DxPjY<qG4NMFiAhC%y3=E)fYRvrq
z1*8w`$5@d4j2B^gL3z@HfngyeE*LC9`YaR~5*nO1*Gz6=0O!fwiT~e#@&zbAqGWt1
z2bQL5GnkgyW`oKD=4GwjY;S5iL3x-3oQFYa9!>rN0r@Qi<Yy3&uOT3xKtSGwfV>6)
zc^>@otRD!-Um+mBgMj=T0`d(6<WmU9dk~P<As{b+U!LU)0r?vQ<o6JeUqC><1t!nH
zz{tbEz$gPv?vzll_ArB=X<m9xW|E;{Ze?*vQEFmtQfZni0|SF=P>_ER0|SG8N@|&Y
zUTF@ffeFfA`xpNI0?I?6IuKN6fa(iSIXFe}|At0o1_k|228J3>DUlgW3@VQE7&7!b
z85kYrF{pGfGpN)su$SmJFz7kXV+hc1V9<7)$Kavgz@XqbkHNwEE`tiFe6wiz{{UP@
zEnr~v{{PPqT)xd?5CN5ep!&@F|DU@a3=9HLKCk!xKW9PqTPQLFOps+z0M*-|`g`}n
z|6gEbMT0Ygf_@Y8KgYQYHZ=|GCACUYBKi#sTr)ZuR2=p(@K`V~D9qqwP|<H<U~rhn
zAOLbJ)LlFlObm<@m?7qZ${kP`fcy`uqiwtx7;Lpb<r`B)qb5@UsC={3gw&A}l|l71
zgN*n8KOHcApzuQuJ5acR$~sWJ(%{78(5T5|fx`?}kRL&Q1C_Bb|9Suao%8=ca}%iH
z1?ru_yHquh9tZ=dg~<TwhQMV&ZD1Bimxh5+fPsMl)Sw0>CPq+8pM`;esRz^?TLLkF
z3Di7e1htnf7#JAAeE=S)j0mXV2{Ibgss|YWHHxt+z$b`-vBr^oHUk4=tbhOy$aM?`
zpmy^A|9|%|Ffi-@YXK1)^BLzc&1L3v1ogQX1Oy%Hz@mI0YZ(|oR)qZj{}0rE@&QRe
ztYOe$U|`7k|Nk4v`7vN|5CJu74hzgQz6K^i$2tZEu=+Xw|9`V#U|>iADFxexTRkWw
z&iw!X?G6J2(*c(M|NqSgX@Z#x*EgFLBrL+00I>z^UPhMx|Nq_rwNF8EQ1=>u^6mfs
zfB%5m>|k*afo2{^NPsT@W)@WIz)`Cin6EG}a6MQEb^@cx8V2Sw5I*M`1_n@SVLrjY
zz;u{_f%yQG-T<OkGceC#VBorfq;Cp@&$$4kZ#4sR2Ll7w8zlJ#2%mEYNS=X#xdduX
z1_J}v9VB%L5I*M$sJaNKIuD3A$f00&Izaevbrv9X3=GT$AbK?evjPJH*9|0dB_Mpx
zB~Wt(K<XG6m_C5$)eKB87#P??5b8l=3?M$60z^F%D4iT;U|_lcRlkOTf$I#C`Uwy|
z=M0eg)eKA}knn)GcQpf34#fWu{u%}*kiV}iWPpU%8V05mh&(KOm=ZwtFff2JC)458
z3``*Zzd*7N<o_278929q>|<bHvVp2Mfzm2aS^`9`W?<rBVBmU!q@M-C=iC6%&-e$T
zjuDg|km7?8lpgLZWPpV;BRD;*W?<aIz`%6@VIJca2%mEf)VviS^B5Qy=YZ(d42+=g
zL9(9_ls=v;WPsb>096+OiC0)SF$O^RaQl5g>KGUpH5eEe4>K?@N<e4^sQVdyfX1dE
z?qWExhJoP=gwNH%z`$^Vfq?-U-wbac;vjp#>Rv$jaCJ){_AtzV(i5O`1(XK$a}P5x
zFhIkbK?Wqgnt|yG0|U1Y#D2CDYZ#c$K=>>z3=C{1Rx>bdVPN3)fXc5}!@#r#!e?2)
zz`(X*H3QQW1_o{ysC>s7a7hi~v+QADVC#USD{cp<e8w7Zx&rZ8ZZI&gWvpgk3SnU2
zwt>oftYKgRjSbl>WMKKiz`*9Qnt{oLfq~lsDzCAIfk_9#XBA*zVAEL5z$C)Jz-<DR
z=U4+yHy}Q%4g&)l$7%*fP<R?Z<XK;=0oNDY1`8QjJs22RU#w;Tr5|n`sQigF450J_
z;<KhOFtDCj%>YU#+!|2%6>At6LH^fR$iUjbz`(j<H3K8a|0+=Vjx`L7Jq!%oDhnA{
z=P)p^cC2P#EMZ{aR)ET9tYKixf$&*(Ffg!YtY%;Y`CkSq@396vj>9dpkb(6I0|Tqa
zYH*pxEdiC+Si`^w^1sAF2G$P@46GWf85lwS7lF!itO2*jxJ4E+u<<Z3uyU-1q$dH0
zJj;tU4B&Jmu#kaGgMoqN#cBoyQ2g^i<w5BW6#qO68Q5GH7+6lMW?%rtKL=EP#To_%
zkpDRrGO#5uFtDsx&A<TiKMPd8V+{iX$p0)08Q5wV7+5-1GcbVs&j6LrSi`^o@;}2u
z2DTXt3@jO|85lws7`XmG<vrGb#|A-swk-?{EFP=D>6PmTR9<5ZxZVWu*)A|JuxP9X
z*Kb^3pz<7R7#Kk1#+QW*Y@l&lXq%D|R9Ax<5Fpxvf$=A(CU;?Ayau8j7#M$pXd4E`
zlOWoHf$=qnHeq0V2ciua7+-*B9R|h^AX<Zg@fC<xVPJd@q7@hzkAr9#2FAA_T7rS`
z4Tu(DV0;Op1sE8ggJ>QG#?v60gMslZh-P76yc)m&PCuafim?Wg9~nXADzrS~ShAXd
z5mdiG%OQ>>YZw?~Ams~_1p@<^4=R72EM#DE0Pz_Z7{T?*Y6eD7eF8HlXEnHu4l7SN
za@K&$Ay7TWRKUOh=8HheSEdROp8*`NAa{XUYhbzn;y#8H1_pMYg$)1yGcd5{tYKhC
zfbt)J`0K&;a?F9m2iJo25PP9~&W802j2sLM94S!wj`a+TpmMzh!UxGS++<+jfW|jh
z<a!2%3k(d5(EQ02u%3YdRK7#YE3kUdIC2a`KbOLK22jJ23!0z6@&%w06C%&~VLdqg
zaYE~PuzUmq0|zvJao$)DPLG_>b^=&lg@J)R0jht=dhmn;=K?6dVLbzbFarbE2Pl6s
z0|P?@0}~4<enX&i0F?HD(jHLS1xh<WX&WeQ0i{i#v;maXfzldKS_MigKxr8$EdixP
zptJy#=7G{2P?`lwGeBt2_$K3R1_s8D;3)<M#>Wf{jGq}87;iE#Fg{^mVEoR&!1#%Q
zf$=K?1LF?{2F7m;42-uJz~w6=sI9@wz`%Hyfr0T60|VnT1_s8b3=B++3=E9-7#JA;
zGB7YcU|?YU&%gkxw;BI2Ffcx3U|_t?z`%H)fr0TC0|OHiWQ>(oZs5F|!U%3*GcYi4
z{zkC5xIqnYP#R%i;F974cMCv$S}q4DTZVywD~*p8qyjRo?83<)1xgbj5f@GdX$A)N
z3Md;S4(jDFK*bxN;xY^j?BHGxR2)>dgVcb;CqUIGGBB`%`XL~3B?bm|P<tK32Bkkx
z9|y!%V_;wh^_4(u4F(2wP}?2ER%2j*6m>3~pfP+7P@XDPfxDAyC)gn%ix{|$I52?R
z&mhad!1Wd(&KKPPPRk%geEAIw4AvkP1B2o*gc>DjB(^#d+XRX2)4;&s0@17O)c`IZ
zKs<wW2sH*r8W<Qrd4++2!Qg8H149@{1p|YjMgs$bACzs~zyNB?fOv+!4dAo}Vq1$g
zFfd#Lu^1SvRT>x=+@b8Hpy6c*JINGbSCUf$1A`Y-Jfs1fzd^jDv<3zSZxD-tA*r!}
zfx#2XzTd#W0G_L0U`YDfz`zKauVY|f$avYnz_=NrChH!;{HzZR3=BUZ;yE)AZZ5pl
zz`$S#5igEJV&^t6FieDqmk1!lE5%eGf}lAFP@HOl$|aDH3nzmX5?hCXfdj16g_A*>
zfq_E<ss<#k17(B6L48}0IUsRR-x0*tWnka{)qx<kAp-*kxC8FO$zaOBz!Ag1P${m$
z$pE#t(!qfN#0Jf?R{B8MEDQ{lVPMO_^)v&63nK$)Xa;00hz$y7aM#_15j4jKnVfZD
z1cx(Nn+qd2ej#p#sDZc{q6Xq-h#H8S85uxjDQGwaWPT_EIBivitHAB8+}yyx0L{Wx
zHVzD6_sB3XR3$^%vJ4DW6C1#DI3Q<MEoxw3&<C*?7^;!dfA!x6=vV;*!)Eyg1_p4x
zWMJ4OfnXmrLa>kDK(Nm(L$I$JBG`9S5ozr4@dgIQRS<h$yCdBE9W;3dwd)TkuR++1
zEL?DN7&$*QFffDK?hFi!0!teh7|(;vVH9zN`-@Rcw}AoFrUx~1%mNx<bLEUyVhs!o
zpfL#s21Y9#gqaS%5Nx+84Gauf5Hr2(8yFZMv(Jn{y$uWuw;|$Tli>M+G3;&w1H*2J
zcvO4?15+1-9S6!siy`c!@CF7@9|z=~QcxUqL&Pgh8yFaOLfCyEaV04GMFVu6k%4gv
zC?7%HJXH`LR*X{>8W<RqA?l~rG%zq7gRtj-{0nvS@~I6B45AS6H9HXOO*{zpc2F4t
zRkH^)4FzQ%bVR5*R@(q>OM={gx(gwGp|62~5i+gJculQ=fzcmg*L_e~R|{c30oltB
zVLxAm2<LY%8W@-$X^-*ewFU+TNVqX^2*cGd@g0TR#Uu<Wiy&@elJrN2%RXpeV8{iV
z&!mP(Crmoa8yFa4A>u}5aJMnpJVdbFYY^<f6a+h33&BnS#jhm9oRmKekotmwfhiSK
z{tH6HQ$haHg0OSi5$-RpZeU=rgoszS!qXvBV+DfU4oc^(5H&rZyaQ!V1cf2gzte&n
z7#Oxe)GPpXw!lmVrUf83=z%#*i$H#dgg?^~P<`|XBEH13fq}sp!d_W}aN7n@euS(c
zVcHGK*WmJxfoU%&%^ZQKKYR{ewlf`mh{XQTz`%G2qUI>bkDwt(1_q`xj13G7kTlP9
z$+Llh0bIT^FkOm3=)DB0Pr+pv1Ji@g4GawPA?ADpsRuI|m_CB)BN!VLo-p=Yg!_M9
zZeRe9Su!v%{rlO#zyL{O%#3T{X@i+vq=A6}k_MQ0pENKqM1c)r7G4QAhgsSW!Bzs*
zM^JM#wjs>XJp(Vhm<@vvY|A!;8oN0S42+;MpMindVGF`c50D$cWjO=0FDNZR*}=OT
z7#Nu$?uk@``-M4f8iJh+Y7aowXM)lIxaGyboQ22>%mo+WX^gp&9pRoj`343i&>%4b
z19NA30|T_%$~=8y0|PrGuQ9Izl_#YTH>`4LU|=wZvMU=H7(we57#Nt>oNQoVgp@7J
zYZo*yFqT8qtbK{FYcnVhLh=Ih7TX2}20n<IT^S7w4B-&=X;3|42Vvhplp)M_Kf&_?
z^CL-wna@FOFi1Ou`86o6A$0-sr`rt-3~>;1zJS_Skai36-^p;jEX*7Y3=HaEH7u+P
z8zA){0|N^iNF0*(Sa^>%Ffax})QHSR=#>J63AlaAz#@~<0Ik~@SX2ue7#NHp>QzBy
zbrO{Q4&G*CQT^WlUN-_7fL8;RV~{ovi<&;P-DSbRq88G?!1NiSSNqWca2uOJf`LW*
z@dF6kg@Hx;3Alm4zz_pc=fcPUYInidAR4p;2h;`tg#)CG%?M8C;B4l?2<nG|`l{eI
z2}lj3jSW%L12q%0MgttCE{qJxkUlP0eF_7(-w0-d%JvluEZR?1U~XX1F>wI(>lheh
z8CZ0jpllfi79CJN0=dnCfkh_{>aP+87M*ygzg94?=p;b>mBPTHlL+ZkFdTroCzFAJ
z;Q#}R&Ll|SgG9k@@MQq?ds%dLt1vRa)O>be0JE7HSadm|Y!(I<U1_NKJ`60nGHB*x
zLFVVcW<uC3x;iS1pt_!shk->m5~>$m*Sat=WP|1eK(2tZS#+aRVB##g?P%&PL49+m
zdV9#&1K2K*xD5k~ZU?G+Kx1i)4h$^1n^46;^#voi&)~wy05S*UmqVy(z<L81Sajc@
zii6dMFtF(UK@|t9k6>WYQ$rO8O)W8kg<Kdx>zY90x?neh?1iNhaR%`CFI*fnqXbHO
z4h#&8koGymoqHHq^vuA9fdT>4IAsLedw_vOF9B5?>`rhU<H884BN$IGu;>+_ssZag
z!@#093soGf{sIGw-YQgau=*<uEP7W^#X)`qEpGz5UmVoI2RRR7FB1op4H5^}VNh{c
zyWNq2fe98DpfO|6`aF=|LE-@nEP78+?P8k502;$((dR)G2el!YA{bco(b6R2J_ZJ+
z7zP%7UsUysnG6g}DGV(7rD)<H^%)E-`peM7LF#iDSoF`KiG$P^FtF(VM->OVvx0%e
zKpj;aY;O$%i-A9?I9PoH1B*d9syNvFEetFMD^SJ3>L)O;7+gRV2dkgLz+wnVbfCZo
zrxURHH4H3<nrP}l{@TF6Vi<@j4pzT|fyJ;ARUGX8Jq#>{3sJ?v>JKom7@kBG2dh8A
zz+(6jRUE7yG~dAB!U!7E2h9tB(ht)~1_sdF5}198fr04{1B;O)s$NiEiJ60e#mEa)
zoKcK{fmwoq#i$5X9MpGUc3@yJnu8`@#K6GZz`$a31XUa~9>d(iz+&_jRh$8&zJr0q
zSQb?rtbPgui?JW7I9UA*1{UKoRB^ESISeeu%TdL_>K8Du7@tQK2diHKT5^mk4pzT{
zfyG1vRUE8-4FiiwB&s+lk1#{Vd>9#=7#Nr@fRcv`BUt<jlnoNU!@y#aslo^@Cv_ND
zOu8ZEFcYL6;RMwS9H2T3)UVHEVBjcWU@_@Iv5ONF<{T9aEGFB~#6jw77+6e>p^5Ki
zVBlzAU@`fCCJs{H!oXq*scb=x1f^$)`5g=_rdn9k_b{-STA_)9%%8x(Vw$1?b3cn|
zy8~nlikX4MbTY(`3>FM5rc==Lg4_TplOcL{FtC^|K~n=Ve-8tTDQfuuQGbAe#q=H)
z^+y<3%s9}*L3W*BU@?<G69<`phJnS*2~8ZN{sIGwSrD2yNc|NC7PA^Oagh243@m0n
z&~TW;z+$!@8V<4yEM~i*;Sj^XVzx&C5<UzJEasqb14!tzn1kAuF!l-f_#2Du1O*0$
z46p=??FkK524B#eAp?u;iv|Wp&^l}e1{Mc3Ch(XxsQu;;g~V<~Vz1%j2ARUyz`)|L
zEd|_W1Fef^oXyY4&;S_=W%1(Y2C+f&sm$CEaSjOv29_MCI4ErgL&ZUJW62P4F3_5G
zbEr6I&dmiX-oU`XqR$Vu3pD4&s>Tm?1D6K_1M@<NILJ*blOW=pFBlk@UqHoU7#LXY
zK*YH~^_mPn8^~?!8Vn4~5&Z06HfVf_5hBj^0yNhN8mtDH$iTp!hG27)AlPg-5NwVU
zNbD~NHs?MBo2>}!e6XRcAoXAt0|Q$uLYz|xp`P6ki4D>VRmRzZ5a$d<uo?293P7t~
zAkhM`VI4x8@iT(WbP2&`c!ppz+9B9XGZAdYT?jUlHiFGAj&K9}UnI6Kg3YlWi4BSt
zsCJG7ggE<l1e-${!DcRoDh4e-Z$+?~^$=_}W(1pcHiFHjk6^RjL9p315$1Cu*~_*G
zp@!o%LJcc;{Sr7h<Kv@3!mzJO14((}l)@(AV(41N5Fa0fRn7n^7hGa!nv__anH&#a
z0q2<<Tw-XLoRL@*Us9BqSyJqo?CNTonVVFa>Kf!7Tw<1!pPZPJ3O1Tp?PkTPC7{K1
z0m+#p=`$}*P0h|vOLMIVE-^HU_slPj4^B<ZPRT3^E-^F)^Zg4_^K$c3NOKR^+=BdK
z*WeP9w1WI%Pea4__}rx8lEjkK_!3v_hGSVRXi{96mrP<%S{7&Im**ztRg$XFtfDwQ
zwIt7#)IhW>%}YrwO3TSFC()&*rDSNbEJ#XB&Q43r%yA_xuFWcn3ra!W^M)o8Ea7dD
zUk3FF*zAxnKSP7~_~eR;#H7qJLnDLu_~Oj?<ebFf;`oxvg4FoTytI5*P>Kr)#+D5r
zQl7-7Vvs>lV~ouF(j8M${PW`7Q}a@b5=&CU@^eaaQ+-p5Gh71#$^!g?<Bbgc(&Iq_
znR)5)0g0e}H6^J<#b8S@jPWlh$;{2HBF`8@BbaME^D;}Iv5Y17pvhG)H3P{jD9c}w
z0vJ^lB_76Q66t<MH3{9@XtK!5dO?LDK4}BjGLUpiWll;wXn$RNQf6KXDBdzsa|%*H
z+nAz4!h%Z-O(2D3ej22>#9CH@!`3V>wIn??&n&kxIlr_79OobdEFt<c^NVq61t~K$
zhNw%+%t=iuO~bF2r~(|>QRXG7#U(IT`9WQ3np>G#R0MHo2=P9RPp_zmFGwva&d*EC
z$t<aiFEe0>j}Of&&&*4S4@xaA&4rZAP+JU5P$S(n$S*kF&>WgekTMBK+yo>Hc99>v
z+Hr=~YAHDZklGDgF~lQ+%QM*(6xm?^2A5c5<`;t`Q%yiQ1E$&7w>UjBAh`;vh?vke
z$uCIFa}DqYRgPFI9s)6Ik_pP00oaQg!+11bV)KEs5j0~5mk_YaG&v_Vu?SR)L8?s8
zd~n4E^({O|Twn&m;>G|J-jH&P_;`UE2M%Gl!(nD(MlB=~ic5;}l5-2dZi1>cG7L)1
z%`Z#!%*jbjPt0-6NzF~oD=7v$1vx3?I~%&<2uRF^3)liseU3;WxH1&PVewIZ#3?sT
z&dD!Mg~Ul-c`n!@s87JIH8gNe%t`hEHI_VF%NX=Si&Kk=_0v;Js}eKwQuX!HbBi<d
z{lKM!J_C3oLVRLMN`9VRazOz@dUCQ+d~$wnL1s>BiXKCJyt|LTlcP_3yeCqN4ztvU
zsdB-qDk`|dB0fGdACx2+us0B)HHDR%hJlWurfm_Jveq>+G%+`^Fg7!>u!b|utr;@&
zN~}u2N*GEqGK;MOGz@f%G;Omr40Mb&ZM8HEbWAjDD~ge}Sy(Y3b3!00iVY3S3=|D@
z42%qPGz@f1L3$w31}SZmO4DpW?NXiMk`yZ|5DVHu1&i7#*jZU;=B1=oSm_&p9iwAp
zYNlgsWL;8G0ueIPv`qwg#T?=zb4_al8~|0q*c=Y5tqrUV4UCMfH4JntAjVi|g2ar>
zt#ynnEOaakEsd<>!DUW7Xz!_&m4<<irKYVi*pDE885(HXg4}1I3E~-pNkc<TTQ!g%
zG}ufaq6V7QR@fuR*$C7aH;A`3a@4a1ZDh_ZK(-E-E<+SuNr}a&2wjH8Xs$$Z9$tr(
zVReL|u_kU;K(v5b&*1PeG(oW%?0IOw;I<i>My-t;wah@#VWMem4Qn4lOf|)6D%b`?
z+-4)SgFxmRg5w)x0W@Y)O7e>!cHs0SsCY#V7-F;-x`F~m%K+vb&<^CB)Dlobgc$7x
z5bcIA?bdaw;Cy6_n%h8G)kKqlw5$~Z(q*b?TWo9;Ush03q=Kd$l*|pyAi3Sp97<bg
z+NOb0wWf{%C?m%E#%JcmC#9xa=^KKCEg{M*G<A%P5c0{TMR0i?GZTbFYF-LL!V;Xd
z<9*}vOG{9U)G;)&fa(EBp_pc926G2U3dKA_OPCXqO4H&YeO!obMh2jQ2r8muWCU>$
zB!|Oe#mEqn)=Xh33zT_`j36F_3xabq_BH@!)rB(w85x3e7SZM)rz%rTYin$&ic~X^
zlN!j(g3=`L5CJ$-7(q)Ch`)^>1)Y%*IB1MaASIoVDWrmc%EIy@v7WMkSqLi7U}F;C
z?3SKdl9Qhdafcalq(E&lf+kKQ3y1?Cj)d9oO{@dVAohTAoRgk)L1_|Xhy!9Tw7iEH
z4f8m}|Av;}U@(M`&=QDPOD*B{fJOnp-UW+*2T8zI8yg^d+1L<V)fgLrQZQ(s!v<-X
z!_EqtMnROZv8Js<v6+cAsD3my2Gxv)SnEbm%?Pgzkr~!tV?Y&sDtKT9I#2_)#28eU
zLxf@DHDGaAs1xgdLj#0&i5L??4xobkVn_g?qzPk4JV2w!7!pOska#28aU={Z!ChFH
z2T8KV=E(7f;yO~@V-5-{@c0zimdw2P#InT997rfwAX{T>2~H*^(1r;(X+nd9R7)*j
z)_^jGqn>qfUTQ@N+-sl)0kLsy2GI&LC#f{IfJ}41T4Cm-r;_I!Q_$dBGLk8I;D9ti
zDff*bnHv^%5FR8FNexJ_ec-`9aO{D`#y|sWkRUKILXJL&1yD;Zz_CZFZI*EJU6Ud9
zmFAHfQs%`Fy|Az<D20Z&i4jTlmMO?h$uReT#s(oFj#9axhPa6dq{20URxZ%MAS+0~
z)`PqJncxtCw*262dJ=0)Q1{*15KCj7RC7SJImnz;P;!8T2uhI#tzeAIQG88wy=DqF
z*R{;r&`Hlay`Tir%r!9sM;+0on1FOb4M_#DVGaeA07RQ&3f2lWCLNTGA;zGVktodz
zNM0Z{`I~|bcg+MxP;qKWI>a87Ledb`9%$Z1rAVyKO+Z$;1|$a?#|In7gDo!r^~oT%
zlNjWtV6D)=Dj+-JKsup@q=MMwxdp5hYD@ug#DU_U*c!kTtP>F{1rWEOG_LT*3K`K+
z0ErH8Tp${>rYNl{R6C7O`|3n{4s1F~>($Uv%LvqU1GT47Ta%=O8q!>wwV|UHxaSHn
z5z$ICMXj|UnFd;c7(+VV#t?6j*%ks%un{+L2Xdqd!rQ6Yu$G!BN+n0lmKxY~q_olu
z9kop1PDE;|krFSk;R-|Oa0NI{U{itMlx=JQE@X*KLC6z{pg=T-xezi>2sYA$w5}r3
z6eGw)3z&)U8APz5#^$8C6RsOnAUJAS!c7HDFOp*-Y+@4RMnmvm2`Dh(6OUjcO;MT&
zC@CDmA+?S`p4bFi3?60ySqz!D1lwu?84e;g3X$hL!DhnqKgvLsDRg)Y+AjfD?9kjw
zo?noMw+tP%Aj4ZAub?+_Orb+)P@7SzKvOelPafLJBC)iEj9?nYTN_|$6q%Zml*F9D
z<C;*j@(Vx{viU`};21GQX)KspK->)R4>Sx-&A=f_YJx}%29@4WOA$kDIr(|%3Z;3)
zndy0{DGIQmaZ^jkV6mwssB4Y2XKeuLS|enT2b~!}gWcdEb_-30vi!^xFx${TlOaC7
ztRyK1bkrhv#v3ATuE_u$t^$wRfd;UOP4e>dQmyojGz@gi3?L&Pu(AQVq64hL)jAn8
za}HMsDdC_hOu=%l)_JA5sYRK|*cF*WWnHb4^K)_%OAwZ#m}L%?b+s<Z%*CO`1SI8Z
zotvMRT9Q~)iQPU6n6#^PZfbFHVtQ&ZE`?BOSL?)_9PDb06LWH0twH0Ixdl1UNdrSj
zpTZ10h!XD!l7J3p21C{wfI<Q^<6wpspoRwVa22lB@xk%wIr&M62xC#g!xAnJR+^kx
zlA4}hlnL?-*lc4+oS8v;IA+iuj)@60qd=p`&=_PVTr-+k@x_@{sZej3K@Btq_x(*R
z(af<xF$b&>Y*>C?GRy=Euue0`;DI@0@W9*<G}IKIUyxW>ni^kFl2MeJm=X`t6klS6
z;$idnV6eC=#%zf>wBm;j*g>0H=Fq7mgeY`073y<v2MgP5h&im=2~!B_K|vIP8rz5o
z5OZiTi?9JSq-73i4`DR(V5Wg5VzAG{n4?7wq?m%KMY9}Q(qgsT1kxBolu!uE1H2&}
zcJw(Qb65!qQ)&pBy~f&;HHY>ev04rFH_QcSB_+&oNFW4wC&T*6pt(z=kcISRkZ19O
zyo2MB)T0Fkw9v-qBB;aAD?n5i1slfWa1pEx15*#KLE@3rgI$yYo997KL^ucMF%-g5
z8)~&=4((e)eF*gofwTsxaUkg@FEKY2T)~+``$JHZEFk&N0-O)wb2OQG>EKCH98;$d
z;lvzJtx=L+1Xc=~PX(oa3+N<<5v0z9r9s3>KogKtK!ZI_de(4HLMmSiXd?{fHWSbw
zv$2u2uAv!txYz<R;0pCUrqQW|pei=r+6c*LMYz$R;soK0Jn-^Fh+1et3!0O%Fon!P
z85y7!aApuSQ1cz_t-;I2G9lw@7G_XQXc^cXstDv;M|+Hd%K~4rHN%}!L94|~!8MC3
z*4B^(W@5$A5)3}rJ2No{RKkNj4x4;K^EkMM08RZv)Vh{g8-TomtRGx1gA<nptV;sT
zCWfGD0ihYJ5Me@Ieo-#i0rAl4s}f>}B_uS_sz)Qk_~3X<Ev}gm2l;}}_D(HMh9+~g
zFfcTXN2mbDA~^4Y8k5!xm{T3#h7f2Tq}b5d3^Z$)mIkQ@Eg|^@)ay0SF*5<x{ovIy
zP+7<Xilq^_s<SlKw5=#bnnJMx%}*K{7@1%s1xsjM0-aKT)?t>WkhTO=)Dqf)wls&-
z#ZX~bKES>>71C}tgp>m4rM@LB!D3t13aNhJI>7}Td^pn*S|B0Z0LuK9sF@z8i$K%X
zSk@uHQV$N-z;vOx1X`eBbqOe*KvT!?&IVSaja)0BH63a_WC?5KVAW*^)defW6b!-1
z*U$hu`U7>4C8QT*XaH>(T4Ic}VlzFttO8VOIO$nKk{2k88X7=bI#81lsRSJMkcJ2T
z)L;rW%{3UZ5(FGF&@vZlrX{S{fzgm9CEnPh5^7u-N{MR=DRB)AP}8&ptWg3n*%GxE
zg~I|!i4IDGm_<6s0%)0zEd(J2JepQ*nA@PuC#bIt4WL;I%7><7qEb(R4_Z3VMc4sy
z9`=yS^+eO94NW}IIsxhsC<RJ9pg2Zz5RO1A$N`y+6kxh=vq28Rp({5Nq6?fTP;)m@
z5(c?~xTwp`1QoN;@V90F_jADntdDDKtjT~f^$S`uVr-&mn;IV+4_b%`TFsx5SriXi
zcwuI0YHX%s4bcFf6b9=sMd$#nx&*E6N6`XV4F>MYfYn=SG8B{)rDUe1#Y5JinHa!3
zz*gXD)e2nvgL-hqMh5Y|@kOa=$@!&u&}CtU29Pd{A!3<IT4Hi)Nin1jFa#~w0xe=Q
zG=%1OLqq5?Ekn?nsfuE-YAeJd7BIgU+-HIqVP>ugsws?(K-EifVsZwoTL&^*2fmaA
z)R2N(3hn5DrgLB`T7u&<Q{usWaEP&nhNjRJZHAyVXu9ALDMQdI9MIA`L-;ZrL+DZm
zLqkhY?<_L~wI>E?dKiMLW0?8iu3lzJJhU$lwGK3p0A4_cW*sOI!rS&x(?I=DgaI(~
zpq+l0d4|}XkH<Wy0WkAG0|YR`jEFZ3)G^OYi3jP1nFJd}fSG1YylEz&<ed@^GXZWQ
zbhrU#q6z6HLQMdh2u@V6h828Vz!0>K1|v?4pq+a|*kTt@qQDlapiu{OgTdn;i76?d
zT#{Op3hM8JD=W}~8PF<5Gf1NyT3O+0)<Tm3sI7%!7Pz&j2g(OU;MKlZY{BkOP_6|n
z4@PK!83kF21#ih18bKQn*c}R5Ap>4&30fQj$|q1|pgamLDj^jlT6%`Gx(woTu{O{_
zR$_5Aq``&IgxbzA1T{x-1}L<{P0&>V-pM%JWQ;cM0f`aN;%S5ybQeL}4$vS$jbegM
z!Q~Fvm^ic#0e1&XJ=hWOp0=S8tksC39^ByuweF!!UV`dD{cebQu=$AO0jiO32D_m#
ztaXPs8DLKZ#<12mipycC0IUbrwuCk*u<L;I^9`Uw<G%698L7$H@x_U`sqvsH0#f;d
z)>I*R1KJuRkR2^RohDS%V7UmCzY1~^lT(q)7;wfnhK>(mO%;Zq6-(gS(HPPWgp%+E
zI!ag=fqHF4sp$5&26zV>#DhA1(2@Z~0i+jcXbf$`;B*G4DFZ6;z%+LI4K2a7KdSK%
z+XK7<yg|FRGE<<Pa72KBOGs#_<1bsFT>(QVjl&V3LJ<}SV6$N^K(jwHuec;JCnp}P
z24X*GjTTa@qvlXhjSu#`A>K$Z1lf;bwkzE0V1_B!LU=I1l!6;BP-j3Vkg>-F)O!2{
z28t`dMnhZywH#77STlf{dC+E^nYktdj*(?>`wlerTx<c_1DFm@x1d#0pyiB)CPtdL
z_AWq2@}b5;w85%=(Eb2x@ccP^g|anb)ijwyYG9XwM(2u6@<21)kQo{1_$WjSTmh#f
zmLwMBl){>Wpp{J(#qo}yA-edC%#_q3E2z_b<3UQH(E{xSSwQ0-x;n)Kwl3QQI?w=<
zho(>O;9~Q5h&iCrz)25O51K%;8%!<CEZCxLR4bq+AsG~q3@-2>`3>Sc*fI*3k)UM=
z;GzRG)n8E@ACy`ESr%%7-j^%}CE23X0*LcKt%uB{)bvcq8azYTTrb>Q&??m8c<A6#
zd}aw`LJ&NxsRbG9#2gt_z`m<Q2O^F!HmU$wK5K~SR8SEEaUGItL0ukj>N17YhR`6-
zOwCI{sLDxA%!Y;(T0}xCB@<{QpoBI^7sw3IG%B>NfEofVx1qrT%Z^YNn?lTjwliR(
z4Bo-T@H)aZ7c{wG-~=v{A$3qrYF>It2Gn?H&lYMtv?ziF2-J8eg=veSDZDCz80wmf
zY+X)fZe|J89@K=0-5XGQ(7XXQ#WgqA8tRFZ%;K!lykwZc(6Sur0cZ^h^8gkngDNkW
z5#ZSrsIkfU1<*PQG_?xe@c<ok1?MY}v}-^zw54VbO1+?lEW~$a$SOg?XetwnK{Lrv
zwT94vRG1!6QiCXP4M@%fWjK4#JR``$c+iwFWXZE3sN;*`D)1OELX~SkvY%yqe0p9f
zXfL&)IY!R$42};jc6AK~Pj=gbN(VDzG^dbkBxo-s$V?MZGY4X(2|+W1<AV{(p(Fc-
zu+YGC0>}<Sum*6TmxA{9LxU1EZ@?2TBvU|B6DkEuTfxPkV9x~`=9&viT#%dx%7^h8
ziA5<88(>QqiVcn8edFPhu$&qkpPE}xQW;;A3R%`>Xl4%ey%}_kf}tt20S__(NjcaA
z%*h8sxP>@pLXnh!TF2PtKn+3Dxu6CHG*h4jj3H>67u<USRkonslObrfwxSrVe1sI^
z@L~$mdrI_7cEvg@3$hg4eJX~um5~&pwcSCJ!1!DT-CSa5g4O^lhPa>@yY=RfGC3G(
zJxmq4-T2F7bI5Qgt~3L6KyEG$CqSobVNQUnLw5w;bZdy+5$4e911&y^b8~~dgOLIo
zb+Fvf9M&p<#0rLTbf@8{4<K2}1SNPt=?L6XMRpRXfW+@0=o}CzkT4aaI|sGeKugF_
z=iqV&erG^Cs8DC1szrANY8`{-3TR@1E}+5|O}WXa#-m3ObV38<6m+Fvmms>y@Ztg7
zlZNDduxZ$oz={X7?kZ?<89Dob+OeS009IJQ+9%Lth$t~3Jz8+a0@WU%Ib}3+z^MVM
z9~L?A#E}>bu5OJS!7WqRtTN127!B184NYXtkR=jGEk+CIR0d3|DI|3oLXAK;+!U+{
zG;;&B8?6uk%@kK)YifdSCsqK}!L)C1BHZYtXAN&?T0jRQij5&{OjzJS3L5WVxVV#^
zbrIN?ka`O?i46-H3k%SgaZzenYEf}2qAd+?E?8M%G#8)^ZAi<R3N3ATTN_f8AY#W1
z;&7zsLCbES+1-j_5*?2eb=3C)X3qxU2T)8P;ty8XnG~T$ASBCs2N%O7K$D8FX@5gY
zXxAJXe`w8RSlEHIgSytBpmWl*hBUz;3BVFEPwyREYz7gAs>w~PfJ|wC`!NVr7BC5@
zx}sE&!BDe|ARCpSj=@{}!85EG#01w|aJB`N7odrE<njVqpFwjgv=oJTEw~tN0JyGA
zPAo`F&Mbkp(2?8?lR&sTwWtiT+Q1Mte~;JU2pi3dQj0;FT_M#w_y7(_2*8V7=&2l#
zId5>=5266HCIHEAXuS}a72d(creIA_$HEHc=vb&JXzc>fk^)f4YyzFYNA2_>nVMMv
zax<jM4_gBO^R%TUWVI}`M8y@{5F;G*tP`OfD@#bj!8^FvD6s^^ct|S`D(Z+>F9e&$
zwScwd3w%J6jmQ-SL~DT$T1@~K1}7Hl#DW4u4uTnG1`$Oz(FiuF5A!RGhI$f5OA3)j
zk&J}2)j(%nfR+Rx#ib#dq0o8_Grd9$1GQ;DNfwmkEntm*nBzfxaYz|%1Y0%$Gag1m
z-3~P$8fzF%hGZ-2g3{uQc=#SNBLnErKg=xDEQW5DC0sXje;%Y43Q3FbNHl|pqK7*q
z%8(^dLJ(CNN0@+n#Yjm36eg$|q2A2QD^4wfq)Q{%0tW1UG=w$+a0MU4Bv%4G<$z>R
zPZ?r4I7y&47@*^3sBr~LZm^6QTx<rl4%9@4WdkG7ss=pi3+i&1VNfoV!eKO0(4)2A
zVWEWTQk-cR=3b--GcrJHgX1+4ivy9<JX+0;-6*JE(UU7YFcI+rn$$-QN@(Q6TYykL
zE|Z8#1&|^swJ5O|QmYt&77!p?iq{i}@Gu1%0UF1G767322gv3?9gFUG3=@cQHM9gW
z0<CQz#uAw2@PY>FT%s%k4Jm>8BGBm&Z~<ZjI}QdGJTMwHB|_sGmf8`XHHRAJ3hv*+
zGY@F71hRjKG8JkGl)`BTA{T*HUl45zuFQnw1LW)kTF^nN*;t*7l&?T*Lx?sL8Vu-}
z0?D~Z84Q1hg_X8&8l%q$H<j3g6yzNYk8IFV6+GU8+X_v*1S5i2Hy}qB>`WL~LW9xJ
zWQ@ZNSR#xB%VC)pbo2}f{wEZ%#JT_x{jf7&V4jB2I6Mzp<^*c?p!e`dP6WY*phf)f
zU_k5V6L1AI89*t@Vj|cCyhIn&1^}(mAt5r*69xtD2sVm`mat%Vpmh=mgodF7w6hAM
z;UzrDvEmnO5bp^ZjR%eRn}P!dG$IczsN)k$E1)GmY-r8U1Tt|5UXYMjS^*kp2hDGQ
z=b~WRiWAdd6)U925nK$ORzjACOe;ZVlR;aWK%M)dQs@+r5omoBcxD9BsSbt=wI{<A
zxgy5YK#Sx-GbRvs!q!4TLlC`_4N1k&Oa-N2u?XsUgA4-A*yMuO*%p^4LgoOBU@MoP
z#-eqX;oS*%JeYvAx&}dtJ!|kG&Jg#37C9lMI9P&)_5zWdVhYv-?Nq`il8uZZT_=!R
zL4r{E^wg4g_&`6@BGl0aLv!fjFie{ajZw5ijZDkTgUpv0!B#^-{f-u-CXfL-Xjnoi
zB!8HI3<C8BKs7PAH){ks(g-OG&<un%8*mu~s!YJq1IjqCB}2rx4XdL-@eDQywKss~
zCP=-C%SFjx=U5kkd<qSG)clNQ8g3t&g3SPT%fM!WT?XlIg2ovjvY?s_A&c5KK(h%o
za*zT5Y7xjNcnF|&3D8W!6#`I$pg{l%f%vq{qT-VH{4{7#K&EiKgNqG~QKWJC5?vX{
zjR@~T#!j)A22uw13VbLP-7sW#<|HCJ6H-or3^oEg6Do_-m8hyfwj&$~86d<o3#1C}
zMo58*sR-GLkf6-V#~PgGV0R)b!Rc5mN<nTxxEK+vIF!PjjR;ySDv{j|4nP#ABZAlr
z>~tg*I9-mZ64l{|pvJBe?rubIV=9C?ySOx|7<#OZ5$NPCq%sOxzQC3sKuZEBg_N(%
zAZCDjgid<aplO`MB4||zTOkHB3$kMqRvx3ZvO&vGpt`|f1M>&$SSzfCK-~kSV8slQ
z^UPsJ!a87(0vWU(3(2#vfe=U|18N$Sf*A_sLMhx<AWD4`v~esH??VeJZ03NwL{N94
zH5yTjfI0<A;V}@hoW%%gBx<>j&+AYlp%!3sD%fOjrn0tXNXpO80q^>^Fwtay?2O1y
zO9P8o8fY@WFExOO7=lg=NJ%Uz&&)&IcL0$x0!g7MH`cT*H8iq-?|8ES4gDK|mXm>(
zoP!t085)5P9<cyzo`7tHgBs!kQViPQ0^V0=%@FTclAoKI9ABPa1UtXL60uJMyyOtF
zPXoNGqSyj*%qT=0)HN^z*#Ox>fw_MJw)rABJ{f*^CurZi5opO9sD1-2X9KS_L)``n
zS#1d3`vB7jUTvQYS!oMCN!ZB561sFB;$hJFB_@yp3b9HY>`u^fdWcT2A)syVkk$WY
zka7V{Inow*;<jHxU5jTECfZtfBiNEVP?&<JSRv!OM)0L~hLBxmuzDEQw1ge039-@H
z$Tb&yBocJ-HGDBO$b9gyE1G&kxO&uCXd}={KTsKf#XV5xq3-I3*zaNlJ~{@z!w^1Y
z4Kfdl{h(vWLFcJ}&xQe6h2zju*a{m|*SmlYHw7Jw0*Yv;?%??3qEyiBAh1*dX?B2C
z9)kN1koGcK{DODOqUm!D3djU?gbYFFeL+@h<>cq3B9bjA*BF6Ld;ysYO1@b9X9!9;
z$te0j=2{y<cJijA7MB#|SE8GVwq6R=(}tk5i>?vbP*Cd~&GYbG(va#A-S?)TBP~)(
z;LZTYA=t|hv*2sljX;OAfV_)-ngt~G!RxvZT42Wbg3e1v>;{2G2()<#iZ*D10#k<2
zA{Uyz5!=S0R)E%*V(e3atbD|}PsPX-lA|#U0<8^0*8y6w1u_dGb3%v6&>RcOoFGRV
zfmRcOCh<V~%|HUM$^b2(Axn#3+XcYK#v_hiFhW1s1L9%Gk{+lY*kmGPQ-YBhv`+_f
zmJw+EAjBPz!UEZWc-#e9^Z~U4azGBqNYK_n&<#RXR+%ZV0^d^8wiwjoM>@Y9R3br6
zYYqlQGTMGUluTiinF21sVby{w;v51<ssOE4gp@jvo;kE*Z3H__1+*~^<Pjs#u__Rs
zLV6+?A#RkJg5oXo1Cxx*ARQ5S6d7fv;Ex|{8o-B9Km!ib257Gj=4d3(fc${X$v9U8
z8ks>_%P7u;E&c%=g9GiHgBB+u2OubNKy@<OMh8T0Fv?5;E%40*t!@UL-T;d9yma_+
zNT4P86fdQ>LRk(EURZAkS|?v@g1AJ$(A-E9G}2T6Dpo;*jF2RSo)bZ9F2VjYfs8Z2
z!xXyO9b_&zaG~i3W2u&*5wc^Pja&opF5EJLH3&chPDqRTLA$fiG@@0YpaqyXTncs|
zw15MTWFZ+0u0cT)EzlYqQG=R8t1dK`fU8iD2FkZQSb-PzL*fTi{}&q?AtJ~Kw2&0C
z_8%N6R-gsUuoWfHLz<vzwAch>5ICp96o8vqC8<TZnR%dtl|ZLk!CGcWs$mMis==!$
zK+A69i%as064Rk)PZ@#Mu_9Wv;B6mf(0UDGlo4pxD28rSGr`3TXelqae1hmRG>S)E
zy=w%jrG4Wwi@~dcQ=wT5wzL(r>=zW1(1jpIpi_xJ4Ob&`=#ft_1+a_`8l(dU3}~G(
z+)(KF8YmAJ#}{WL79rUKavZuYh!TXZ;?xpU=fO%HQ|JgEq#<c&idwM-L;Y+V?;DRW
z0%R;?ku*pQWL08P4z#}tUu6o3T~MiktO!*rWI<AVVqOZ^Pe`Uf2dIz&2NZ}f=b`nc
zpsq9m)%qaIuow$kwgPc-QED1!3=|x-MzC{x;PyZZ1RTKt-E0ohkMKx(QDPF*G*|-w
z7N}^Z!HO9e4bR-*umbJb0htBrxr0{mLPz>R83@wogjfPvv<Yflfx-e5^H9%&+ItXX
zka<#&3D(f0nUYytkPn-*1}*XgmpCv-qQyLR#~FgW4mAhnK+voVs4#{$Goj9dE)j!=
zC<(?x+PUb)gPQ@+9T}hqL-I7(RZw|Egn(PpP}e|*58$rB9wXS(6L{wgs8tSg5o9%(
zH3NLm9K15p4%+Ggl{m$QNJXIqc;8Z9YB^$*9jXE2d>JFqxklhvfsMFBg3SoDAQe<x
z7$U?WJt|QA;%*06K>M>`RqzG?xI{*3OTyNMA{h?KWgtsIG{$ObuxXI9!60#u-Z+Jw
zYy?&aZuLOth9EX0YAIy*;jYV2RKa{<%@7aof`ErQK%FWoG(Lh-3&5^~#3r~N1J$9R
zy&m9AQlOI-%@Jc3u+@g3h6t$n1i$hGoE$-iQGgm$29~gsBCHv(B?NFSY6MCs#fBIy
zL(o!PP?`YAgX061CZMetByn(>K-%+TjAEH*vMZjX0_pkS)eH-J3@3sX<l=B5$i+sW
z)vKTexDn{cCD8s765Sl+9Sl051XOaPr!`ARLx+I7kqtm=dO;7v!e;=Uq)9l`;7zW?
zl$4^>V#ww#BiPzkctAi(BqPuYRZvC;g%l|03@srN@MZx%cbY@3a1Ba^4f(>>p~CDj
zfwY7{_CSnB-BROCobjO337zyn2X5wr&IE%LvY>OXP!a*e<wh0|8d|s-f!44>d<Z+g
z1d^U%KE&q&3uuv>3fja7S#z6`3KN660$TaPJPd2|LtO#tEP!rLg1CtoI}HsGR)A)A
z!9^adr3$vw7`EyZW+$j10F6#sLIN9R3*Nv*UvCYct%h0w?Y+aS!0s(Wv{S3_g{39j
z25_$ivZ@Wa4mXA^NF`t=wE2S1E_0~4t^p8htQm0JP=|DcG4z%?<h$n>k~6@!&>3nX
z>8IolQ?MPzphdD3seb9;n=NxntQZhX@JU1<`BXpmcz<sz106s2_~6hWH%DjJcu&88
z(2#i7pdkMsD?^Y(n7>b`uWP)Uqo+@3kgJsuNGv$S2SuSVSP*IgOr;4((9P4wHQvuZ
zB;L(G)X&ArRL7bjH?<hzV`I=FS5U4oHZTPrT4xMeGzt;`9T%Ki01g&o1L!44#-J6N
z6~$ODss;NFbR{IX(gYa?@-9dKbRQYwu1-k(2R5nL*eEx#A_G*#8G{a00`I=pw6?G|
zu+{<30;FZ;rRIT(Hqh08(7Mjd#JV^&IX^EYzbGZO2&C6g6LyIg$XlS@1o5DoP{HGc
zpi0tGQwMsJS7B*lN_<K_sQXuvngY`cy~+z*J0n^z#;_%yAg6+hG)Qp*3JfS0Tq{B@
z&c$(iFV1QaWICv!iC;IYUbJRN$u9+Ufgs7oQj-A`uF#MKtr9IZHZm#5&&-1zLS+nE
z@>x;r7!>3fX#=@i*2)T`Cjg{K$IuRR9Dt#bg>`C9No7GXWY)nLw1g8~ldF%bud823
zNMwMkj)9#OIHW-ui&N9V_ie(og4?2~_60+ffVF|{Rs?G+ElNv;UeyWHY6{vhgsK&K
zo2O9<ShKN(3CLdSviux$y=FM|f_0i28i94@<R@pRrl5w9Ii^-{=y-xIg#_z1Gcf?`
zE-ozs&DRv?=jG+6K#wyv2CamwC=PLS@^Q7v%qszhskxa2D5+T|<(KBABo<Ysrht!^
z2F)xPgAUUI<tt;*TEmKBPd^veh<KxTXCMDySC@DX$6yb*S$0-BmXKTNprM=rJ$eje
zh>j)XRyvTQprHYm1}!0k*kJ_n9K;S|oOVDGKIqUis6p_AV5+GDnw$gs9wZNS2VCAn
zQ^(NI6dtx2X{C9|(C7rM<b${YoFpJOnu5~}cq}agbdV}kDJVx98XFpc4$=e_1S$Es
ziJ5s&Nzf`dSYm{vesG)^8k?Jeodt@Kl+@&W(7nqrEm%^6TVismOKNd)QD#9&ei6h7
zQv*nP%1tauEy_&Hfq4tGybER}WG@~##hV#JJerx33OfVN7__1b;!E%uX`Udd%(Tqp
z#FEVXykaXILvu(h<(1}^r52TBCc}?#)6_9EhXgz<&{GqOk~2z*Qei0zv>FR+Ht5Ky
ziejhy3geUzkhG4W1>`zG44tri4=M^Rpx2n@7v$&Ur&mJ52DDxZVkqcT(qd3q5CT)7
zV`yOkz63G3w5TjK1!OubNI*-OAlksW)ETS*nma+>v@`<8YI06#G3d5(m}SrtM2tbF
z{Zte?=4BS=mlWj}WF|Yq6zUjSnu4!TOv%hm%`48#hsHa|2bLD#OA|rotreFf<`zIq
zHPqBGGB5<+<CvS6m0yHS+63&4+|m-*wXVjXV}L+4mM>UL$H>6KK*z#5qX6Vd(0#$6
zO28Pjt^({!&}s-+3V;=x9sxnAX;3eN(~ps%A=u@~`H4lvsh~OsYzSx(1lSPJ<dKn~
z3D^(0={c!osX4_U6(A27nnOI0layLgo|+0SP{5MLpydweRzotd5x8tIGBU6LC;Z%;
zg2bZKyb`DnKqec5mNCFIg9;;1x-~L3hU65O22hESn4F5HPshl_43Zttdtb(&(Q#12
z+Zf~{P)!Q00ZmW`@sO`E#$Dk-C+koY!|FGv&G7y^$YxOOh^!cAUkR&Xn8~PfRmPy3
zzL0%n3|gE3>OjK0Xbkn6F=!zGSOT;X0MxUDsem=Qk=+KL;{lC;yQJo%mZX9*7^H=b
z#p8ws2u09wC1=nj+`*8F2AbF)JvwaOGcv%Y9N8r3z1*;x7LrIH>#2|(ZfFTM2vs$*
zInX*Bw^Iy_!6v}eLG^~D7MJ)#4yQ2&Er&o3bLdF5F=(v;IKP;nPM&!qJJ1Yb63AF5
zJ!{956mS&{30Tmg1Z0DaL9+!F#b6cSN*sKw1UyDTlkead1<jU&V-$2<2~@pt3Rp4j
zglz${+cgMu?w2{(7UL9G$a(SZsU>~^P**`mzTxQwydDVTTjW+FiYHCM+M#{~8w@oH
zI!X;Q${2PZi81JYkP2`i4p9y=8s<^lelP_qhBz72q6SSEqXr-(CSWFk0u*F2wj>WV
z%P%<I&;V>KIK;sRc7TnBb}QjdhfN9_gC<uYp%3XQ!$SrfPB5!b!v|~u$lc)OQO2No
zSQK|d&lQ6ih3;-_hG9A!>A+m*Hd&BGpkfF`e`bX%Hoag^Lxzly(=S%lj(XN$AB9vF
zKxQ?JK@-WSJ_0pmQUm-wAx&>^;)Hn*HG)wbX9+hKDYVd2kr`wO9n3hqp@n21HeW;h
z0WBS1v!aMNhi(Wq2F*f(<J=H38w9f&Yiyf={ZI_a4xnHKP5Ge&4rnACoReY3K?`k|
zamW#XWSS$m0D+cNkQ@e^MT0vTG<{Z43{?Y8(vYBs+F%TtRRdcMx&;I5C3A2;0;&V7
z7@IQ;4IEQapo*X+2#g0F_kq+0P$8(>plefLZi9}@z%nOlDnRzKp)rbSpp@XKXALT2
zf>R+^6&jmD=15V}jyY5XB;c)mGV@YV-2)u}gSp2TG*bi#3&>a>5iT=CG8N)3Pz1xK
zjNm4lL$;yAlL_vk!4Ta<NC^QuF$8X+1!~j9*c^2P4JG(cj0B~6BWO5)(*wMSgPkh^
zw*$@9=ysq4HmbX^S%RfFvVbfWK`{tgz@R%39t@!J3)y!#)I;+IO8Wzn3_+6zD7g<b
zl~hrT+Ef9D3^?(_5`i&jW(k}+K(j#LoC2C}s3?XuZj7K6E_9F&*@=ecpuB?O6iAk_
zgzE)If;BXmqo{;9+8i=`j%=zCw9SR;aHK?tmb}rjo3RBXO=8R3IE)6xswJv*;BbOC
z9-P};A<o5?9nByLpt%v%7>F*|<On<g!e&E^El}%mJVk{Gs=<ih08OZ%MmuV)fodj7
zk~G9(C?q|?CMS?x3~FE?5)JM^#xNCc@PdjJ(3A&0FQ7*$eox@B4og;r-mHOQBsA;c
zND}Z6!jWTfXoHmOsIge=p9UKCfTj}YT5?#J!_u`Wq(p?oqcLcX1)N(!vna5hB&-LF
zYC1R#gFC{IE+Kdn6vd-PChn;vsG6Woa?K3#3ywDh5B7QD3{js<16Nm9&`cAk#gEcC
zf%*xpYBUBNJpwOUzz#$yj7&fiDIo8efR5Y%6-FkYIS9B8uwraE+tAE0B?ZL+kdnw8
zS`s0$GM=0aZr-~>Gct-{VBeU4rU8)CsR?LI9^zVPa|zn|fW#G6cN(LZ3(v+TpizBP
zvmwz=Ox22MCOD0nfMy6#&4g5@@LY^GwTN5_8=@HsN&`mFzy!w|I7BrJbWA{V4yd-E
zx*Qbept1uN94IM}7;7+dEyzw#?n5yOnrqP=2@eX824o-NQ17T`4fQ6x)o%itO+ob{
zXfQDqX~-R%Fo}sPLnOmNu@7+`d<p|PfMWt0aRn8P(9DlN3{lL)7Ew-m)+osi(ho2J
zO-P`43p5!4PS2nT22k!ZHbkpTpsfIufHyQjH5`(GKuH63-~+5kGXb5|0I~pXE-b@9
z%|(tMY{nuY$F-;^A9{y5>}Up<EubkXP=X^SI$)^<Y#by$Kt-7kcp3uSzky_Z*bxmd
zJ79$~)cxpPGgN;#rlcSlh^1HRsAmlxoIz3ri6U%cN`~ebYC$3HsAr9#-2^oEff~cm
zo}LM4asv{>&^`e)rD4ydM#x5kY;%J2Q(V9k0Z_MMYaAIGf?7vVWeANf@Udj@fEQMs
zM((L4aCK0v!AL^jh%v^}fHX8hRtAbxQ1~Y1BtvVuV*fNh(DV@4YHWjk7S0%oLH0Z9
zS-WLcfD;A8$0nd@9OU3dOBE)d(;7g@-2^n(0t#&tBk){{Q)Y={QBh(gxYU4!IC7e?
zgj)ou#w=huz@@IWTV`Gg=x!ZoSpstnjs^rYjpC`4%+L*l1{zEl+O2}wgFbKuE$>ON
z2WBADo}g6F3CaF>sXmF7sYTFeLh&57j9_GmS06OWK<N(@Rxm5U$;JdUgM<=u;GQe2
zV#F3DhTwKSvPlRFkVK)zqqlotT~5Mb1Zt8YnFkFaXVB#i{$;5}If(@YnR)3>(1}Z^
zb&v(k$SKIk1eX@5v%%8N`FX{e#U-hEB`5}B9d<A>#-Rmdpp%|8SQb(fAjZ_O=4?|m
zrBDML%Tht>wmqPJLhI&{P^w$NOacWS$YqH+$<XOdoO#{=l;<IeK+bX0vkp!zf$$;j
z0ZsOyq!`pxZ)^(LLj$LwH8{4UWC=GKlDpw!)WOE_Dd172AkYff%w$vtppDd`6*k0J
zY-oaNFq-w)>;{bk!2<xj31w)3Y92^2nwfAfA;Q<KC_fjP$3fGSi6y1bJ<cYexlepy
zj3-Quks=VA1+Ea!p}GQGmb+vYmn7za=6!J`4kK`d07(oWvr)A{lM8A{K!eN}a!e+U
zR)Yy>(zYVCD7CmG1AIF-sMP`~reUcaTk^q(MZe&9OSmDfnGm1)<R^k!9f>)h%oq$i
zM$`m$y$miVL3#*Stu@Cm5_B*JIK2j^mbim2!G>mD=p9AK1(KlwLJ>4Fd=o1?AWZ{E
za2aA5>@+k$sDfta;M5X?7(^%RDjQtB0!?p&D`L<rGpHgqM!WVF#ihn5Hb4?Iv@4tf
z>+(Xn^DyrcD63$36Beu}&1X=7?*Xv{UJ!u0)b6PzzL|MYD?k(8$XNrHKwwoC%nBF{
zvkKW`pn+qMiD;Gu8$%XnLlZdYA_dSaKPWetfac1Ptu+D7euF~N7<#!c%zkY48JeIP
z3C$_)sU=SNkb{9tK=a|q=EDj#Xlv92G_wu1&J;4>1QI~x5}2hZ;R&jzo$@POK?4}@
z01Y+*T^J4NY@yFBW3dxt1<WdA=rjyTmVvS)d{PK9LIP`uptftUR*)bs_<<S$FtwnN
zbJDX$5(1}tV@v4iF37QLXo#c?nq@o^i$Q{crHLt!MjX}}-p~kH9n{X?f}G3}&%BcS
zfW)E_sAg=9W)oC}Al;x!6r?*iwIH!5u_QGGG}wj52urXLsH&mnz?vtnDd~`zI1|u<
z0OXW|G+PadJy?$nMq`aVLnCB!F*;`8CFf3h)}F9=CUBsGYz9??D2dJzt_G(m&<3a<
zwATt+w}9+^*enFB*9vnujK=B#OSqvpt#!`NEr72&$JUItfT@56b#Nl|uw)a^iV9>W
zVq_FpXA-nR092|%CvUJi)dXZNXah|#xNrm2C6M4mUmXCm1=>_Lft@UeWr)B8WD3+w
zM?Gub)FN2wFohg;jhrt`!O{rTp1B1@`DKXpA10tx3D~>~a|mc%LPe^7K!~TWXOyeE
zzoQS%{+pp8lIh^6F+$b{>BS)_@k&h2Ps&V$+<RmKTK7P}A)q7<3ua_b8d_p86_*3t
zic(WS#R9yP$t%k&%FhEW7X?QRw$8PYIUZf0(grjRk(!>E6Osx!o!11kcmvrtXc-kW
zh>GkRlx$_;12qa#nnN85DNkWiMqsystijBca5Xsf7iB_bT1`yRdbMcI1Wl9SbfzW5
zo}$cjNY#om{{`)U_-5w$CRRA+r9(GJnt%>RKn@qQbPucGO+af&z{_v&w0;duK@D3Z
zD_m0oAk9GE#G-7^oE%UvLPHNy0E2qKpzuOXJXlo2N(IQ|9jGzsUzC}eR{~x@2@ZN}
zg^H08w1tnR0cIMgS}+O$-Ng%PW?(bH&;VQ;fE7V?IhAJSq(FTU3|@>0UTulX1S2Et
z8la}Rr<Op?@=pV|=)s;b0j=0Ui5AGn0JK2@YhHn31(Y11sRYt!hh<2VBy0$3b)i}W
zNhI*LNkB?cCb&#=PX$%9p!*n-OEUA)!}4=nbD*(>z5)oASkRmWcOK}h2XMQ|1kx?V
z>NF$J;GAb3Xgz2ul8tE23pS1iHKm{p;7nKp7_!O>RHGwS?LtBjw1Nmr2*Qd{(7Ga!
z(@a3CgFv;0320GGMKPLDpwNZ68YPi}CY@2$LL6*pf+h;<nfO9i4?|stmd{}!33VPM
zCZU_8u-b13?j1lDB)fvnd;!}7YOJH$gSMInY9y?Bhu8zN05!#cq6Ow?P>~8MK|rex
zQT+!=8Q8K8bk!l6azwKCb8`WQ1=dC&xb5lY0#Xeb<V7_GtkDF01(AsvB$*N8J5YW=
zH51}T(1Zc(mMU03&IGg|36fYK?SFW81Dq>i!H1eY+*3<1&4={Y(3gLMiVhR>WlJXT
zWlKiTm2a@Lfu#vzXaUN#sH@6BVF$ie#sq!Yk_l+p64*bW^+}+_Zw#{onv-E(LQN4k
zECD5q{M>@ll2pGi*y44Nm#`%YLo-l24_PV1nc($CVc=-RnjXNDqG2GlPT(;y*z!p%
z1MjB6pw*S2*aR({D=kV5DauR-RRCbqO+YKLkjn*V*-1>AF$zF34P=3no;6q~wFGWH
zX#FavMnerbLnLKTPq?R+z*p3RLdyiSG7Q;aCZN**AhBerX$y`e(0OM_^KsZ?36yVP
zCPVECEl5c$Nref5!vSmLn4l^I>4v0O&<I&7tkQ#Ic@xljE)@5I)@6a*Yitb7C3q7P
zC<CGz3$hN9+2PCn!2yH5I?DvKlnP{#323nqBw&b{J^}>{%w&XJA(^?U!QgEJAj_~O
zK10wX5L_KpD=3wvR+PYnafhlIx>Aq{kZ|<{?My&30&8C!6k4c?L54u{LTW_`svJ(Y
z7=fw{KMeICvp_u;@TLb)FqnW=ZJ|c48RWJW&<+Vur3fzqv6M!p;N25YLj#I3^GbYE
zb3OCY^1<#h0j=;tHXBy=L6eIKXyF$mdM&|w3{5~Qmnw=OS3TuJP9X%JTmZRj6}0sx
zzN9EIv!oa-Y6rW}3F<P`oM?pX9@n7Yd~ln`ngP7V1pD4M@UD397P(?WGsJ-kCZP3G
z6~%5jiI8bD?3y8)lR>)-t@I5+D_~$d@bk;_a#M35f}k?U61>v`y0rm*1fB_EVHIR?
z2Q;uuKx?DG?Q!U$R@9mtS`|TCOQ2yOPyq#QU%L8a8oGiu;6s+8Lr?5Mu@F>#5jMdP
zR04vx!h?6RBbfj_;Sv;NprnH2WH1+8X_|m`go8Z*-H;6{%m`a-<eC`(9uxsv4mu4K
zu?^D%x{`{pDTXMfpnCz{Ji`cekQYoq%bvhV6m+Q(s01>BEGfd3ia;A|K#>nkl7w9c
zI@SVwta-3uJglvY)w__vYQhGBj=#WaAlT>dtu&^frB9H|iPh&s#}dx{Hy|5`N?<Oq
zO*z4-CGeR8Q_zYmeC`7$E}~s&4mBH^l!H@C96@(rqgY3jW5KiiaJ^9D91+(=nSvIO
zp|~6DP*64oB`9O)mIR_5ZU!+MG>!y4kpi;16<orC0*NSBn?W=|oeSkaHUW`l5Nr)O
zw1<gepig2kWDJJrn1}6m0_lX-A`rJZ=Hx)#g|c-5cQI!P*9SERb%U2FVl5pcwHup5
zXtLu4vb75mC7|;iKx^}mqaD)%NaLR9h=*8U1eq%dN+ms|nn5(VW`ZwJN3Zyh7L$S2
zY(lF(V@+F>s?QX(P7qtwho+sv%Fh(BbP&?9gVrRbpv8XRy2B7E4$e$C!_d$W$z87C
z;?&s)nnuy<KaibR^A0vca8-e(h^2Z^2STRlK@JCHcSvPz3Od99<PF$5J5oJ>vlax|
zPl_jy3<6gN@VeX-u_O?yM@&JB_dpe)DQM9is3HWN3IO&ZuBr}OID%?B`1xXJC+mQm
zK!RU_yb%V0{er04K+6dsc@e8$NC|xx(2>EQ-J8(lgJKD>uEk!#AyzA5cO}V5onRej
zh_cZWQJ7+hDT37;XlW%%7=xV)OZTRrMUs%@Vh-ujl9TlD*L@`ThG6waf?34XfQFDQ
zIT+0~Y(Wj(fP%XcB*8F3^&nz}CM40Bf{vd6MHDPMBgY`A*&9-2A?ifXqEF;B0<sKb
zCc+L#`AABJgxG<lS|lL~@z;#dIZmWn(FDC#1TDnGRx2WDrL;x_t>uJ-9ejxlIOsr2
zJ3&>6u_06(obPakpgGh@gepTL$jm-QdPUQZvmOL3G{xaukh3B2YzjKN1MGNn$S@8G
z&PJ-aP&<OAMmS0}Os6B60j@C+wVx5Z8wd?mXf<vG%_X4qo1k1`3Oe5dQv1Obg7>))
z3OKwKpAn9}9fl9^)OoNwpWx{N)dxg|H16uo2y(R*0e@nz=@4rf;eiH96sFM7g(MCl
zT}ZH!gWfqsz@Y@|H_#a?C<z*z&tOgl?e_(Ffk;Q=ugHjVF~K^FIAe&bzKDx$!reFG
z%pp{1A=YU^(vK<VPA*V(ge7(|qXklzAnGg7`bgwB2iXIPWtj1%MkEzm5aY2_Rm8;<
z{#pv!0f(G~1?|0=qSsTP)tA`nDHP3=)>MeakdWB`Sl<n_2ohY_8$!jwIS6NtL0ZH>
zpyOtYqqxK{0cULmT9yfkXsFYmg{3hxe!(s^g`fTd-8M^-(~;^LwED^zWe+l<6^_mM
zNT#6GScnChSiN8hT1g4YBBr1fl#m(=t`K{Tg<(J5I?DvtzzjTXL7N<)@kpFKHN<*M
zxZ}VX4i*u(`j+T!#a(Zi;An_rn1FQ<60{zaU_v1}T@b9hP!~GlP8tMjFT|QtXt+XC
zG$ehaFD}BUE71c7fBi+Qs|nU%#2Q3g9Y$=-6RyRGHHlC?hFC)ii4{}OqD)XGHHH?E
zrqFaxas)xD8bnQoSW1bo2{~hdiU$y%=%fp=083p)Y@88Qn<4eN%+TvH&~Z_C>N1d4
zN^3IEx>87PhipTI)nuS0qmY^mDh|#~IKvLU37kMZW{RT-MbnS77DFr`g*ppbi@`1t
z0K3u@w2Tzwc+k#vyity3ExyS&Q|OIX7zGEK8Th8&OcBdYu{z%bHs6V>4nVUDZ*66Y
ztB(k+t%#|s5DQDOx|hi0fV-wLg`AyFAWX6Mun?<BvAYeN6o_;o!CDHsZh?S93D!}F
z1*uRsgB@uKO}(I_8AwR3_-iQQTuiWjBF-4%YA51in{eGkoH>MQCeWf^<dP4xa~>4>
zWX1-hwm?)zh()fDsKzj!w7d>69!q6JTm<2-iok0pz(-DisuXC?${f8m0xgimRvV$}
zrnEu=Evtn@3#8!&tB^oTUO{ELG3??jaMr<@zfn%&BTy-sq3mQpG(oYLfwN))ExE<v
zY*3<tjQ5y=*5iUbfw&L`d&dNe)p#o>=v8PK<r5Zj@K#Wub;5YOVFEqcpR`~{9>PQG
z7@Faz0I<3n*&J}!5HT}l1|Q7CNHyT#$5o+X+Jn2Yf?oJWL>ObQuwXaA!Ba4_?E+gK
zL27~~SZP5o;UdDp1S>AYYGi2mf*orLb-F2NQ8B1dZwgvW4DNZ8p1|=}XT<x3V3kI^
zQN&ei#HS0wRU7eU5vtx0ORga?W(r?&4J|;CBag&90;!A;bsb_2Ho`8DkuWRBC^jHg
zV5#qjk3IA{4}PXR=nQFSC1;^&TWn;2SodfOT0LG-?Bp2i8V^=)g>iqBDQI0eq>O|d
zEeETiK#Q-zg}WhK9Hnpvo9CGcTcQZLS_L}Cj+mA)M=cRiG{MGe&=*pgB9>Rftp<m`
zIi&3YseRB3Z4{$%E*UXLIrzd9MH9~25VX1*M-V~W1>M$Y3R-6kDlbey>#LC)7od~7
zL02<iSy+hT1W*?Uv;-8sQWYA&ko|v{p^G{YiDDFJ)DE{%(8Y9+C0cj_1+7$eOo3cd
zXo^_qjVFpsVXME*Aaw)2C^L3UNpZ~#@`fyRbxcVygt*NDby^eIizXmd2tA&8$)F`x
z0Y#wA!;n3{pp=BX!~<Ekp$V!6SGdifeWwI12W=_>DMZ*0I@&U^ES0dkK?hEQv>Kx5
zb<Hg(sf1XICq6)W!78E74Tf%-GzG1%$CE~(c?MF?SwQAQ@kU2*YKdzmJU-mv*1(Sp
zg2X#0iotmRbSNmOxG)7R83*M7&@~z$8DyJbHp13<qvSth(Ehqi_{KKS(IAlE04*{{
z_L`{$^cpj0vNE;6RxV(*1GF|DWH2c7Kr8ZLr(BqV)~zGk0dY0lWN3p6tC=RCR0mqK
z=%i=unpaX(3F1Rs4qA4O$5e1^;0+T?xPed?gV(JlmZU;QJ&OJFQk_6opg`=yQ`#Dt
z;L-|mI%xY1`r?06#Cmo}T*GPx3rKLAg4VTznn|EV|KOO$7fOcMSM$Rbc|y<SHbtE5
z39-`@wDugmB!w=~hQ|)!aLlZLh99)9K{U1T1R3tOGd@kQTFsgPddCBfOIjdRHfVuO
zv6%%@l?^(t6Ix~CGQi3@IUjaki79A(dquHtepxE$5>)UhVW3-U?5uQ*EG%@4OwBN>
ze9%gGaI!Rlm&>5|1K&Xds-aNA6WZQ3GQs5<&rIA~6ih8qYAF1M!0KOknFd;@kH?*6
zpat-t1ZW0YX$~rL%s@-G;WzMrH`~JsC|EWl#yg<nTp(LS&7q!lg;h+jg3t`U92pvh
zFni2E%ZNb*p&4l5Eyz)zYk@$)3vw=qhPeb*nBWZ*aG?mfmjLDz@Gh9pV(2x#X7Gi#
zP={DRX4paQ0NF;CttKGj(Lw-hE$E!Q#F9jCX=4Uj+KVT8AVnz5epvXxd{0a~fEphz
zi6x1kdeSdA9$RF9q@k{XouCE{2WV*p3kR5MpiMPUN&yECXw@tz4MF5#t|KaLK$Qu|
ze#p_c2-iWXFqEJIt-U1}RFno0qyYmu1Q8xZpryW8>_rO{Xi$;sd9Z&$mz=^^?Lwmf
z<{|P67DNz(_e`U-T1hBcKnV#{D}yQlY*n2Zd?hc`<<Jm;g%hZ%1Em&{LI_8V2ifKT
zN;i;n3txtdYBfB}pp_~y`32S?2N@2n)j|6i6LTT0VKdN@UM#5rW)*b9In0y9*a!+(
zzr@^B__<!_^{bg7N(T^cS%<T}CCLy({R=ru3VN3wXnA9?xmi(uKJ>;lGtddG;DQ>o
zqPC*gy(kfM;TY(kDIM_5SLqM|$Q4!K!!?Y-_n9T-SHQ0PG6Su>ML$y;VUQMR{8`5m
za#s`h=D5tf^rZX>h>>QXx*vK8n3awZ=>9JY6O78*475@e5^0cv9hx+ap-b;z2ROiK
z?1z4sK?1`W+QEb$B>_Ke54~>*vKOiA1k(@gRbtbRI)-TmU;YX`_Y2M4XsHZpKJFxd
zy1W&p92VJ-Oa;kMSXIJ?(ICSVpo8V0u?yNlYX(}9i!Y4vhAHHnI*4)5bLxUqOCYDP
znSqiWK9gaGK;pH*1Y{nxItEWq7^grjaz|JM>zF}<8=PQ4g)(SiEI65iR=`5qBanmy
zOEIvRfYo`>uqEo0IWy2|T4etrTMH^NVV1yX9F~E~d0Yq0fzB)_MLllR48B$t>RYtL
z1+xuC!)%2XX;2>;8agA)MGIco5qF>iZ9VhyQj46xxgMNa;EQ3gTW$tgcnS`2(5lCZ
zV#gG>qWruPV{miS47BhOB59&&n}(~k4RfIhs{O73e!=k;Fauo!+%h3eLs+7K3=@G*
zdv?o7Oo#0L2jxV}oNNfWXbh|jT33XFu0ae+1zpVwKhX(?W>Yk^P!m8WTYv%!>>jA?
zpcRbBDFiL5%s`6|K@Bi7&}u{UvJ`BPG32gLP*|Z>hVW2AwHJJJ6Jp*AURHtU%^*SK
zo?7BxT9R4>HXq?oEWrfI21rVYFxxpluPn79GYvABgFOH(;ffF;=ZBoc;Eh>GUWK$y
zVP!t{<ZKB#FA!`7!c6dKb>P!~A;%}0fmTE!$A%eb6(TtJLCXz6#RI5X0_9vYBS<$N
zW*J)XU<e8rB;!GLg3j1LA4M<&tzJa75gPw6<3UFVfULu14fbIJ(BV0mc^*Nj$&fN1
zz6ueVB?%QnW}sz(;A{w5X9xB++Aue|cMT9$Vipwvpo`-S4G=ZItF-}Yv4wCcY-$hc
zQfP3%q83(S!Dvt`1vz-oN-z_o5-cb+8H>w6r~9Ki416kiUJ9aAL%0og0ugq%fyyqB
zTL`$x9JR~>ISaF(0u^Lv0fx<8kfYkn;EUmKI1D-m1S)yK4g{?<11D<GdNA}d1$^r&
zxMc=DKnRkxpd|M4#R9`eM&Obd#RDK8A&O?C1F%5XqJZs$EKvfLAg1x4aedHLD3Gna
z!Ju=zV3iC!IzWrou;d(AQXvoxprcGs;uzD(;BCc-+5*{PEF~f2I!JJZ0gh9wDGOe%
z5iraMSs@OW!8SZ2?g)mCnW3+eHv_GsLmnhV=%8@@h8bw>9V8AQ%P^tY)eN-s4OD)>
zj|IjyW(KW;AV&b>+{O&D11Y#sHNnQjp#3WN#F`m=EgRxoVsO$oMXoSlht9zYS7_;h
zY6WN|2IS~Jj6ML?rAbilfEHfBw1J!cNN$6daTtLHa-#`!jVu9I1$pB-Im8U>Dj~SL
zkTrr`1|RJ;gRfY_5o)OUA0?b{cTG%D=M=#Qi+Dm@gLUbI8GNl44p$INOQ3Ey(s%~O
zSr{N2a3v{1izCe7OTA#O13L$l;z5yT23j`-E?YrMn?N-utggiuoru*CkR*fLUo%A+
zB1Q>wSg#E-vIWruo%w*&`*`#cwb}ucAaDj0p<TWBG!t0QU<O+Mg<P(ffmU3Bf)!Sv
zAcr?7-0_7jcy$3dU_l)|(5ftSqd_LXOe7}zflY*EKgcN*1Ops>fdV)mz)xC2Jzx)1
z{Xz?K^kw2^pha8Ag*oi5GE5UFE!^SDtPo9Ia6o~UHbDw^XcHKm1F**cs1`&&M-tr8
zz+K+sTor@e9Gq1Ed=(R>^FiK#G-=F0i<v;40UeqN7RFk};q?a69wyWUZ)RrL@-A+(
zKy?NzRbkX<pv6{@bPEl2$mEon8DtfX8E6R*D2JGVmi~YX9)!P9R;GXx9{3JA>;VcI
z14Ha*Li3TifhJ~wfX_#`m+_gw7ZM>H3W@=kk4Q{VxGNxY96O{i4mbrTOzd?MXr&OI
z(1#=@5?xHNnli_+s2{t-3D#WjrA)B!2IpH?aKnxqCD9XzstcBKA@vZXJS5Er1nV=>
zj3cg!BQ15n<`8h6WdTanB;*i66(MMu8FIQa11<gnWpiU_BvKfC$Tb<Lc7(6xf*B7z
z$N**ysF(p|QW8@M#2U;6erBY_DE2xObZjDcE&*C)qOWB)GY8!+h;+OmTmz*wBxpe!
zBz7Q$Fs%CnTHgk$RE%N!slho7dzb|zgNq&9^`Ql}A{V9$XYB`Hfd+E|w7Rf>7F7@z
z!OqygTIs>;0QG=inH!@Igth<;Qn|o%;;qm?tKV=09w>#GftGB6Qo0%F1W!nX1`lN{
z6&lP=yww-<B5Bl)9!w`_@&-}yp>>Tw3%KyO7H{0*u9_^c?evDa2YW>XT0e%*9guj%
z7oP;H9Sdw7HmIWrRx<F_Xs~n#c90n~*_m0u78rn5lz}1@?;ZoFrTFU@e9k3Uqu|p_
zT>XJB3JKR1_;eGh8{o_5ph0a0T9F3IF|c9>In+T}3U4BX6w&Zy-e#cXYsd)=WID(Y
zgrU%(6ntq7Vknk!A78kmm-6U`j6;igOVE}jqukWu49JABv5A=`WNI2TQB#tcmYJGT
zke``XQVfwd11+8dmC9z2MRU*vL-8Jt!5-k1uh4a2#zs2EM%KBB6<}lXiXgfTVW-_<
zG|WLO(jWl^KL8OFPR2%%C3;|IVNT8%nxc6NH23Tm9B%?T-WD<ogHhI6qLdbxCK!SS
zPf<-kE5$&o;qbT>ErDRzfV&{GL@6^cHDa5ICZGqFcOi4qu%*>zh}C)6gB!XR1%AyB
zxcnjHG4S$gP&dW^Jmm%69p@Ts2)g46a=bO9Ig40v2elh)xH)K{9VlU$z;2~PcLOww
zz$Q&Ff(N{e4U6^QMP;BX#Vd1jQ%j06q0>2*kfVxl8V5e*I~b-1d|M5gzhDN#(qL)|
z*x!(;K6B7|LF80o4q7(|Dp)`{2RUsM3RXkVq#L>&ps6rNJ!{Y!I&i3(Bi0H+LKV71
z51xT=geJ5S#|TWw>O8EW3AGj;G;sToTI?9+85)C}3fF@@I3cOTH?aU}B<$d5=xqeh
zh9M}WfWi;b1~UgOfdmDsIcSk0Y#s*7UI?5K4Z2&xH?aV+!VWq#m<ir;0=d%1J+&kt
zF|!C3t?>CPNI2tglm+B?baT*|o-jwj2AQB`CTM&L><>Z-8+6YC*aav~L|&{2Zv4Vs
z>zh~sz0b=WwDJ%+5yHX@(vF3tJe)xTvKed|D7k^wvV$)<1gCPi4X_J9Q6mYK?jRPz
zEW(xA3_+`5eG?(q82c4Lm(W==fTy&;*Xcm2E6}QkVpF4%qSRDKg<=j`d<ZHv%t7k{
zD~cUmK&s8GAXQjWenmlUW*$VTG3c^BV<R0yOCxKrL?U#J6j%bZGzuhxUNM=&SM3?Y
zy#k6fV`E4V!sbw6G@4H>;XVM>OrRND_(}oT>JRh^3S=WvPC_yP)U*c0Iyn4|Q(O@y
zpjJ@k@a2Bk-Hg+XIBO=5iCEnPZ(e)m6{i+K&UH42F9F1EJuIkTG(239`y&W%n?a1m
zoOZ@FU~djubO%oK*zG4^ANB?-c=ZS3P9jJF1zOaH#UgXiial_m1g*XUwa<;AE`fO&
zMw^(SItG>z5xzGB-E@L{8xm$XAWZ`!--86o2oPt&SNB0(VGde02QD%}YtlgO1LYo2
zss_0nM8i@Drh804#zIR2YX(p=3x16VG>d``hc-4eLvB)<Lz|RIpsTV{Q_^8e8x1ve
zAnio3L|P)~x?6n%P%{tKP6R17Dg&h~u(&yBsT=x61hit+&I+^|60|1)WFU%WQ%zg^
znoaDibc{`nz&jd1Rzn-T=AiX$=sGj=N))0EjbiPrbj%DOn;<|I!nK%VS_al)2GU_+
zW&*aPxU?t@#UcwFx=cX23=NG8Alg#Xk#vE!XXqFjo0~!8N{iBv90HLvGlqByWS>!4
zeoiX%RCjo&>KIx;cXELA8iCZM=0FNgun9(nCJ+~cBEtwpyD4bPq>+gMctI;z8lpQD
zyub@=15^nlvLQ-R(=nvYOhA2xlFZ!HV(<klV6BFlIz}egFT*g0uh4^DgafUMjg29v
zwi}yZ9qKSNLQb}@av4vT0b~zS>4&BtS}LRHM=Ouvi}|p-6PDXxwIhoAaF@xb=L4d;
z3SKS;r<OqOn*--M(9%M4P$dc}wcuOyKzSY(YN#a=RyTpGYlN}T>KbMhXbuh9STaUA
zCIZ!o2t&XcpoVxP7K3h6h3PkfUR4bXI7n9kw!YEC9Nq>4*B!9p3e_SDn3>RG%Oeqd
zs~f@!P~76y3UOT-xUmnp4+&DL;tPH=h(eHskT$I!s;6+<2x{v?6oWKFu0QevI{|7d
zWUDH6=YlnZ6(eazxr56bba*9XMJUJ(*kc!Ty%GHCE^tfT2zp?Cd`M(~E2v*>55Axa
z*;6R9ET|Cxb{1GIKEuH7hI*2aVPLg*3`6s?DP$F_IcOOus9rWkJ?IJ5wT7U3p<o8!
zu><TOsC&&}7FcN7f-EoxErHZ9&@l(Cd`v6?u{5m>tPQQfbsNr*2Q|0B#^SLS>~)B>
zpw*P1xHJbXe*{|#TA~QD47BnPVXZL{)`E@2V=e4}XxL51=Abo=D5VHIYk~?B)ItP%
zY6Y+EL^2mt0zeWxk|6rEo*;Wc;fLKwBT#DzyKbzeL&Fialn7SJK$`aEFfQ0<X3)w7
zM#Bni><+>7TTmu=^dI6u==u_HTtnRe>sG=n#pVWF%|)~X3<?Bf8&JB9$W27hW@t!5
z037{rm*5UyaB~!DHdqf4W`lf*6phew*90=wg+2I;Kz(wkHR#%juo2`k3T#BzPK1ph
zN5VV?TUHH=NpSK(O-x26kYP<2O&~F0MrZ(*)C6)i!VM(FA&PMzb718ku}K1153b+>
z7fhCD6*{(LPC}@l+8Y4gB?zjTK=Fa*2gtB5_Vflx)+y<!Fg>oBDESp+4ai7X@Zw9m
z;BiB^23&ywawJGmNM!+J<D5BY88LE!Y7SbOTak)f^MZGGpr#}ODF;-S!K^_l0U_5W
zBV{mRJZ1!H7eFsiM%IeYi>S&$E`g>f$c{biu>rmT7*i)c_mi00jX`6@s5XG27ugij
ze1)Ww2oD-#h9oFxkPN}+S6Ey@mPO$RLQES#9xQgvM9J#VATSEc&neA?o%aCs0KWWU
z1lpK^RWp{11G{t^nr5KJ<4ph{*MmX?O)DZ)K>0Ma0%{^GK=9^D(C#l(UASTxR8D~w
zdn0E?&=C=!b`43H51W<XZiIVk3B)PTASA&b;E>0vnSgh2*9Z8F1UuK70rg^5=x$fg
zrmJE@BLi$BJtg@Ckii4ct+miWp3Gv8%=8TZqLftVY#?Zg0@CNtE6Xe{P0T4SO)5@J
zhK`^ZYJ$dbjE$`GK*L|@Mc}L5!9$-21%^fzpdka$SV%s?Tw~Dn;)Z4hI)+9T)}Y}G
zbUDzW6(G4H#6C1|q#Hw9@1TYE6~#!q)O1V?!PjwuEK1H#O-m~V_oPAV<IxBA!%~xN
zjP1Z<`z9t9a3`SZ(lIrMY}iE@n3<Q7S^+W@?0Rzp6Ev-;@)n?hY$FR$Edt8d&LB5I
zG8X)dSLm&~=Ah;16~z!|fh!PWW9XbSxM70YQipX2O;BSTT&KY;a?OP7D1lySjcPe4
zo#8VDl&!%!V5T@H<|IR#>>)*odBth@MY-U(hWZEIoI+XW5Ay_kVK~?`=Ad=u;GPg@
z1$jj=IJwvu+kyRU4q8kOmNdmaE`;W3BU7ACb4|$vZN)JKoA2q9Y3%9>-d|!ImYNLZ
zLiW9Y{06CPvH1<YKpPrY&@KkdZ|0!&+AvQTK|BG8E|5RL#Sv&7HrPv$1<k&RB{s%7
zFs0CWB^<E<^&U3Yf%<`PzZto@x`KRb7!UOs$mhnsi6vmJPbNqn?pbI_hSjt1UKZAf
zG&h4Z1<^fd4$YIGg~%1f5D!8l6xQ~^>p`q8Lh&Fd@-hR0jpETf1`-E*3}a3RwA>j|
zJ7Dz+u5m}S^hM0bqZwq98ix;^i5Po?uXx6u#t4KV?$JloHOz)+euIxcdLkA2pwi78
zw89!W>_C+OsPF|XxCW<W(1K>roCNIJaF`3>4IMPA3=NU&fR1~C_Oiq48i=(h>;KT5
z3to@~ubn`KK!@$Z!1Yovv|fUk1e=wH1t_HCf!-8K(6yjOfgg&oAnPE*0-$keNT~;3
zWel|hmZZ#yv<74g$TX<g;IUfNA{}ZEflLWKIslR>VSdAr8VxP6SOYE=4Gj==DX0wx
z30zQp0%;9E!WBLO0?kyY;R@!#T!1qzfEtc)b3s-?0v1&1pjrj0rtq2q*8(-g6Fe>r
z2}5Yup|v;B)1?uz7Pu)Ob4fG>p#^RVYQvEP^Nfr<i?L~jn+z>4NifpT)Uy~x7t}1A
zwI?+ANUGRDtB=7`mauAqsLBm8$%I-Pxu#@-XH1dnZqUI$ph5_~6d9WB&?;oK#s+AC
zGT2SjXitFLgxq{Ea?}I6$OtxI0?ieWS`yYoKywjvKExclP!yJ3aAa6$3xL=_!s0?}
za2#W6LO|jezB(Cuc$&jjCxco#p!N);oH2)bgE;47vmLyI548mo<QEKY!N8*%cAg1t
z=OMWYw0IdD-DdEp0M9z%4_)G&7mwbkf+Yd;f)O<dfC3T8mC&#=haR9vvoHjw2DDZn
z=muO+ihz~|sQC_>BJk&Wv{oS0Gemg;tEHF?y&&k~ba3W^k4C~{6cm!M%!TF{&|+&y
z{6hwBsp=}fVC3irH$9=z4_}~+Bay-iL~sQO%R1(kkVHaNHwGKUqs2PRoz~V2j(M5I
z`6Wg91)0gtIi<y*r82N_0?<|xW2_58^2#8i1n`9*d8sLI5$MoBS|VHovJ4;(y7tRZ
z6Vycp_5YJ07RDE)7C^@az@2E=5CBH|9kj$7(guZh1R?Pb*%SzCY@r99B^Eyt(}=f(
z%-O=q39QBt(UOO+B}Z&+M|L$auEpJ)w?x_AXNb*>@HRbUavPGLiO`2J84ek~3Q8>~
zN-a*!D@n|Qt{Vd_Ek}tKNRkFEGzawzOrTu_NF@RDF1!(k&B@@AWK8Rz4HVGyS4w&+
zR)?Up53v~yUX=@KY9eb#7z}E&A<IBR3o@~d*Ueyq(X}HCh8A<=nGGJo$6^B1cxwi~
zbl=jF)CyS61)cS72s<JJZJib3NO;inbFrZr;(S?iOEb_iCins4@D19qm9K_&Rut|U
zGlwt2H-Rknfi~65K`Yxqsl^y34o*a{)B??e&{e*m#gtGdxq=t`;@m$5vJR9a2<gYQ
zlgu2x=pE)PNcjwME+`X2(zFF^4Lt1RP!gPtbN@8RY9gJEWCnNz6a2Id3;1Go?9R7<
zt!=jeEkg(O-a!kW!5%|xr{f8D&?#dE@t(nk@vzm*j(XPUi(^3!A<i>6j05`%epZJC
z=pbEik%Qe|M20`kVijaQ5zd8|tjK4NT7VX|<8vD+37gP%HIQXQI21fi0zTs#>0}Vc
zf`ZgM$dUaPpf&g?ZU#FNmdau0^b#4Ycz3K3>0;Ra7hH>li8KcLqF~4-ZO|@eaPdxL
zY!lqiMx;3ecD7l-k4}cBZ3_eFrUY2gNM^J^wxhxKxLJU<I3UM4$R1GEhZ%1H+JpdV
zI9q^DC<YY{Fb5Es4j>j{?tddP?$CF_fp^z}wyA+8X&{T1LF=YaYB11zAB8oP1!!vm
zBwSF|Sz3U0DnM!|s5m%}U=It(Y8{-_637nBYzx(dt1hyDZ)pI{lYv&GLFyuq!$9!{
zsf*ATVW8H0P%A*2njtw0qo6i~G<C3Q1MO&rX#*F?h>8ZjbpgBEEI@k;AZf{5(-xAJ
zprMGhqJg^;`Mf67(>N?pPY;IHP*9DaW;uA>7Pu|~yA@G+z?c7HcPqXa#a&|<LZ<bx
zg&X#20<_rxM>0diCO)?itQ!nbCLvG)h+u^P+5v&$I&fl#B{kUL-T0h`s0AQN22$uk
z3S2xcBUtd`(Mw!uk0+)G7x8#B6Drr?+c}`g4z?<glt2X+so?O`Fwn68?FT`QBy<x&
zxelC`@Z>SDiLk7KbefX|o^VDlnIS{6;E8ZZxeQu{QEZY49{>PvF+lImTYz@4fSnKB
z;|T2vfUXV%xf*oU8i+<qWXSi}fqU`62JxVQLCiL%5o+HUX%{GX0uG@MTs|PmGSFrf
zJnq8p9>|7WjAGRYrC3Gr18jFNX86E{IkATiq`b%P8gNp<5;BlgJkY`l%{34`u*+9$
zAvqSblLk3VEI=nlg9>!mR$~%k4U*&_u?9}Yu(3s`KS5@LA|7TWL60T|gF3>Fde$&U
zD;SzUoedi)gc=Po$^x{p15}lRF7X4|1PerX2OlY|B$s7EXX7Ay$>0WqDsXHXKx<$<
z^GcwaU{2E3hG>NE;()pcJ^UapggFRH3>6fhc~(~!ZZgP8ShVEkLbX^kfU~M`3Ur_V
zv=FA)$fUwb-w+fL#zva9h~?)-pylV_i3$Vo=1Yw93EIv9NfRi;uoj@b3ZT#hZ_tFa
zQ;-MTVLK_EjX*&JuAtFsc4L%`3s;XbqkvX4;cy5jcENtoFwjA#(E=H6kCAIi3i!l4
z%#34<l5yZV43Tu86(*p)3wYdy;}lM~$wsaLxSRr6=m(8%xCTRn26UIe<|9BGY9Y}A
z3LQ{%foRy?M%<wi<c%mY(PIoUr;6zqWIgEafoFS+<O6b#1!yk@sD!ZqZN>mK8R6sl
zsMQ(V>!8DLKnLoA(>QpQ3wX~9meB}fl*Ss|08pzDs{yb~3@r>n8#s{T)C77v29ZID
zT>im}EL)h_u!%!hEP)$dpe-<<KtYcrSos6<LTaHaG{l|stRo=h0;rRO>{<)ZE)!6|
z!tw*m**Id}FanxSBjM&Dl`}A#4I`ndqVTGUf~tbK4PM1T(g4g!F#p1wgzis>o1FBl
zVQzxtOAKXTM>*+P!yJV}8Q4`wAqHDr0gVGth=D>M9#G&ofw>JW(HllU3R_TtYJ!&K
zFuaFi5Qgi)CPB9EhvfTa=0QUiE#JV>1k80XJ`VR;1gDmOO@pNrM?GtZP$J|UcncG(
zxdW;%u>wgSEMI8D5-+^Eg+wQM#;^b_mIoz03(z8YP@c8`H5I|j9F0@JtNn1ey1)mr
zf!Z`IHQ6`?w3Q7}+v_51$C5B}Ju!8{&Qq~~AK?s*X><p}A`IkkPzu1|I`9c9DWIXt
zcw`4Urldd)DYpQvOhwLvgzO>GUeGO<My@Cpr@&4ZfW?i1kpU##fGT@rH^7QhLU}E@
z482SQ<zXj1YsfxwaA^x`Pk@dkGDezF05t)ua}q0IONT*of0p1QhKe)u(o1s^k(LyL
zj&d+IvMvGbtN?EzHa0WU1Z|B5O%-A^KH%$RP2qb<K@ntP2}yZq5oHNlR0Vb^?p6ot
z!O=)MaJD-@OLQS2iq#G9j0n!<VBbKhUF6mfl0DG+3s$1KW`eG04e<B$3xRFl#8Q=^
z9x#n$8niOQYg%w>2{=%o2?SoS;P(z{^^33)R6>A_gJd|k&EVddduoXfI30i%jah)!
z`=W##IEXAjtAD{QM>EvHAy~<XWEadlXj=3v4u&Ntumzws!^jpu=O95gfRYI;d0-lB
zXn-&ZYBqRuADUVrhQsS<tN{hJ3uXtjen7b0&=AQ)*CcT8!1ohbfEEuUyBwBo2s_;b
zWC+5|ARZ)QL930Cjf8fzVR;2+15O8nMw&pTfmUT$q9sUc2DpG#38;|`IygDi%`?K)
z1uS7@sN?G5?h0WW=>&%cxj8yRxyCx4etxb&5IGYaYX<0ga_|XM&>{tN_AKF{1kf^S
zun*0lYviFrqaeqC6BJb30(5*cxB>#LO9oXy#!wkF&w~cYz$YDm-GYCd!3@%>fW|YL
zPEa;PG@H?y!0-jh2uFZYrv<3s!z?P$Ovc%LLR)-ngdD2QI14lQf@2(R0q0Ocp#&K_
zz!?2NTXc->B8V<ng$1q6;R}wT?y&%^GzLW-EXH7=1d3BosNr=Ra+@2}G6StvMousw
z(?N#748`6JM{zjrQ3p_(MK&B_m<4EYFeouvn4vC2f)zrj@rxLDfCUU}2m$rbUQ}H;
zM+fj}LK+`{rWIJ`11$kEG_fc!1eJT>p$hO&UV#yu4>=nIv>zs~2qXwUDiBn`gM{OA
zQqxL6G8UlYd_jBaK@4jH8~|0q*c=Y5LEDN9t>MNNWu|Aq&DF6q*D<s%PEF6uOUs8@
zrekTLV`N>N3F3nq9*|8(DfytIr9in$QwOxt$k@m_B|kU66l@kq8Kiwul9LZQ4h5<g
z+BQi~1!;!aV2(Um0zM<a0(62fbmxbAYJRRK$TiR=jDa=Cyu2cCP#S82w-Ol}K?T9#
z2?{y`$i}DwRGTa@Z2~QMtpKlW23NTV+ssT1bWBVvtP658Q=qOlG_U|03)=COlAm0b
zm;;jl9ZX|vWSvnEpPB=YKSM)Pa1#xq%>`OG3rW`~>#t0qmq8dqPXI*Ai005Vg})HB
zKsj;*t_N@N2|wl+dQLO6UW2&@w>xkQ(xM*ci{u7~dRRV$-bZZ#UnL8*9-Kcwg$rnT
zEGVNJgHG6iq%;r-Z8yQPJIs98{nKEbu>LUANcd1FG>9Q32@a1z3_=<Pg_fkC1+d7`
zW^4he9bw@_Al{IRPHSrh)K%!<>JeI6fQ~9ATvfx*@U?)q)WHQW=<Hl@ph3kgV5j9;
zfEI9qt7@nWn)eMYu{eo9VnZ3O!fpWGlm}njiEt9k6Cj#M7ebO1Mr~<{k`-{d4yGBc
zNdaFYiNj@Bt8pA52&tDb-G{a=5?k=YCb4082AUM%YayYovjD9f1Xt=X0Sido2#S4T
zLlQZGf-(_k;UPG^!_2gVGzu-CW@F7nSc4gN6%Se|hinJLObgKJHc%o4rCDU>!}2^<
zTX5I(mZ&XDY|(_X!Y4v6QoRpdTxbNF835(RVj}~Kg1pjP$TXe>XoVhPcEB7wI{=!*
zfE;ZGudu*fcMH(EIW)zf$`M=_8X8#`SVQ*3f)#@*T~lK-9YZrv=O1Dl=oAGVLsL^D
z9W(1Z=$>Ryy8$F-p<`m5l$ZiL=M2={GBh<a(lN9yN=-}4Nlb?r0TnmaF|<xe%}FhR
zEm4Mwo9Gx?BW*B-8g6ExV`hz6PntnaNkgkAL8r4B8)00_YYAEo2uU1}#0;yKj3M1c
zW9)1EE#Yy1x9|d)jx@}Lqzhk(WeHyi2+IV}Isob>Ncu%9w~)*>f(&Y6%!q)@#pN1U
z@qt=GS%TIa;t3sa%p+Bo&~6oqTR`XAVw`P-IXD8c3YX(>8iDRwW9UX6yx|5ZRUss_
zn#bnu%uHCApw6FI8lcR0A^FwN6`=>+Wv~s6M7xX__u+CN%KSCD10kB+Q%gXj1mH0T
zOZX~CsOw<SY6)6u2r43tE#Qqv@OT)kh(wqT+6)OY5X+DT&X`3WsxZWoL_qhT20&d1
z8=Qx_5RwHz?!#7cBb)^~)yFRY>L`$TVQ}+6i4D^qU=3kV4X_Fk+K99SE$&0k4e%Bp
zaTycV=F>3Hv4k(^gL(+IfCBC%l=#PVC&Vn!MXi3Nxlli$Oa&mqz$C!06gsI4wHdqD
z;p(8K`sPCxoPxX!(t(jl%s~eG=BGf48@y&CD+O;Ch4i-qG9gX}tzJY9SWD0<Mo?1&
zTn}j&=$H~0!iEO0zEwab!d37^g3z$G1g#DPnQjSMr3Y%Q!mJ@S^wI4>@e(}T3{mz&
zBO=7m7~x8k;4_3Y)-Yomiw?+C6QoHG>LFUfSM@=CiB^$8atzK&12i5CHVhJMphGXf
zTuA&FK-%D#4g;O02UZ8oF`)aH!H0x^orG5#c&{*2TX1R#m<zEDb+I5K+(2#wD?_-^
zDIYQqW(iueh@66;zK2DqCFs0jNR(SZ)-+mz*7SkKKn)>#8!SPq=s+r9_a?)F3e(e|
zMI%o6pt&{BWD9tyE9jOo#Go-`qXT4vB=~S@$g$y`d7xuiA%27}FvAgBxGE+yh#{bG
zbJVkja3J$mmPROD5k#<=Lsdcb_<_#;$jPiq^()OyN-fIFONYA%Yc4mmz|?`$C<0C~
zw8S(EHZ<y#m;!B7LEQrzxQB)is3{08V;~#Ia7T}!0m3MdL&2G|#Lp8^m4N(-JzD)d
zktz%HGjX8q!rsV$r8G!BvjnY61EnTQBk+O9;Bg-#$gC-Bh8m9#K&N@4+YUK<2r^M)
zl;Rqc0zNkgRF7kx=wb<90tO8{wCn(Lq&egSeM=))rU0Gxi+<V;)a{^^+@Q+V60{%-
z>^0ExsET5j)V$(U-^7xl%nFoAVaxzF1UC{;T<Z$C_#V366|`3fY8&E?<#<CwBrCu<
z36u<s(UJ#tA3{%%2e}#Kc056j)oq}LCyMRhjjfOb6`YX>Jyy;VzPJi|Xj_67SwTX~
z6k7N|hlngeD~=#C;G?X=Qo$=5@jDe%=7KFpp1K322*2QXGl&V`Py&_VE?|X_gli0&
zGQ{mlki~?<3Y0n_24isnW^94n0ghE@A^}$opanzt%4_INL4+LxJ_QuXc!-loOi<R&
z`MCw9C8_9n&Ly!V5fXTybxnBOi#uT%nd31VT0DXFb9*Hw=O<+*<~b#%<fRrvj$5;Y
zFJ6L18EmNmtgx|!1`}@U4K1-4P9Tgx1q&#Pf%Y6C`x(P_=ymPr3ofwad}3UIS$~4=
za)7VVaMA;xgn$-KE}2CxsYRJ(&{#q%n=xDr3omf%+7h<7$`W+YE_h)J(M7b8IhJsR
zIfg*^Le9nrOHBr4T1YXDwBR0RJq}xK4;f7KEQaWTw$Y7nH61PC3ZaQDIJE?EQVi%8
zcxceU)=@!|8nirxHN2s<FD#*9^aTt-ojFu<VYB6_#aJ!G>0(2R5DYC)w?mH=hq@SV
zpAdcE1~XBbLrsINkpi!hfi^+FZCdD1IcRu{;83}x39NLrgbW-(!_U&h5^}hfF;v_V
zwyeq$wyX*!gBG@iM#yf#Kg@)>3Jgsr-k~PYk}hx_hdK|**`UgRkeeWB5q;Fz(iD;!
zp{WGJJuqEpgCy_;UO3zW&a{MF1R37IbP>v+4Tg&#x?uf%$N;z{d?6UrJ(i$VS)ke)
z7Gt2u1%(nQPC+zYx1o)ETf!GbA(;*`1ZF6tjYh~wRENXX(p$n;J3$SH7-k8&upTs?
z14?@!yI`inG6!n7;2i4)xgFGo#u~r42D$NS!W`X(WIhwn0A#VbS$tVRNs$UPLx36(
zhOjI25QE;9poL0^aco2IEH8MT4>a#>VS+Yb4LWWTF<xz`sbgYfq+^Oa)@=z|;^a_l
zXl4zX2?z14b&ZY8%`B`zq`9?@3Fw4KGmMdG(3zBGpiysdMGhV|w*;+m0-0i{X>DR{
zV69_f0hydnEGo$?$;{7#bjBcq;UJrIObv}dgY2Loa_~WzmY{<SD~iGVlA_Y&5-S~3
z6Yymor~}=gQ!lZNbHkP@fkG4-^&rX=dit1&0c4CBmLi~0YYq)^{KbzMO6G#=!CUaa
z);ZyD4{mqB3J<hVSu>PDPb4?M)T0$PuvJL7?8jEvz)i(D!j8JU2(BJB!-hUfZwXrS
z1S)=UxB@m1i#s$xJrpcMrx@iQ>Rt6n4#QypT2TmEiiPY>69dRJ9g)cgx$FZiVD^Ld
z#jyvMX>e+ZUjTHFCm4C48oMt*O%L$cGY-Fi#(iNHLThPwc!Q(a6127pR4$qrKnhA&
zc*Dv?Sa^X)hml4I{Gh|hW{{y*tjb}7$#6eoS8IekObzl4q>%$#=>>BzEJc8v49dT_
zTng^aRu-f}8&0t3gpFjw7Hq+cg^y%Iiv!ePKr0L&W6$8T(=0&?y^s?Id>q>h+6)2*
zFfLa@OoEO#Lp_W#iUkXJ_}DbuR#5$e9^P<OP!r)JvF2C}MIMyKYc8@vjB!`&X&5wC
z32sC}lQDQd8tC+4P~~NbBfWrn_7F{wt8h?9Q}JkmjEX`up^l~Eb{#|yr2XKYS^^(Q
zg{`)OB@I}M5E`MNkyKE^1C5u0Xk1ZC?jR}Tm~?1bfX<r1oC5F8p}7U88z6?EcgHX~
znxGD^bwE)j^pGS=(BePjM2!{%CWtN)%4`@cv6_O7at#X3w>EOpG5}4!gM45O8VEtU
zX9!m+01dA|ltD8u^bBZdm_Yk!AQPeC52E1Z0XV-ITcT!Q^o#;39YDt)LB<pjL&flM
zBakF?EY}>ecMaVLP+A46!fpm+%o*Zj>{T=BNH%C3GqnUd>|qY=+2Qb`i6MM764vks
z`7R{2q!=;)4;h~iHh>>_0~x*pDe%cpPRxP0(HwP#0~VFwL%HEf@fzrtpORXPY$AG9
z18HEvBFfAd`#3h-X3+Hs2s4qbh7Xe(r$C2E!3h{t&B5K^ms$?>820MM5R^X=#`~p~
z6K4Z-{ZL3zW@27?PAV+IpnHjMM3^bsC2{C!349zEy2)tnK}w{svx^`R2_M+UQ9gn_
zN_bcwv_=z8o`>Wo_*Nk3CPG{V57<NKgNcxg0vW(JCNzK#TfT`SL?O<Bm&MRwZk)~m
z=Lq}*?-+$5=omuG!CcU|AZ#foez!s0glhyBp3bql4b+k&V&EONsuPDxV4HeC!|b3!
z5?A<u$};dkIi#jQL=lz|b+9^k?gFhI#gl44`(i;BTY{Ebf<_K3LF+p!ib1EgK}PDa
zPMN^N26VLy%tDCUOu<9l&<(TTC^A481~0xLqw|mg0JescU>HHlCtP6!s+AzdLL7n;
zNSNsaRM$b2Bf<)QfrtnzOX#c=Xe}xDIQpW@3b)LP@XVBw4A?~jxSR<tu;CU%oQfG{
zCXlHJn4!?%gq5P;Acm-b1R-n%DUKi{Ivav+bU`v4>N-dT1=>Fb-}r_!fs7?+@k}T{
z-3y%?!{J_BVMozag*l}Ejajr~87C*`ad1pv(Fhx0hk6q_+7I(4&gLPwO=JmMz6#pL
z3d)qY=3!yEkE9`fjN%hM$d4%Mag4mfmcYWi0c~x<y4Ki7-eHa;ecT;KHHl}`-7_aA
zH67Ma!lB(9stDR-h4R7@b26b56gc!58X+r%>JCmVfr>&R8ha<k&;X$h+RF+~E%8Be
zD$0snScrjoW?)57J&<V>s6~)OjMm@9JMjWrTnvjnXmr7vdARZ##<YxkYDoZWFc9h(
z94$A{`TKCCh!BGdL3LyGJZJ+F!X9V{Sl6j)80dgJV9fx%3JtW06}&~z1hh~s)h)=?
z6)Iz8pab482;&;+STi_fmN*s_C00VmY(Z-uiY<yWtDuJnTY^?vgI0+`uc%G|&8k9%
zMM3M7K?5(Aph*^oVhhMftEDCACQl<%Q)}q8M(E>ah6b=@yCz1EfhcIEF*E?JodubQ
zBmwRuK;zT`=2o10i@|1tMi(%2;M$I7XaE|e1~&t+xCT_zfI<L7!%_o=(YOW_!A9e7
z45aH04I21~X*iva^EfozI$$0KZ-K3X+@XilMW9hQq!TMaOmIpxGyt7fiX1wK&;T6`
z3W+T2dx+t&Wdbq|k=9`bqb#t4D>e>J1)t~x9@D|;Ua%@i7&_;qCKlm0(-f=*<O2Mr
zLe)Xc1<hl^jD>C%f+tIGon&YL9Y;k3i6%I;!m3fYpG-hzf%2;pY}FUcVC-Rl+Pijy
zo*imv09w(D92dy>29!cgj3CS5aHUjZ$GjBSgsG#RwLd)AAfp863ERlO2uZbXVsSRi
z*|07?G{=An0FeD8gb2tq*Gy{zCp~M&loYr_V69BJ!LX&vL<cp<G^oL_aZ|{QprHY%
zG(-(P(9%^<%N`!Fpb`k;6L{D{;uJk>K!f6--NpvsW0g>+C%_SfHQ$2{9zf_qodm(H
z3pN!4vdfyGD7CmG1AKM^XcxE%Xn9g<kZW*Akf(EqM?7fHx0QhoLIAWU+{zG10J<?8
zRs|R#NkR698zYGXySn?j`h{45_KX*ofY%#>Y_%}ZWbh9N@$~hKa&`B2^nuhipef)~
z|A3JAz)(jY&yYwf104|0+27C2Kgie05X=L)%*qJN4EGNT4smu2cC|7F^L<?19eu!7
zfySm>Qj3#|G7C!bi=Z_VXws+H$O5vt6EfLm2&<GpyG7wbpczB>UQ=*Y1={clo^FF}
zHid~o4$MzVE+~TC#|PRYV+7vB3EsaN584I`K0X7q;mHJTCo85bWKS!!TnDcQ1C1FQ
z8i3YBgVP@3ZVXU5GlLF>nxLJ-08MU&Ca58ScP0;P2~x7d&<smQpj#-=^9F30wF%UH
zCgzYHC5a9MWecRktI+cWXzezhP(UlUFgyTTZHhiYW@uoDGKq<y5mpysi5J)?HPqkG
zY!CA`WT*xUiMqrW!$Kp_ZW2g6g2k~Y4OI-i;GHNiy%-?~tBSC@7sn1$3_Cyt5ojb5
zx?>n}%nufqqO?OXOaUb*3{#*+x@AHKgbfW~%e!G|3lf~LASTkuptC%{=R|-EhV(&U
z`+6a+2Ms~`!738aigA>L2CcC{C(?r3EN1YQCrXu#?nN_*si3BVlb*Fper{rBo+spZ
zC_@87)M00Iy%sR#D7yViA(uH2)NTpU?q3RS4ms&rhxqt|T$7j#Ys(R^-_QV|1L_1P
z=YS$m&M&Ei*5m|@K<I!P0cuW`7NsFV8a8^2JwZa^nn1?3gc}NKBteeJg7_X0)+k5p
zq6al7ag@Mxpqqgd;`j|Q3QmO@0t$3U4h&8$K?DIT(C`}rYMUbTKwV?akd#=QnH-;A
zTvC*pm}`@qkyxZtT#{mC1!BdQ6eVVs6oW<W6zrfC9%u-y*wDm8(NM?GNY~N~RACqz
zfVMhR6hlnQ%!lZ~ZIU%)@eM>d)N*i*1Y3fGStP=CH<%!<$bs5y1X&G-kN_Pl3(8K0
z2B2;ulm%TTZD;^m#}4W_f*4Q}plj9*4PcAb5oST!S;6^`IxZi4SPy221X|+`&e&KS
z2e$*lfEx%Zra;qqSet5~1@Qz7gmiDv+SXu)1_YNFnnJ=B60b?6X`ac!C5DDbvFe$O
z<8(wr1JELTe3oO{Y3NF-o!}Y-sfP($dyf?Iknn<AW(4stEYk+(<LQ0ER?p)$4Hga1
z!?~eTs+I<t*4FXy;QhjxdFk=U4usU@!TBx*pfbl%3%nK#WPvsO5JwvuXkG+0f00Ab
z)Dq@+xOYH@mO_#yXu*COXhRxY5zKKp#jXKvLBaXK@!-k?oNA3>y&Py#!)CMvBu5w;
zfR@CAj5ag?Ej9=BDZn`u60&HvlvqNNOlE#D*2EHQjN}T~DsqtjL8f7G1!$=^#Dky{
zD50(}fsDf&8bFpuXXY2jgHi&@ltpkp)ZZl*F!^8uWV>P0)i^@{dUFCIspJ&9=76#;
zI0Rv*GU9YQ;z&7gEev)~E<!u3H;Bu6=x`m1?ch_sAhyG5CS2OVOZC7?ks_!VZarwN
zFM*^5S_KSWV`yk#Y6u$oGBY(bHnj%D6^g@35C+3W_;EQ5wbBHK3b-}`yA{;S!s{&X
z279nlByVLQY=^aYaoKJR%RxxmD-haY4NzR#4GlnxV;zdkOu*xTU@q!eLr4b_SsNG_
z!VexoW?+V2CBg}WGA3*;0VVvwr~86~1+<VBZwP`Gd}7338p3`;3Cqv`wD=UdV${$K
zv}6=)F;al0!c7A$F2(B_BTLvI2$KE)Z}8Gcu(6=UrT9%i9Toy7DAKBt0(h$xz7!Q#
z{KHRTfM#jqg3=P#WRQ2Q<9*}Hi!w`K?N|6JQm9eTzL%i^wCIM-(?i?tFyC9ijByR{
zP6p=-YZOxnB~Tp3nt}~Mn3<MSTATr`W#B74p)SQz2*X@w3f2d%xb&<`Qgd<&U}m7D
zT4Pu)hx!(gSWUs&phgs@re?#9AXFe4qerwUSi5U*iAh>PezB*aVSIdUQgKOQNosrv
zqNW3_pv0Siphbc)YNshUA8LF>aEYN&yk~xKd~j-Nc1mUuB(}j7PCU{eh%sbm0l4W@
zW^D*=kD(8OL60O4&Icc$2T}u_;Q+N57(mnI;MofJjcM>{SNL^nCZHK#*c1lnxOMm_
z5qRnXBmzE=5i(2!>JBBA<QKsP3E^ut;j6Gf%Pm0_I>=l|sQ_9x394|BM2un8E$B!(
z(5`fZ93*8xM|3RVKESr?7&3ff1iNMdRAD2BAAD&W+-=aT0lV}X%msT6TriMeGw22#
zq}$EVW5pb0AOOE7oI$tOz%IG~4gEti0O(XYNGgM-e6VW`4L}RVK%r@906l;ZmJ?v(
z==iNeo?U=NCUm8&p#kWaIAlAGO<~0oN|3>9Br1|1>t3NV37~72Q7kZnWkKk{Vo<+A
z$BT&b62t~4J!{Ycepu*1CKQQsp$SMIwBzZX3L9@Qhjd$sa{_1q8^{F6idm=|;LEJ=
zraxnN5Rj1mK%HjLeqva<f!`TsXaHYL1+^734xxcdT-bn2hLo+~JOW=21v4G)dRPQP
zO(!ngOkl=4A&$+46)o@uPf)824a{Mw1DYDi3OY!k0l99>+8Rp{2_5POO$&qefrD?3
zF$EnPgTIh8HL%o#PT|7x72GG#HYCUtNRbFzBZVYl2I(6iiNJgR7SJv02su1S#n2GR
z1CTY75HI5_CoP~2G$iApu7obz1eZKu|AEs0X_gZxDlH({3~OS<;uTz_C1~&mRItGP
z0CuyX0jP3=l$JOaGNYE3SZu^wV8TwdgIj9=I!O*({ehALIJjV^YC&B@TIfN_HfZSy
zTGfN>NmJ0yFt8o4EP`SO_AH3Six5jt3sBTch6#rf)-u!*GIUS0L$DU3ur))7FoyaP
zC14R@L0SOgDM?|=T@V(-2SQ+0Lkl}%Q!l)@gGDH`kqff{;R#rTLM<RV1mQMdDNsQR
zP>_?4p#k(DLPG<ne-QSQ7Ko4n22Yt9oC=<dK$NGTQBwltDd^k??2S&)0vfPKjGz^Q
zA!vyVDA$=HL=2(jDMG{u+UPU{Ez$rrI^n9Jp^P;e;nODIrPvUk;wnlFL5n<4Tn3pI
zG&F>5wgM+z*a4?_+fk_Y;x9!(_99JXqUKgKgRnO@4M7zgip#++H8cb*q5v15piqNU
z3y`#dJxQWkhOg{2gxx0!iv`&6uV{fqLLfoPF-YDrgdJc8Hy(BhFvv?_k3f6~jeBCe
z12G?^oCNuT$dJd{%mg_Tq@Nh;v6YU7uvHo`Z^A|eP*WU^SwnmY1$V({2wDe%oK`Sn
z4jQOLWGHxn1&ub?%s<ROaF@fQ4Qe(qAqF=bvoth>P2s~VG6WSO;B)~CI=IzP8;S4@
zq<Fwn5Q3`7)Vva-6hu)78gM626oO8K!&(#?!j=e_8bkL98G_Dv0~dt|5mRVUh!8P@
z6@{RgUvN<flf&m%^nEE1-{LF`L9@r;WP&go627oKP2da&J9PtZK1a8iKyiqA8zyQh
z$1n;<fe1P?4aN0f*BTmvCKMqBBJ@ZYn8UFrNObG)7Kxx*5!p`o-Zn#&AcNV6H9?|V
z2r0CnxeInPCoH+ZPYwZj3miERUqVv>abAMhfLbh~9ngk4j04XEknRcgf)V}96ud@Y
zEgC`7t;nGXFB(w-6CMOmKNA(GcnU|*)Go5Em~lvoMeq^~7J<;=THFx`HJ!MqgBy>f
zfCSA)BYVUURJ4H82rM;{6?Bk7gK!avC?G+{$`B|ZL0h@87m%R)K*7E+fo{?^1kI&`
z3rK_r^xPU#xCm&P8>Gh25H#luE+AoY(D22YOknHoA?X_8Q=G*kY-Sm5E3B<(1a&Dm
zE5f!$;LY=>_7W%@p~K2VcmPLd+YmO34R<-%rG|!}*=0!42p_G-+g5~@1CY?dTQGv^
zNaR?AoylNmh!R*N1QMjsg61mFOgF0Wu(*JY;X`}~jeG2Q3pElT=A#yhsJC>YR!gY5
zu@{NPkcG#@SdX<pgiX=I;|=CZlt9FBJPJPN;wcVcC-K4T#Edy;pc0WD;iVNU+F%1$
z(7XwEIVdH9ODd?@#Do~!a4bb3XpsVP`Y;3)B47((=?`u-)J7tF11TQx6olaIbKo-)
z5M?1~rj$Tg2wJO$y)1++jxaTYMLcLN1gM-t5;2B077=P-Ya<LnYa_sAAxsXRXOVAj
zhU9RFhjEsNCeY;*aJRr637ugF=Rx@8%cR*&piDG@<SeYQhUpPp<sxhW2iyl>7aJOa
z&P#)oizd)kI@Iyl(<P>bc*{mm^$1R!&{%<;Z(?W&z0e&xNCLH!NIyc#FK9-Ct?ht$
z40iGf*lXZ?0P!i*3Szwlu>-Y?G{H9>jCB1qBuQZ}Cru&yM2K|;*0K_`Rs}g!;f+U>
zK!pbc)aOJ7E}rrdc1Rs8s4-)cG^^mn8Z0WI8%bg2!+iisrQoQ9non%#!7ad2Zo*EL
zgIQ*12wMRKOPg@(p|%nkfRLgDUUoV;2D`?GI6C>bLe_$sf~J*=jSL{W??6-7hK8U6
z?a+_=Mbc-LlbM%V3_fiPRNCqo=olJVSm)=ZCgtQOXG1O`H?-6QiDJ9=)6fvKj0c=x
z5Yw{Y;4}oCPY25VAV)z(EMVPl&@pb{5*48dybc5!42A{>7eW?#LT;zWxb6aMJ!l*W
zn?~@0PPj(2S#m>Aje^VFVCSM_ZEWV_+|UOy8@Hn%Yf&&3)EdH8_`t&pRu7m$7xox}
zR`7soEki@l@)~gX!LC;&!m;4%aFJRwnc#t4(CjU?6&4`p;PwLnv%q1ESgUCWTf&3W
zYpA&ZIr?!gDg~K}!-ddAq~ME`5laj3L<`!+M$f!tWNom%CnB+e69_1UgBg&(hmg2^
zVh%Oc6?*9ilnXh6$IuXU12l3FSin@mbUNnb<R>SVq(b`<#r}DzPT<@MUb2nHn?@$M
zv_g{sOd57fJLK#aLqpigCU^uwJZlI#GY*u;4Gq!OXu?WSWPcc%V;B#bVRzEA4lPc_
zVk0PWv6^FqtR89v@{D$7UJ0lygUn!qrqS@X9F0sYaLiyEf^H=R7ZR|1W(ZmY1<He_
za1qc_AW#?}MBr-!AiY3n*@2PuPzrO*@&L4;2*qw#c>vvWWe6${!08V)&0*LBDM6u?
z2F6->L)3%QFm;2AQMhh!)<!JWG6XGh!s|+~6Tv|Yp0ULrB;e*MtbjyYT?DcUlq7Hk
zC%APA(TkLMVMm?8V+ZUWLqkwa35qpvS&AHruoQ<85O^D-ur*gOYs}#7CRl7i>?IIY
zkd`5|$pTvm1vAss0@@(}IUej}6f<!;9AYSHa}(!EEl90^y?u#xOc+K8VQo-?)@>0^
zAaK7C2q0Js5)#j#RWy)AM~1K^T(H2xi~s^A!`pMP(1&%hp&1?SLU`yy4a6Dla1*h#
z5MgViV9qrJ6)fO5HH0P@xP4G-a3y<iDFO)&YX)$$$<xopH6q?9-r2`L*wrQ8!!g(c
zxs3>#-Y+&b%SbHFfHd#SLEESyx5j1Wm4T1*&<D32ao+ZBXb4+vWoijsD+4Q>KntZn
z`3f$9l18DPFtWt!P*5oVT^ovC2B2O`O2{18nXE|V0c>>@n)?hvr#yk&ZD<HuqXcPN
z!Mz4<o5JFmfThUA1gM@0$cI&;pmU)hIRR=gBxpcZgJT}Tg+@1#wwXh%0M*~vD^d%{
zJ{cm-GsIT6dL$NSgeB&bLRVxP!j7B55{4j;n4z6BOr+J8aBHCZHlgVla^h7$elhqE
zWoUM{K;2YHAWke`y78IpT$Gv!TXb#+T6%^xt|03+VP1rnGSFBfE`1xCz}y3|6U|jY
zklYMY18X3X=4DH`b~Mwi4M8WTLPHZ`KimeC#iInm)X)@W1Jn>SdxA2-OShd8i&H`S
zHbE!FK$@o{Wnn`@Bm-O_MnaoPNc{`=N<J*55hz8XjgJ%X8J;c&>Z(0L=D_xoTQm6P
zm!&2q<)lIurDx`qKsRrif{x88Ha0>Wqyt-po(O8=qX<LBHX&Vm15jJa5VZKOq8Mhj
zZ)&a$*km0u6CFcK%zH2m4MD2{A*mYLrG%B*p!Iy9(huYwECFU{46_960nbcV+(+wx
zEJ13fVbKZ;7HB*oH6}re2ywX&>@1Wb5}Mht*nqodZV4$3achND8BoU|B{H;YfH8su
zPqHDX6&55&6%~BZBWzy}#P5cn{tPIp3=KgQIi#Y3$0kZeg&8o&brWi)Bor=a9pvED
z5^!G**}Vip7JMufTob6iLb@3nwi4eElpl~&0K9-Tw1kwrSO;}6eF|ywLYs2%1%S|?
zgg31~fr*)}VD%L)Q&C&emXMkVXNtn!l(vKvwYaUq+LpG2w@KkK4t73j;K03yS}9?A
z2Ula-2(+vae=HC(8s7GWM1c|L$S!0P;ckRS0n|j?Ap$oNbQn-UK`QLLBO}<!T=2pR
zmQx@p25uG92AnZv<_I>`HOL!M0VACnXJ|+$gTM}uL)Ho%pMf@62$^7L46zWZ3z|b9
zVn`hwLS>1e0YVFCY{XH|8Y1AB133p7Tn*qYp)KJWpay^syY@}Z^~_7lhxo<_w3ZY(
zLgD>gSa%7&W*E}r#~rVRM##oPkJQH47GVTmVhYW~hDM;Jn~<RxXl{UI1EOObsqX~3
zk03D@+QKygt^UOBJ4l)WjfjJ)U_wa>RBZYs=BBy^K(;ob?T|2nuLVW+6l#_Rc@r!`
z$S#m)K>K10z{9y{<1|L_WuY*ekg9&L@kXE(m7w4RS1e#5LN>w&mLOK5_0>RD;!S4G
zu#qK@c54P`b00F3VFt?7#TJ>+)0&JxOD~b`eF2@f4n9xKK*!7kzTF?X@5BhUmJ(zL
z$RlRZJqZ@jjtQ*wj@B$Rhq?e1kho7LGJ-9jgqaRa`7pzA7>c{W1TqjQoM5g1Z)^me
z!31rIAeu==u*Hu!TmTLj&>_x<s0OzSpamb?0?_G0*t^*v8?ZYGI(Ci4P0+nQINU@a
z$RQ~SK39XDk_;jHDbPItPCrmh@SFut1kh1?xPu_If)VJLAyE6t47xNO-DR+X3KmSM
zg|O4@K%3n1i=g}Dj6f?$K}7`2d>Bn29hd~?7lDuJfmV#5#iF3I7(m*w1UW=GIMnT}
zJq@9SFKmq_NE0Z58ybODXM!>>Eba-o!pIZW;`D@e{)`M!CZJ)yH}Zs?rsWB>8@5&x
z#cq%z;8sEc8ny6%nP=z;t=Hgwfi2BMG7q*()(Etw5Y)nj`N|L!dmy`DnG8L$p#B0K
zpa}C9+ImWuL!cf5sf2k9J;4~kPB?+visCbLyCGgf8ta2KYK>qk3SnM^HEM|pG31u4
z5o}c-%p`bCN1PKOW`Qrkb1zCv$;?X!T?qo7Couyh#9}j(irmZ;SfLAAF^DL1jlhMj
z8MuRLX=a^a1W^V)1p#tEUUEiePD(LE*w6@c1`k5FIY_q|Xz8$_k%f+tshM?JVo647
z5kxgu!q@_Q_+CbSK?y_@d_V*E4lfgH@TFJ~VM8O(GR0z(<kFI){L;LX#G*>j$O8C4
z3j-ZP>&)WJyu8#RxR{}ip>=*vN^)vW4qVJg$I!X}RhhAlp*8%{uB61A94mbzP#tds
zS_)ZF44z>F_g_H!z^#z-s-XcQFik)jU9B<8N7$-IP&h+VFo*(s9AXP}0g@Rggkd5e
zC9t#(E&Y&fb2f4f@J<0=SPQQ7(aTNLC66dN4Uu(1+I!BB=mUk3p%G|JBe-}ogl&Ze
zCw{nlQL{I)(S}CI#z9+<u;N)E4|-{Zp%G|pBdFvvHb7Z~gltq^xhv?L7khXOuK+#?
z2-ZpiEnftgfO-i#vH>YMt^uGEFzl_Z8Ng?-!w&>NM41+RuFwort`-}bDH`e+7#V=W
z-UzgI5OSD5c<2L{eru%h8tB-$88|iPm*?fC=0XPAK?l)5atfC80X?(<<OZ<K(Ds=T
z=*S>&eFbZCg5v=#Q9$p)4FKJ#fRq{`i-m9xryChVx}ngdh2IpkkrAZ!2yB@kHn)TQ
z0&%htXdNEJE6@fzY9hjKy^(8X0Pd7-VxWnXBk`LA-uQrPIi3+{X&@xwLzg2XMFiM0
zMxf)9Ky|I55$JMRQ2m7Dapaa3erJFVR}40W9pZqVbxk0LU=SHILEZ?Hz%75oxRnuT
z^&(ymkq`^G$GAY&5E}#7muDEk7D6J0CM3W?+kikNA|x4+;9OAm3p7LpZF69bT7m2&
z*40=CupmPQ&=v%29VFZX5C?;@FvKIE1(DE{V+pw>97lPIH{~G4DPZ{lQsBZ|fl{2}
zHwmH<8az-=PHJu{^p-3`lER0m@e5MY3E`m(QcS}b)qvGWupR?!wI@9Kj6jPaLGgtU
zfh9#83mWi-Hl$w(DR@B1*$B4g5n;S3ba)df$w24)Nl5h&d!QvtP%0<`kdZVX8bN1d
zJL*|mGdSf}7^gt{=Ah0-v57@)W?lufVgsEhWB{IY$W5$37BsXp0t;hSc(4P?%#5G~
zER-?=EuI9|5(o*DLKW;1Jl$zil!0hdh;mqW2a7vk&3=S+kj!cdImQUw2-PsqA(lq3
znIZNYxu$>tj{deO%E38sH-oi+Pb9<AqJ%BH#1?=cADEdyLliCPz#J5WG{%8m`<SAZ
zArLLd-EA!42;I|<%Q>)%nqV}pZ~`wagx@@bsMau?h%)XD4>DZZ!L=e%n+0~d8!qP>
z8iCfRf-=9M5ooC>sG2u40<F=kD0a%PK)<61R!>7h3RJcG1;-mZ<yXM(9m3i`H$`oK
zL3D${8H;Y;%)AI_J_aoW1y||NGz&|chDM-;q2SD80xe0Pkq_&vLc+-eWEQAp?xbh!
zn^=KlET}R>SDRQ70aKfq7m08JHno{~kubH16$I2KR>0L}=0y=QHws}c0kw%0aJ89v
znJ{x<<5Spz43u36g;yr1mV|^Cl9|}TAh80bHo!L%dJ41==vaD`C^Lm_i2y|z*naFW
z1u_Ea^8nw(3Yc#o<po+yffPX1x~G<ax6!~3U^arS;KWGpX3z=-+*W~Ieh15(s1_I+
zAdE%3=?;9GlV@IXQEFl_ta}Dp9E$8^=s>9<sDuak7@lgOK1azzphd4>!(1WdAI$8a
zROl>*kr|c(z|as$9b9KgYEf}&GHl=uwx$yv5@4T$vku5r=*1>#5Lkk4`~#bYG%Dy>
z3_F0@2(&yD#m}gQqZfXthMRy4f-a)~$DVNtWbsQdG&Z5J4_a}GVhOaMhX*WZTPUc7
zfi+-ZhGAIb2)<+kY%#(jPy$Dd7{`<puo`UoVNEJ+SepvAh!qyt@X&?16)u3ytpz@?
zP<8|tRL=SNMJdqbRG=IMavW+9TKXndKvY39f^TLXgb(!+beT1}E|@B)F3`PX&Z&7N
zsnC<+v0G+nfKZ2|(Iqo67iJrz7l&$_1xOc25mZk&=&(x{(5Zuvp%>VaT3DFD^9?Mk
z8i5wILaHXvGFDJb8-W&}!p{57PlFsY4L&Fw_o3gQ)Q&x)Tf&{>8Wdb&XaYHi7-lTi
z(}=+ZKu~I83bIGcG;Q&vQ$r)r;!vnZEHrH)9<kK4g?S{CbdR8>RhWa2y#X^6hd02j
zUB?u+qWruPV`zgHG<a8RWNcQDpP5$zAK(P7;Y8nA1J(x49>$=PCZXGD(h`$X5!wtv
z9z-3D0&4?Z;sF|q0`Ii}?ZQb(%>mn(0~z-;292H?nVNy*t&{Wf@=}vaQd0^NGmD^R
zo0^+x>Oe<P^U~5Fx(qdSKtrg;M%H<0X(k9U6Y%XNW%)3}L33m#7U20b(9+Nn&~hcH
zY9r91&Wd99)coAw)Wo9X4C53l9a9q%9YaG?BWsZA{5+^q(9I{1Auf<Gs5&S~g^ed`
z>KK|s?o264O)AaINlDHx1!pnXH7kbZW(J^5MmdSaC9s)4kS54&DY@XqsR(;4jKBkI
zps+0|N`>A-W(>NL)X>7z3}g;wBNev#(+t#M0VOv`$!UhRZyTD3EMUn1Pj3Udyb?nP
zT89H^JO+mjJVPtEbinL@)ylAv5ww;SobOP5X=nsm+6rnN8XAGtqJo;Vuz<jIRWUpW
zKzD<J(>2PK%b;sLz@nJf6bC?yK-gkYm`%`j7RW8w3Lv<Jrl7ly1E57D%!e>DVS}Ym
z55lWen5o95(0O`jtpY26;iiMGg9SU>6+C1LW`egILn>U5-EJ_;AngncBft)HLpTtW
zePLRl>vUj#h94LKb00JiaJde2UoOZvXp<AXJ_2eqXw4t4xWylyU{j#Zb<Hh+UG8B7
zTNDU$ENGM(lr`XP#pOy<uqhA|!5ToT2$9`}VG^V<M8LdEa8(kVnv<Gb0$ThDT@M5E
zKXe}_%(ak^B$R<cS7@S{2XeQgo;76gYDiHk^qy-&BOJNS&=5%}RI__(3A{a+nU@ZV
zGFTpjE#HH?6H?NE^B^dmU`ZB6o4}@haD|Q$sPT<q8(JeQ*f<_^VK8(U3bL02o}xj{
za{^76LFfG-tqst6LgYw;w>Dsh<bfm2654G-Z!f?zlp!cbq1gcGBS5;Ga94n@PltyR
ztc3v0$Y`+%wi;G6!EP8gg`R>63o&y8NL~j|M1qz*z(Nu<DGmy1n48hdXi$*`aSha=
z;5r#};4*yp0kTCh$S>G19wd%_#t`WKa)=D(-gT%PG!8(qVhEdiMr_IO%ye~4OM@=>
zc26yFFUl{49^(x<&mGcwf`tuQ3W9`{p%G|xBd85!VhNeBz!grgV8AHIKrsil7(EzJ
zF1$5xb%lBfbd51IQyRf;r-ug-p@0BqZpcV8D7C<HHCllQO@y#)4WIvqW)Ae2gbfsf
zOazsg7(E$d*osD2c7#<BFdJam5GD=_EOeVJaP)pKYY~VWKrVv#(HOP_66OS$r{GS2
z9!81FYIDbw6p(R{ERU2F!DEV!DJjmdCSgEgCbZvT3|dBooV(F2MPL1b?q5SQbb}$*
z!;&)iba-oUryDeD;GC0K4A0vr3rY|Lg$b%^XnX~w7N?dFqul_Z5~>xnlDQ<Ys07@I
zh6OfyH37*yu&{>a9bCx?v@kXpZY0zi&%ENy6zJH7F>Ey=EHa@6!s<*|a)QMrMxHZ+
zm<A0@aPtG+*-y+(4MwsFHa-n22Ec_P&U(ZUG@Wh>YUEjfx(lFs8R|<6H&_HiZ9;QJ
zuyK5Vw_k8P_FBX}CqF4M2i#VIg*9yC7wSTI9zui$q=d#98it@-(~%6u-Y5&p&nbni
z&@_gvXM{xnd@u=SDNf7GAm)Ioa3?+MAV^mPT(RaQ=7fN{J+Ksvqa|Quh*gtonKfh~
zBe)uYFBE{rEvRD$S!e@VQGmM00OWRCY>Nz#^isOOz!<cG5>gVtC*Q%9qcLc;Bq+U@
z8ABx?2@INQpalXnEnu|wajz`^SqK`RA!Go~Wd+9Yg_bafLGrL6$i-l%gB@xCJF1F!
z2jjlM0AweTjz%&BZEnFBzCII|!;N8wRT>%_LhnI?hd$BX2=aza|6=Uv2DyMZZy*~2
z_6DM(Y7AN_i7Ul`!=E^(;_ldjY#_p!*p~}{Mnj<OBWSwCnmk~|0D)vkaHRmqIwIUk
zaFKv9Xq_EOsDe{5JW-?990c5rv=#umMIX|(f)>rtiyH|U0?`Al=b#+qWdOu_o~RW7
zM8-O-O$XZg04l37_c0J@5TSX0W6-KE<QRc<ILM6?$oxAbIzaWWF=)OVuLXvn0)d1q
z1+f9LlE4}=QBPWgL-fEW_u*%Z+N9){Cgr3eDl5<u1^lOHn1Jp|f$UAh7;iI%FBV3O
z$AG<Q3|jRIu5@4$#-LM6LERWbW7w)+GngD|IfHcUKnl2j23b4~32)33f<SIS%D9-i
zLDQ|EeG{Oy_OSU2==6oL5v=hAbBZx&EicHKaL0kX0`(JgCXIk|A(01nF1XZ1OiUZY
zm-xc0gcWPB3!uQ|1vu0}iHm?`p!zugGJ}R$OBg{$wxFd0wvfq$>P4&}F$OIP1}A2i
z`@rroGzKmA1;r>h{=fkObsDVQiD5qSZbVqX!`4iL#&IE~JJcEr6UcH5LwI;Y?1fr_
z%MXwh&ycnD#-N3~$WAwdo*V~uJlM%7X5w@>#88xNfFSqcOJ!Jh0fHO^(uC6@Y?}a$
zL6Z^4zJT_X(E<kUHv$0!>gs@Q)PbZCP+<!#NnnSa!V)@W1Q0M8zF!X(`mh;zXpF*L
z2ueEO(1#j`GdSQTA}z9q<y6pWVw6w;<y5e#uml6Q4{8mrBm>T$kl;W*R46aCBt13H
z$_ms4M_f9OC@Db`{P;^s@Kx53k`k8O;jV{`gTbyxHU{+)z~u}=1bU088A8ModY+mw
zXaz2KI3Fg5(~qEm3bgCuA*+PJ-bS=2(TgR}+Ffw?B5Z(!t}%3dI5_Koy@<D1!f8La
zq=J>hm}QlTA@TxWq_PCJf#7w%&~g^30D~{^h4}>RazkU#vR+65hPH4DOM1j<Dc&*-
zv|JcD3{9X%y%-v!1R`3Kl^DN5N+f7TgD?1nde8(mZw&GxIMN_KhNclxya%xdwd6BF
z*_=g;Yq1xGCa7y#h%pFj`3PS|4D~*|d_)Okc%VQ-0kbr~8ON|96P8$!3QhP5V5r@g
zF-*F3@KO&Jy|BqIm>n3=3$=rkh=p5%rD%m6w2D;lKzm%UbW1^CLW&(|E=4YHL04lV
zZOs7PpohPB1s~Iny?BMM>@|lBksBJrSN0-Apx2h0Aw*#N(2PM#Qo+S5Ob!~tn29DJ
z8Qdg=_zK!U$7~jZ&g29a1PJrM`4T>I2Faj?&?XNd+Xxh$sEbRH@)gvv;Dgg(i5aP&
zgfB3Kxf0?kW7xr-hM;f(yBq32EC~*31>Ry2z7!K?k1>4dB|N&I1Beh8K&`_W`jAoz
znq5GPH<82I%mQvGG!>&5irWtmQ&G!4)b*Q~L5sblGlOha!EF`Rat*Zh6xk#2at+#^
zf(${!y+<gJ@RVZk<)z3G0*+{S0wrWRygY%$0&F}UY9!p5pwt958fqf$K!F>HrHFzZ
zFp89UA;Dn?b1~dPs699n4<zft3nb{CaKw}y<cuB2P%dchwAjP|X}_2;s2NyMjB>mg
z;s7?#svYRwMnhxJip+{)@X2iO!D6s&pf;tBp_u^)W3Kx)hA*pxp0of>2WFPg(gGzp
zL%oT8>Je0KfG3YIn_H-hD{*Lt4c&q!VBqIFf!qh`)Zn!lbGQxK>wquL#O^uRbQOHx
zD{5?FcPBWdL)L|Z!#5tZJR5RO2mIPoWB9^NsKsy_j6tg{A?-rYVn%Q^096BX4XnWi
zOS?Fn;htIoooRtN13VcDI>8O<d-%#fs09|#C<VCzlu1al&;(>MHXA{!TbvTpq0=13
zpw)!nf(2?TEa$*lr_eCQ?>o>WvLDD$&{&<5p0#^wiBn=aQf&fSsff=Olw^zFF7P^J
zsOg|VONe!dg@>REtRRC2FdxCjP+(qySOz9xheN<NWfK>fpcOhG3tclaK~wwi=}^#A
zKIoi))Dn;^ELvf`J5=YO<yfd|AWnrRKWN|+%uOKcK#l=T+#y^8J;?^V^athq7Gu!L
zOO)t<*lP$&YKXhbHLXF}nLvzSKf}c-H66A9-WYa3rx`2|j6qGhiek@_)FSYXYES})
zTTfgpn1U^UrZ`vFfk2=HP1woCu6Zd?o$e^H1zOgLFJugjL5E+0`sRklpoNj362TDU
z8IT7kOiiFt$}rvn7ABwt7x9SLgshBmPb~qRln6~*@D+{FV1*YEu!saZ7vwbJf)tdn
zLB@m24P>W)%PO>xf{o}v?S>~>3hV}_WAtDE+YN0-BTZ0&mNR0BDR{mjChy>xow9&*
zNbzPp_}mow{&Hv$530+-Z3T1C@j7VJzMw^z*rt7v^in+S3tN5(vLBiXz?88CbT|nv
z0WN=Fi3I9tq-kHMqacGtcv=q7V=4(50BbuyV-;xt9Cnx^++nbi33ih)*rD*VGf*QL
zpM#NRY|%E88Ka)-h|>_{5hYBA!<J^^a<~O_p&IdFj66Gw?qo|yCxUP|BO3x9)kDfb
zu(hAKoJ^!s@nj;@<H~S_EIb>bHYH&TN^v?5oD_(3Bxaive3}PR69x6|XIyS2JjDw;
z#uDyqh%-T14wU!cMKp0qm%tP+agHW<Je@Id<`8#C9q8^qPz_8VaS}1bOI-3HIK>NF
zjSGtra|7t8HJNdO+(`rtcN>Fxf}pAhrv)TteuxFo0VK$5EwsWXE}qOFdf+p>jxL}j
z&SsGPEaspED#d0NMfv&g-YDoWQAjTbbOdKbF+_u3eoCsDl@54UMsjW@Y?p^IXw50&
zY#&q5**@Uqx5kj&-pRR%70`_uAf-A+MvxO}Q*#pG!r*N_s7s}g>;#Yefj6#zHo{<(
zwV-Rqz%6``S3r3R+6gu{gboy$fL2?A{bK@J0tsrPfgK2nERY;j54dRyO{L}#>&?K2
z>ZBoNLeL6ckkv?O8?Fm<pbnBQw35~Yc3vVb2cwn+FjH|CtRMriI0Iggdgc|U!p<rK
z-91Mzw7?+(DX6f!1+-)_wW!23z&j1zc?K_=1MRKM%u9!)PZQ9ojNqJL2%ioI8*KtQ
zQxOy+=CEV6;PC)sz*cm_3UpYX0~Q*Fpe+~3R)X9TY=mpcJC=nkCa?n+aYX~Z0Ki`M
zf%{f4ze9)S(M&f4`5hDpCa@+9G=$MTXbLtAegvjVYC%Z`%xLHeX}Hl4m%>cO6=bGh
zGobw(=%Q_Czu&|FvOpKz5f(6IP+ed*1*IlK9RgcN2zLmyc?NR{+>a)(g@uMDpp|@}
zENEzqb|fK&-7w2QVE|r)YG??_mf(^IvWWofB_szyhZf-uLJJv?J3xVtD~K#$=DFgs
z5EdXPc7ie=y7LVUk(A;z4(vyi-nfYY<QxDD^RRcT(a#fyO-)0a=+LvpLDxFil6$te
z3222P>e=ELw%|Nx+yu0!5nKj<Zk7NQxv(?>T2crp1E3>Y(2M{rg3*sC1}!&*ddd~g
zk>eoyK+!>zDX>j*m~{&5L`P7-K@$v^GBHHCuYoitLyAg_0ag>#m3jC*j@DN~wBJeb
zxC`tcN2puKGzAuXh-R}1Y(*nH+@Ki{w5|>m*@h;d1#qDJ4_5&VLs)vp>wV<8Bb4p|
zDPc&k@lT3jSbG4lB^|I8OeV0kZ!k~8hK3BuPfw63D9DtG31|r&awda!D@?GS6h~B8
zLo7g<;{y4E*vy4>x(nn`kO4$F0Nc!$3212`vQMFnJ+y#?`yZOPh$#)gQv;bn-muwE
z#8kcsXcZ!|rI^u2f<5rLD_Gn?*A*ht2E2rX&Q3xNCo0h3rlXyVZUS1Yi0lVLSk8nL
zXyk+#{@y4lxrktwloZ3Tx6L3eK%_`DL><yYyTbvcx*^;-C8hMm=%He11i)5)!V(1{
znM2Y9&LMfc-oW03fR$Fz1!yL)Rh@9VO+f28L4(`~5ok3?PO5=)XQ7j-C`YZEfX;G7
zNu%(b07;~<!VH=rh{@HU!XD45>n5;upa`$QE&_)a%^>fCD}NHK1$h$GZ8QKM^#tw!
zB8D|hjEx|NVZbd#_ooSH#U&`j!SyAnmO~an8^we7AVI5mRNFyiFfpMAIhGx268@&9
z3B0Lks!4uR6Li!pzNV&$ofYn;CT!iOxe@d{C0MY64u=J`K9D4kd;7SX942_1ny7c#
z<2MD?R>Ev*f>w7z5<RA?!7&dhsEJK{5GUhmY8s;~^CskEm?_}&i_}OY!Q*(EnxvQl
z3qGW#rZKF84U26^IGMl>$u%^At%OC7cES~@iK{E_rX~qt2r7L*P6o|PJA$vEMH^=}
zA;B=>nwqf1xo}ULfEKwz$`a7B){0_CFUrIUoVZMkp+zes^hii$koF<85d&M!ie#%P
zbV0HSXb~o;#cyb00v!ed#Sz$c6ibPU7Kp8=ty0wWz<3ii_Esr$*(y;Ez}hN>t@VU^
zAF1R)@~DXkJc|=u{@`hrf{u_yDLFt<4Ym#*t0crHyyXasQrIXHEPugW505^m=|qJa
z+<5ecDQrO|JnSHe0`3L4wNTp#)>|g7t^``9BxE|mEmIN<BeG>mLcvHx%ajDuh-sOE
zR?DI!I(Skiw`FSL>Pp<bKPI3ZXef4@fKHADm2L3ELs7B;wc6nQKh&lvXxT4{)o^b^
z%TI{!VFQcABvMf2fUju^TN#Y-8|*AUgePGlB;*Z{Cvi7TVGDiXmZJO91hnKA6y)Ig
z6<peoU^#)NDe76XcuR7;P1DT05`}0(qgZ&G)Qo~QDQHbHYMT^cJnqIQXx%a-!9WuT
zEFeLPkRgpxxCFk+z|qhs9^V~{CZ>>~dmJeasvXvT!fbxRmJ*vA!JPw+R1-6(gCWH+
z{y2u(g7dCL&}<H#J_l4g?y*f`{D`NeNtAY25P{lj(D@D6;%9i+K*9pF#Q}V*hM@^)
zu`#G^16KhJCtOt`#HYC1mS!kho6s`=L^r71067wOqmmd?h-*!PmN28FViV9(Wk~i1
zEj~s}{2Cg9W`1F#N~9+qNW%`=_JA$@g$Jt%XiYA}TF@F?bZcSOfno-ndLa=-RAfLb
zMr~0-PX@)Cr?59A%}{4=u*V?QHY8|8F-lNF^B-D91ce0Hi%`$ubtAO#2ue55<)26c
zV6YX&NP!G30^n8>V+Xup2aP+}5@IA{p>-oXcA&-*$Rr@ALgyWkuU0UDtsaJ30Euk4
zqe%)OcuNr}j37gWurh#%97(v5NK}p_>c|A5%6lT3g~XJJ#Iy%Nhs|P7wvf&qu_KkB
z@XsW+A!rU=MGa2^CZILUpz;ABf+!Q9$q$<9@MaiL;|SyuXl_Am1HzUq!|g!Np(I!Y
zD#Y<M{y+<&QGylZLvX%Fq;qrVrX^xMhP%}VTjz|^XD0A<&Y&s}><(gVCD7cnKv}nl
zy{shM&ND+a^UNt|=7H94V`=7rOvl~IgDtr>H-g6nIB-lr=dMCpd2k85tvoaQjXc!j
zr!YbatQ*#V!EEEf7Dr=qCD^%;5|luUf-S+_#6w#mjW!YC2-c0eg-3)3@ig#=(G3e8
zP}2!owu2U3LoyjOk|5z=0$L~yiabNm0v20H`wprC8b&y)E|52IH}8lDBT&Hraw6{5
z9TA2Q*SLePu7)LE6VS3{P$_C?0$L4>Xk~#{^}*V9u$@Sdz=8${u2K~g9*`Cmw0Qtu
z$BVEOwzt*<v@{dVQg|tD0gV6@Tk%E%#9Gvb9qQt4toaCgyAHb460e0=n|1KjpfLZ!
zDh<?NG(o!~8DDt9T6Jde&}s_QQi3LF(ArIuBn^sFa2^L0Nni#MF^1lz1FZu^GZj+1
z8-nr*vZ;6j2yQI;7$9g3DT?`!ScW^9l+Z!6=-}al(x4+E`w(u=5n%|CgMCC4T|~6z
zh%kki#vJTYdCZCelKY5m%z?5!af5oMu;W<acAA3L>w-!lxCmM40Nj*=Cjpd}oGEA>
zFiLtu&y>Wq<UqazmDhM0a;EUbzOeic@*X(Hz<mW5AtGmje1)?eX9_!t6drQu{xStE
z$pwWixM~ACg9uCUH{(FI;v3ung%aUboC%^8XF+}|&J?t=7)vV-WIE1PoGE-Iun}}C
z05sT4K_`oX8Up4B3B0X16TGcBkUjVU3alH}Ou=ZynSyqWK@u&dE5R`dDKrVhC)g64
ztvHY+L^u)0#4s@)#NCP`N;fQcKush_m1hcHg$#=%NI00n*BqO|)*PcpBhJbS6i7H*
zam0iXs8j$s(G?O>n72(5V+c{LI8)FD4wPhJ3R<rV%H!s+bJ9TRn6wlEX*)rhY^I1M
zywHSa3R=4hvcb?4v``k&WHSbh0m7PWriRcKBP2ec=@M_4L##z<vVr_ZC`DpzvVoip
z(umhWY)v*(#3ELxf02p<q+kSv0+<W+8LlQ9C^5mBY$ljZHdD~rSd0h+8w`&(B5Z)S
zt)P(xT4#%9Ds*%Y9xqT+@dgmwShOaaDPl=0G>{;%40kdqp@V3$!NUi+$wo|GA=qRi
z#t`C~Y{ZmPgqv){m_kI8%@ncp7a9++#0t%QL^s($$&|<@8)6YI)J{{-3RzHr054(5
zN(Z3EjUzk>pf=fH$8W-IfcX=)eUG>%8_1WS!Wmza4Y5=f$$O|7669g92s97j%ljZ-
z;cl`aR(e7$L-&^{Xgww<Yz;y67_pWTXtJRm_leOD289w@lg-t~)z{T8B;M7<-PNiD
zw2{LUv@BG^K*!v~K$8I`?dIqVlLoEk1WTJBq(dSDAkyZbbp)xwuI?a1tqgUbEMqGp
zosb|;cPm2!oxo5>7b`<UFyG9|&<HGGW@Ts$W>{Dmn&^bMhFBRHfG9%)D<ealfXE<6
zUr!e+BO{%FAkSc5D<fkF!_dge$VA7(HNwisRL7bjC^aoJ4|2CLWJf+|dcW8tzO106
zNCim}<V`b8$eu^c1{r+qDD-+*=ptQn9Fy_}2!nB7B4uifI^Te-3D)+3x&*Nx50551
z%`MzEL3a*n8Gx#Db5j#dYwP&<l+>KmlFYpHc;f6e08QOEYJq13Ax1#F4{fx;m%w5V
zJ7W`QWrdc%kb=VuVjlQh3=`kf;ta_B6Hb|+J%}({VEe%c*kTG=cM8d4phct=#Sl|6
z^Yi=*N;32FiXn^T@Hhsd0m(6-qhJipAi@Z9Aa3+c%uWqXP0r6t@h?h&-79En47nf>
zR+J!l)zB182a-MDLz}>t130CYl&6A@B?YBgQ_z}MaQ%<N>(E3BJ7XMl?*O=L0axRe
zhLGVM7!A7%)Dl{$pf7VlcB>JxW57p;#KZ5<2{w*T$poF~5pQS=RtCC9&NDA9-!<4Q
zJ}B5QJ}B5E-rC4f%LtzGAfAC<D*;MKU{^v4Nmx#UdjjSu&;@oNjbP6}TcM_)1*6b7
zhTgbtVF5A65F`gO7v@hxQ_$K`kYczJ+;MM6(8o|Oo4cl@r{eWCxcv<Bq_uTGQGQlx
za!E*jaA^_fUOQ-thA;2L9<AokGu5De#^Y!+bPK>~$`tH8=tae#zKfO#YD|C@lj2EM
zsP+?+T!?W8I4_|k7*Hw!4@*OWD=aZ5GbOQvv}|RGMHe_2kjfL75=d#20xD==Q9)ec
z0zY2>Ph`Lyf@BMlLqPt=bp!>ZM6s?@1y!}6qM8A9BYjwEvW>AF;_MmFsxs8GXG}qh
zcd?y4gV0Lp(KDv7b7;*?;VWQ4`OVN2v^W?%!UB_kWLea(LipV{EHxRLI>Co);y!!^
zWF@Hk2Q5zV7=bn^h1e+px;+cBS03g#SaAWXoxqMYhg~v8ilYNSEke*S^BDUxK(-R?
za3o{Ei}aBl4_hCP%kie5!(2gSoS`Xb4K8?^3!xTyoC{CjfzHpvc`>`G31l@4F>w&&
zjW7o66{KbcsI3SobTCs7I2jQe@-8?xzM7h%w7YQo7}_=hFXji0@IVU#_|jBF2!dS(
zO5k7yIH{o3o49QObx}a3yMoGKuw7^y2TV;-S_`;M!a9hAyq?Asw1yTXbRkZKyWAAi
zIEJPZ=nXaIrjYpnqWuH!;lnSgLFwQV>lwnmd}2)^va3&Qsvx4jPpnZGoqn|ad8Y6s
zy0D~X3R{v3FI`dM4xIdm%^{%9DlCpbEhtma>Q|K10kREjB*GFCtbK3XX%b?IF{q^n
zx^)Yb&=J{)sIm|JOdCj51wGOR6dc7yCKXoth8hMs7ND!h40H@FjW7>THHELzMI>mD
z-*6P^h}s8F2M%>%6_OUT?i*534O$Wm31fJ+LUskbZH^j=NJbmELM|!5tW2Qi3qwm_
zBrS$;E#T4?*)@jH-HF)41m+qTjaCLASz+Xwg40<jr7e;kLxdg-cNvl7F5=vW(}5_(
zE1CmgnlM}lZxP~+N0`gdY5XCHJ1mWnuR}n0Do(dT4{Ss#%8}EYA*j>`3E*=SVzWG!
zgokn!0E(-SwP6GyY&Sgi#AjiQwWvWd9@HQKU08_RkpcISu{aTB2RM=$plg6|n1SJ5
z*h)m~?nMM6(cy_`2BDocWol-IGoryk3e$;RVSpBDBNs2`7Le{Cyp$v8Hb{XD*|QC<
z72pfBq25L}5|*@ark=!LX!!$ktcE7E3IMJ2Ms_XivKhE*A<Yw9Wnpp-q|F3&CTOiV
zvRUwZUra%(twH?`LsM8SiefBmP6UxCAm$>SH?Cl40d+BaAvH8G42_H-!zYFyYmhS$
z7Gui-ykTe0`lRNi!wz{eg)gIq8VV0l!e*L53<3?nIq6wLI7TqHLJA8^hnPdvL3R1%
z7v(1Az)$ivg)h2>ItQMx;n9LCLM-7%fsA$p9|sK)f;kB-%fm|~!afCE+YB)aWVVx@
zwKW4c-`g0&cK(2>RwCO1@I}S22~0?D&)gJik%K%^f~PfLj*@3lG~sR!fEF`@lPz|a
zz>@-MkfRumyG3A*vIQJP6Ye$veCacO7ZK?)Se=Akz?q|V7r{{p>Fqn?X&1nkN)zWc
zV%><-ktqAE(8CX2OQTm5@Fmuyxsqtt;&LwP8f#4F!t`Od7uE{I?p_=fF^c)1su*^f
z4rc9Zfzkv-(Fm%45gIXE25SdmcNx57LaTdFYydgUFE}1iyhDc3u(%SX9f)EE$dy>k
zfK_PF<5WNgVM7Lypw5L2k;2LqB0>_m`n86c53evmOU;o>3wVVIE4WZ<HfT=(*(Z>y
z4Z3aszE&LSTtt-#S}hH#GA&?tVxgD|t5Glw##(2BmRKV@8mZ1iyZaog!AP|xtjITl
z7x~7Tw#9~K(C(k1sfC56En<lZctQ%Cs|<9oo+oMwUlt9!ObJpC7@C4sM}q>u93g=+
zN(L*hjPRWr0x}-Ci;JuYcZ~pAjtvPeXv+f0O`rw_*h8Qu4~{AU;v`sJM=y8LPS!<r
z5=;|%HiR$aMuY~)CE#EJF<?%D(YUfB)M+qlFx-Yc1A=tnsT1JGE)(fEVjYRom5^o?
zdgg+;5~d9!3}Kxx5}ir3b72-^gepq!44!A<?uF^Ya4@W`hQq;zrl1ArkikOGqH*w`
zGD0n$>;mq=;OMAXqIA^IT#Kv`!@00AaD)>;NfVw%4NXBSp25xq9c2aH>S1UGT7O(o
z3>s20vcop<i{b#(n#9P}6<1ASiM??H33Bk9D6}?$n*l57p*4vaeAP14H|DS{1fY@$
z>MyL*=Ex4lTbG!@S1UuUfYl|45JM^Sv7~q8%ES!59vNyJyfQHZtwRQtrG}=kV_;DX
z#whfSTwNh1W2;TfK&yUHibr@43}g{FXrKinR)bM$6W_!V8)F?rZ2~&Z%ESUzo0x&t
z3L|P0@C+n4HyY>|TbN*uFq(mu5JQ}XGIwEahV|4_W8cIQaEsR`6O=H}rYjJGbRc7q
zY7sPTIO`G6!G+Mq2zJ+iZoaXF<Ok65SxEMUD@D$}XjX!<0iwx<R@{KB#phZ?7Di9<
zpq0DGp=S<l9KeDP9Q2s=9;)MDV+|m6X7J^*&?^(sO*8}r0dk6grg01-F&zzSk(q%O
z;3B&kmWyDnh77+zNoc~vY7HpKgJ*ysPKOu1kQhPaCeV6ZaBeb#Ezp4ECI|_&0haVp
zeFC+@Ne^NLY@r1-ba1#FY9dZIgUWG;dGG=li<7~jOr#UR*S<LC=Rn2`%;0Ngq3(o_
z8JIy&CPWQf>|Qr^&Ib<`Iq6vk<(ETEhOe82nhdTEz%C`p=%D;^&^Q$6{-^w$vQ(Ji
z@J<yZIG`)W4B<w@N8-#t>pCGB(SR7gm<EHafh@Oy)+5HQuAnspM)98D!US?)wlxE!
zd_xS7fOcD$8^Oyr(ArK!`DOuH{Q=Ho20EtZW?0HM(8^CpT1Q#GXl@1{0>@Em7~^Xt
zn;D@rP>_-=NE`0*4Zcd$47+R0Kx;c8!3tV<2`S&;O7XfBk>4?TiDpJ94HOL5BDA8H
zZ=kiE$f0L$32E5Ff)5<@pqzv=#~Hi2qLgo-MVa_aGz0|!I6V_E64TM}@(r}I6WI<#
zDFKaiNT!C6_^g2zK9H`b8GJn@H1ZJT8)&s4q<n+zqJo$WA)z*4$>he6Rs;C5EKn$#
zfzH(=!sSpCG0Qiof$;JTv~CdDJQOE`Lzze?qLpu;Rfx#uBFZ<=Y$nJnM3{@Ue1jeb
zhCQ&1u@_tLwS>@!ffrja$H0m$*fapFuZP_hTxAt_{sKIR>z0#P;#gFaSc!TYX1tM(
zA*`1V>Sy4;1Q>iR6xK_C&ERKlT0+~)X0XM9<_Hl>P4FQ<2ocztKtnUoDm-x60aJ~d
zV300<MLtK^C({s|Sut*5LTcB+*ViFT1}7sk*u)n&-oai2r2^vZ1+A3Aee0E(2}(B`
zp9gSU5o`uJToRmQVJ-)|)X)sHx(t$cO|VyF_$))-gas=*VW+B_!55W5Z3Hzlu>=+@
zF+y`QKI<U+kD$d7XlWX<hd@0<i1Dy}RS+LS;{cY{37e0yzX{|Eq_#0W-B`CdnVCRl
z7>KbR+ioW__}P`vP();I=)4EmmvGNReM>}OLiaqu57a~KQa6L2TnV)kGv<i52EIcH
z7HzO|E8%8Cs#I`F1jirLY+^zTZa7l66V@FEEt*44ABLbp1Z)8;vyu^LDBW{whOpFR
z;}poU77Nf4GvcPQ%wP-VKzhI~f&~G{3{Yl7l0a_sn1b!YJ)H$I7%8zrmE&%X!IqXG
zSqFBG8T6iNSn~#*M$58LdqC?+U;|U=Jxo*70XT?1Km&v@4QN9{W}roFpxDFZG!s*-
z%|f_S0$>JXxCEu;3RQ3D3RjQe5NNLsmqRQ}p^*now$PjhH5lCQHh`?8!fcnKUiS{w
z0-Co7hHJra4s<~SF6W@HwZ|1z;1Mn4h5w*7lQkA6LW(a`UxAzmsyV?U(Z(sR;FbZl
zqtwknYrXJABh0zPITM!~QKw8{i3`<>P`w!83GD&n3QwHVRS;W1@rz}W2}?Ml4lhD<
zL&FiNLkM;ytR_`3GK6+aVe8;vu0*sqK@;eZ5(9hf4M|kVWvGsXPRGM6f)$yt!zN+Y
zSi&wUhZP8T6Fa1Agrr<h<HrnkJ|j-!VC4$d*oSqStRtX-U<O^j1v3uTtuchT4y!SS
z5g^Yx>RCr3jKL@)4I`n7qVOq-f+~VL2s%B0+d<&E8JDvVBS)}f9AO5*!v)@7ffg>X
zIuR195U)8Qc@5IfMN<X#o0Fb3+;3P_fj#G>hX`%7f(Fw~D4s*JGXgrg0$H^J3sh_t
z<5>-320iBy)oiqtFt9YNjg*Kn;<v!ZHOL!QsKRvU;?t4qiA9GNTn9>X91<J3K4?p(
z^zo@q34^M3Oi6(S4|H%3mI^R(A1n&+=0an~loZz#gl`>FQlN{A%wVg+h_-+z3r#>a
zKy6HcZJPkyTMu5M1oIoT)d#a0-in1zW5fN5HK8M|BMJh?qLZFAWL*(_1r*k%560Ri
zYlhOi;>`5C)D(rxyb|y}SqpPBO~|Sw*k(6qdk(awrr5}!q$o2zuQb;RRH8w*nwEk%
z;IJ_^(lIl!E`#$e%|Luu0)xdrY?+&dIi$@3-*;sOTjqw4fzKZ!xBJj`;~^KcScd#y
z>)l|6!o%7eQboXa4WgAxXqG^BC__qfEbRhwlpKJr7gVu<BMNJy0Je4xmjekptPEUD
zVpg)|D6LFPM?v&iGhl4=gGM=M5m2#_MQTcVYF-g2QVpRoo|0Ky0^%b@IwBE3A{~(k
z49yHcsQ|hS7-{eTwnz_Y(;}p~4LYO~T#2J^ctj5g%q@z9q6ww7YluZZfvAGj0x*|B
z8@p!a@DXy9go?!i+>>Oei|?>I4iaS;ISN)(!kh)#5DE`VFax${lyDHDWG8EekoW+9
zPrs00aOq%aXbCMHjG+BIOVAj0v7uQ3bVweYB)}0~Pzl-502VPb!I1`_?M;v|V0Xd;
z6gr-TB!L{Vkfyp(3ZBj%>he6e2Hf30P;v(~&XKGHJIM^XOBL)94Feq*jTV$}8$caD
z961toc^g~@sOJaOf!-hoon(s7QI^J7XJ+9>gIeT>q6fYAhq|f_t_go803AJw&q?4_
z72uGA)?U!w65M!@n~)c*!<P_X7GV}B<tp3+kOQ%rfSxm8C!WIM)Dm{DCoED4gda*~
zfEy00$zW^QV5UPV6hkx6sx(jmW@!v57SJt$wcud>NiBr6u8>-spapEm!Hj6%L(?OU
z^ap7MBen1?Q2MMeJK=47So0mW#tr5fcrpih2RS;?T@SGksoiY>>40LWgSEV2;izC}
z32hI77QG?+6~1PTklo;(9P)w@S5T(}G>@5D5|ZzmnFq4~+TenD4jv;!rUQ#$sClr6
zg4~Z3lJA>Xfv^Ipp$!XWsK&$!B#qW+o1Ver21Y6HUC)-FSuxaI&t{<2eAsq9Bk84d
z+p`&L*&fJ#XoQ0)GtlBWaC(N_Hiy#Jfo5;S20EDET?uS@23d#{WH=4Lx#igmwr~!c
zi@{EZI20DiXgLO_g+{KKcqSh#QJPga4KYM91if4b9Ucow@z8N=SjA@vxd+}7Hg$+v
zhT^mhRHnju8<@qYB}%&ory-zX6=4Y2$%uW;X0WqiaRxSVPQ|^e8Ds+y=>hwmW;4(f
z8@>>Q7S6;ulHiVJkaa}3mEeA6Gtjz26lX(|EG!*huitS+A=1ue$o?m5<ekezI-20V
zWg^WXZr3uAu}*l;GLZ%m+Occ~JKh(b&dp%UE|El_xs<fnfwU$d5d-Q#o5N0%MKTy#
z9DvGPPzgq2IRLT22r|MRl#1A^Ok|Xy?^Je4%_~mzO)M$OtblY1EkVm3iVcl2QZv&t
zN+2zKShuh|Go>U0E(Dp&PDw0Dgb0DBD9lWBjIr!&HU}+Y1?K`|cs2$3-q0MhiWFS+
zAS96c0fvT1F2vm#0GW@JTTyl4>=Bs5)|Y}j4R#K+kOsL5%!lMv2#H#-pju(%nh74>
z#2h&Q*@4GpaBbki7}0q)hpkn`=`?fDnpN=HA#>1rQ_%cBl3ILWh!`xxs7OH8;&Cmq
zPOyU!wFkZ!z+EHY)dh<;l&jUvK`UNyr6f|~3{e9>V$K7S;6cM4hM+@ozzb91Aqq(X
z=Ac7wL8;Xc6y{*tps51Z7Dh=HW)L%>E!J>w?tz;R3TCXjOu-7FnqW<7*kUJh*zvM3
zm%)1tq^4Pv#ZKm+<+aG63hx|%ybSgzBm^KNK2Jl<g{<H-hpm_;<XotU(55CzD1r<G
zb)LZmDr~(h%sgD~gqnvnfFTBAPNAE_7Rtg5w=_mC4?vj?92!vbvAGvgL_<dsVetnW
zt_Rrxig|1r@s8T#(};EO9@>XBhaH!T(^K#u#P27_1T)M(u&Du%@wog0pCRxq%_)I&
zqRc@{gOSr2Y_Jxd%*;VY_(F;`(80IhVhy^=+R_5j+lICBu|}>5$TG;nQ$qvL!mY4W
zP^sz)I?fHestqI`Qk0li4092z=ZL6)z%h()5oirA#6_S}X2C8phO8{Kgk4t)4LYnY
zvV_|Rah4gni-J;%GgC@SbD<6bEw#lTL`JY=31txF3=5bwsICY$2A^~X4jPwKSTuo_
z-6FdIo<`uIg3rgsE~zEZ@+%m$4jAe&L&#QDtbv5Q2oEEGz=pV_7MB#|S3=E#t@DMY
zYD7AMG~5i$afA)FjAv+sYy)ceK++9lpE%5J=n@H--SAmUSm0PfH<7?-ya^Vv3JSg^
z6m;hiO4WnYbRtXz$7(PlR-wblFo&ZD8qB>g8XoJ=$i^0SkY!bwkkY|5Cl#KGVFSO=
zY=u!z!7PE%Fz;Zq1iI7=Qc*kS=M|SA+k`sJic)u2!WDxXXyDbHkn9I*?Sk%TvQRYC
zF)%U!WqV5tGfm7nCUek$4J1E6GCn-yKx>ad4FXGOiGbz{Xi~=0a5aKt3aGDP>S0YE
zj7b%9Q1cv@OF$lgH?NUffG}fm9<&cK7O5DAsYe@_L2B(`bvB-M9(MIGZ^CL7Se*r0
zV2$iacmZb)uaOPm5rM^{;40WJI3A@2fVUxFtFB>jhG9C6bPo*`_(BcTb}8!lwNSN)
zMkdI1Yw!v>_{<7u!Dz9eSsBL6N*NI|E9S7Z-j;}4yutBr4qB`YPLc=-<RAjg-s78B
z0U3;x6rjpsF@+^TLZ{5IxdY@NSYX0vv>XC8ANQP!xiQM{HdH<C85MKbYHC~#GBk&+
zt~N9`h913&q!w#fA$NSx=2$@XVGA#0E#STqs2KvSK|o8l!RZ?jMWCQE1jQYg0on)(
zW}ue5P|t(L0uc+GaL=xQj>5+7G_1NYTnXJCgcLYnH^E#98)JvjxMCJ*VVV*0;yLty
zG(kOrAuKi7C<T1%H+=0Kl6LeW1GZ8f=2%PEs%V&N!O;$OKejLhml&{lH0X?pIcyC%
z%uGZb2A%i8>SM@s31WT*WDG_upwG^LF2{gW`_Lo-&o1VmVgi((!KoG51F(h^BvnAH
zMa<HGOaxhrrV2JkgS=7+yHY$Wm9T3-Td9P$CdM4LfE^Z^@H!OgV|a`~;|JB@*w)0r
z(g3`3g}QA35-#|ARoHZ3?M}fvJg^1oFmJ$}4NGY7n1lKQTD3!51Md^T+zwkcWo`nQ
zR6|h>Uo~aTP@IupR03bcV`*lf$&i+lp9ophV`*ln$pC5+rRJ4bS*7J9rWcnKl_r-!
z8#$nbiNz+liAC9|DWLHO&_-<o9YgD+#FW$=n1G><p><JeS!z*nDon^o$Iv=6Cnq&M
z5w6M@ArxN*I^-Cp)&yc+UTSeMs)Q-n+%%A*U~0{D46VWEw<DZmu49O$*#%peZ)pZ?
z)x$bIptE8@c>*qB4qE;W>7;>Hn}a)P2u;Xkx;ZqQ3=Kd|f`k)fRS9I=7RwMG>bbF4
z4FJ!qKn<{FK=eDIF#wwDEw(6z43xr#Q_+S_A>*l-6)b3xJh-xec7x&h2DF?V90)K8
zP~RE3fPkhY<W>;Gr_jL(%;7)O^GZ=Pf_(_rh+bE~)~|!w&rm0TDcG6lXeA|z$v9Wc
zfX>ne6=B%?fxAHhI#L=}c!Avn9>W5UR)dC=P;)7o!!qIP<k2fD)V1s=+CUS7kp2*2
zc-|a#Oe#Epz^*U^t!xJG>oPPqgshf>g%qs#LpB=Q{v%kM8Fn%%%mVmm2|NmjNFK=5
zASiG^rvgE$C1@JMFcn8>hwN&2gBe!l!M049gI04RM-`%ig&v)cwKs!oHu4rJ(AjS&
zjzx5xVaLQl0v#IV*g_a$9@5w#eBc%J=u%`iK*kdx`+Y!e03}&$W<%7WY_|f107yBO
z6b|k4g8L2b@Gd;`=5B1pLk1Giy8NJ`A321P3lBtQAR-bW{d`EWhQ%YiPDDLq7CCI-
z^&NcOFKFopN-W_{QbZ(J^yR;Z!45N&S$t$SfxUvXWH>mr1iEAxbRq(YZi5FM5sozh
z*@V7y*vAO8v=`bjfUS&&WnzTsuoR3Z?V3T1f{wg_x7HVBR=8zWfF~5-hY(tVmYNh>
zV4L70vPLq4b@`y%!{LbvbhIb9MnXs+SG|TNkeNo9lfXxdxZ)WcK|Nv=hj!dG0c-&{
z$j9Ks4r@NZP6I$G4q$l@mm?vgPndP48Ol}{oQ{NP2iKfPBPqoA5zoL1QQBcaggRnD
zjKk3eFd!3Ju*3=*IfunIBsk4M>%T#%z|b7D-W%K$gQ-AIy;#E<_q3TAWUvfg`eD_M
zb^Z)iOTre5!;*xh1#~{1z!3-7qZMAWfrj{@p#od&4YL8BIm}_H4?1)OFZYNF6x{O~
zpv3^#DlFLYYnVe2l`CrUhr}Ax5qL8Jr~(5gV{p>|w2B*CwLl{mQQN|N3NaZ%Le0k?
zbx;eOz^Agp!V_(H7i*kD>ORCEI_&UNnBQ@^jVM<^3`eV2VTYo^Y=B2ImcWIV;!x}G
z1~RyI%ETN{H%D3Gg*`n&+YI11vW5?RlM;~F2FyMETwEg{%fl_rH4$4%k~0#Epu(1#
z3`vQ_naS~)`NcLMah>9l6e}wbE54*CF|!0BYNr4_FTv0NG^ti>XkvgEdN(vMG1at=
z_l+;fOoeuo42+C4bu28+z-vl;<I^&8av(AmnmU$77CIIn<si}c%)CtKVii#5*3!%X
zs-?K3C^a!RsWc6u4`iODr4dvzxumiHW|cW;{|mI#f({BAnuAu1R}_Plfqi9XrDJGd
z1X<$Z8=qI28(&Zgwbl@H*aWmC4_ZJCD)tP`LCcdXilGX@MnD24AMURB%#zfi#FG3X
zh^zvxa8m%=Z3+ttkdf)B2)j)nr$ZQ;gO(;k?Kac2h1i{*S^_a0Y<MP_c3W70gDoCh
zfgufM!qydo;}oTu1+Dr82Pr7F!7}Ec(<~v~bWke<>`=HQG<O7-Si;kiXL4|fp&?Q-
z@=OMw(}R3w1$xg6x;mM#G2k98sPuHygCAq6U;@o8u(h&qUl|)f59ct4t(G-}9ghR=
zvJtd1xh$Xz+CGCdq7`8lLbrRuEd(WGa6t=QDM-96d9c~nqLkv2#1iOnPv)>Csc;(%
z%|S~(D^lY<^NT?ktQHbaB-?2YwF~OI<eb#RBACyh%|(R0u$s^U+T{hg18gs`Nx&3r
zzH4xap%KX8APjOmxU*E8T7u$h=%gnVZMT3~f!zV|zVR>xun>TZ?Ga2KhK5Lbu^XPA
zpAX$YX%1bd>`l;E6Oblo#H8k@!5jtYAQ9GXoSL5o)t#1@2|e?S1lvKHp!$;%Np`$(
zQX=d``qYZ#ROkhWprJ%VP>Kfio(X%w&;X$q;uJ{EA~s%-wPQ_mn8S9kRi-%89jGn>
zxq{d<?`-565L{wt3Mm}ng}7&OuwguoQ$Ee1r+gAjA<l+`%?6kINF5i@qFGS;52puV
z1r|Zu@pNx2AuWEwMnJkakUK)bF2Zq1s5#LlB3~H_H4)S@0jE6BiAUgp0<Z+6=z_&3
zsD6NmKsvvKQ>SrBW-208TEN!IVlx|7VuD*nq@*6O9Rb1l;3Bdp1=J#f`UHA@Iovk#
z{b33=7uhH1bsF4ks6SwQV)Fph1_JpYIlrJXEf>0$$pW^L7Vbeq1K5$_=1^xCg4!j>
zaYit`nM3UW?fQW>Awca=NYQEmTAGWJHQ@Ced>#WlE<!kIn1D=04pH>F6lOBiCr~c2
z;R!K58Js9GbCXJ;O$7_sB3pQvL03~5!a@wT&5l@$Ou?puLaW3qCqFqcClzdGUSd%Z
z?5s-**x9sjOQD{DWhe+2Lc+=x!Xan|vBNbv8Qg;_&rC^$ZW^-yt>(oSO{7|73O3vo
zViA@afylB9JRoKW-b#+%wk5Xkat0NLAS1y2B5+10x-KvQ>A_bWfXt2ejZaQY&Pa`i
zL?$%d!xksQV-Vgeh9*x^lZ~M%nnfrn2;>mRRilu;BSU6>F}QmInRNpT+7=rc8bNN-
zhRcG=P1rr!N%{FXi8%!siB|eXAaM)ON@#Gu!~(Q-8QM5@%Pj%V7FvOt8^I+OFrzHt
z+Fh+vQj?L)KvQU(lA7#lot9aVnu1%ADOldsIwQ3Lw<6<=)CyPY%)FA+qP)Z$-0Cb~
zN?fgTQqxLsD>MN~yISWb<56RrpIqW<T?E+@0uLuNKbnH&U9F3gGgI?QGSf2ggomL4
zLY1p^aYlYQsCj|gkrpr|5XA-gpz|ycF^1+6OOR@?8kq88+(w&26c@W%7iVV{l;crp
z22tQ@U7D9!0;)lAI=~#N!qvL8pdhs<Ik7kuw*xHUYFw=oQ?g2nOVTn^bMT}SLqjBG
zuGXNCBcKD+qfP~@akU078vwI$I|(#7o}UPD2grhyB+&2*G~$glAt%#=dTnR{Z<dq@
zQtE1*nxBTM1zjOTfvYvBbAVT+IaGzKHK-SZUnx|Dt94>Q0o-#Y5a&Wq8b!*y#)$<5
zuGXObF)SV-Lu@EYN>W{|lQQ!Xiz<-}ME49t0Vr4ER%Dc!=W3k~8ZLqcBc^5fr6sP`
zB}Juqh>U}w$P_H^YMqpt4z~+>4XQDQV&kOLbXV)-(jp|KXl^%7E-iAkPR&CkH^_J^
zav-GUrMQAd+6szNlQWAm^Yb8sX`nMep^FxL<B|4;fyANvz<lE&#U}XbOG8kJsskU*
zgZIcFrhx||KzE>kN11%%;T@cKNN*lu1SrkvfKD6%?J@(^K{+`|iOJcpl@%7C{RQA5
zCkxO%f{NmJ=R}B#_#DugCZKyaq2`z87p25kr55Ewt+xPeH>fC%56>@3DYnuvGJ>3F
z5e%u};=#&`LFey)9Ri&VMhYcEi+BvR;7|kCkYN3An=Fy9fH4OhYHbLzUdPD7LdVq5
z2o%<6_8DsGSQx+tH9>MPvq0mhP!C&Zf^HXudJj}Xfo+BkaDp0e7LZseN=?r!E=etl
zPlkpKwDtz=Zh$n0pewRqwYLRmO9I#u&=4)SoooTxApjP#04>E&167^SQ5}@=e&hf$
z$FSHnzzuX#n`vrUYF>$JP;h)E=o~Fj%Lfv*a2G;1Ygm8|Bf;rJsKY?Ro~d9HQEtmX
zaS6Cpk&}Zk-xYM)1Xz=+HK<t;?;D?zSzM4`oQhBb3nuu05|&^By8+%PhPM-p46qsK
zS_ZQ#y(qsNoJ8Org)W~5w_(7BT7cG|gNIJRNd*+(pm+hxfrA`2UWjSCp>e!#Ji-ds
z%p{n-pr#(eTWCdtdAu)pTpj96NKOW~B`rY9-cjQLoVUS_0<BPoqzuq9Zg5iyo@}6Q
z^G1pcQ?R*6T^!i*Y$UH6Lc2|H55sMP8i;9LaEYNYXjm{F)^r2+H;PkBpu=Al#*pDJ
zB%LN8S<t8(rbdXzpy%Ha<uE)h#9<F?^=WAy$Q{sF0WIc64QOaCBPJ-!AVy*Lk~N|<
zhsG(qYY9!)pmH4Ianxlv$gyV$Hw8Hc(Q^pgBxrvh#>b33B=aD%Sde-J?m|!!MYbQ*
z<toWQwHu@cDO#aQAjTC}<|W6MXO?8d7nh`D=EFkSLemyhgjrzJpoXSsI$V>ijU4T*
z!QEMyZs<HXJY9gwT5!NYy^GQ7ftL$7y2a_KB{})YP~%~X#W9VCZmmJ;3&RaIgBSq`
z7*Krz;lqrCuIxq_2|Z-U0*{fFph5)fFsP{&i8)BFG=c;nWDPe`@S8!Df%JgF4P19Y
zq7$~f8tw{1&=fV~z84ep91kwyU_M1M-ZT?rBDC5l0I^|ygznNpHr-4UY&v`@2cC&=
znhrJ*YC5z>j~D{AFoKOA!^0i-P$o9TpkBI>BW%zTH2rBEAD@z%lUkCQmmZJbBGlnY
zs9sQ4ARaL{hs93ZgA3RcgB%F14#C}OWRqdbpb^1~Q80m4??Ngp*ungm`5m$zHQw3)
zRO!c~4R=^T*F+;lH+VllFho6U1`WEl!veG@7_F>?Z32Ktw1o-kpbnDJkhPBxqftf`
zAWN??PEs^9Fg4U<$jQ%3R{#^(6qrF3q~w<-<)nfq%MA@cYquSPf*d2^*9kZ|2D`?G
zI6C>bT3O{}=7DxPmL`|L&kHg%1g&XBJunD#D5fp81A}mzMd|rL7NC{Y-~a}>2i)+0
z#W`q=GPur%%b?^a=z<5tp(cg~2xl4^AiPZIL?Mt9K*c+$2BK|bL&_nbMbtR`1ok7u
zCl;U;&R{QEfFh%!7~yD?W+tgF10O{M9YMxeTV{cFjyO2gc!D>!;SE1S(9uM2L(vNb
z*ivM0vI2V%DH_3i3(z^%kgN+@Rt#>#!DAMhiAncpkT>GsZ1kLNin_^*c#k3*3ifD9
zNj`Mq*#dTqG;WWQ=NCMMJnBkVViP>}6OSxl%cF6-ACfl7^99<{zTngnM^Jr^ShEhY
zpNz~#@JJ*J*ty#Xe?j~KKP}e+v}_rY{y?W-gVP@<+kq1&dA>zDI0>3DkPl8G)2jpz
zQX<n_;to~<rEhZL#{lAU&;=OaR+%333@OJP=-fV;1`|4X$-)fQfr6JM7N7;s;D|;D
zK`&!41SJ9rA{<hkL*g3T7>BJ|Mx;8B?O+=a2@h?VHHn2B#7<+()0xOj2k56Y`R13U
zCMM;i`ljaEWagE?7SkDm)>0K4nINsDvj8o{t|)d&EKWtzWQDoM$^y1X+7M(4I5c3j
zff@9OR=5o824~b9XlRIJgJ&jaJ$Ej+vyEAup{+PZ*J$Vp(FiVwpv`#DVq!@B3|(dl
zACxkKtlTBeQKn!sq2>prf_VrVka7{aZ_J^Jp<3OFQektXpvBF2T?Xr|Kob$V$tEDP
zpcycr1XgFk7APYE-2&oILkrN#Vn_=Fw6qsoC?drfWI`E-UB&?=MIfsnr}9|9mhQqW
zF@iOVEuf)F!0j;ekXOzbK{ruYK+npCFZD%>=H#*f?@Wke5X<h2jA5|>8Ggp$kL0od
zuvXCNoaKoHFg>u53#8xxr)0QqEI`Y3A>|Ar8G%C#heamA<%tE*Lu0^Q6L8xCsoe-#
zE{Z?)EzB(;-AzLab7=RPV8(&matG;gp*N9G&pSp>O^}X}VLW(b54~puTE~j&SWtkV
z2N5)#;ddmYh=baL*&~J>XADnS=-HJ(!ot(ELOshE-T&|=lr@7>PJVJWIERDQ)k5cj
zjX=Ya#YPs8A$$W310B#w_&VSr^(2snVk>>PC}dV6ttd4WJZk`!01p~K2GEldk#!h@
zPV%%g15I6EDPBQqfx$r!asW8QVZ|$GZ7?`H!(_k?f;kLSWue7{3984Oja;*`a1^wt
z$0{45>oi2@1gC4HHZ$zVV?&s$;1Otn{j7d8v++eBWONOd<IpvN#u|(q^{l}$2hG`_
z)yycN2aW|$*jNzf9B8=@IaVE1;KB@s6&bj^3AYG;BtQ!B0Eqcum%~ay=vX2S-<m<x
zx|V@QJ|H`WQLh+uFG@_w%u9F5uh2pa1Q>x1x4?hTprH}yVmn9*#wZ711G6A=z=_eq
z!U9@IAqm0ieS{FGfdMW)EkNs{!Br1j5;cI3Zj1{6cT^GnfOPI51qif_1T8?2%2m+f
zYG{nZPk4d46k0w(`U+rQf)WCW)<Z5Hgft5=w|ZMxqMS!g&@+(R2cf1R^_*bm78}At
z3F2xC&>C!TrUwNi*k4fh!)i-{R$|)&4BJp^0b0+E8hRF%&_Pc#=(Zz63s{2!;v}g3
z&~!@BddLP@$Yxgy*x9xSZz38Ju)CB{Y$4j05NlBO%7Q$H)KDYnQs`zFa0v<DKnrp*
z$Pl6(f^GAU1$=EUJbK}UI$9z{9c?5wkRf}35Stu8T{URI1z&Xww;B|~;FJW9SQ4WY
zeyboPRxM$N*5Zp*s1-y9Bis(8oodi=TTA$QSA>_K%f%3B6`ro3784tikkSU4L9L;i
zl=9Ociwlr8#UO1l0gEDbDj9(q%f)6ENIR8GEDgXLmC`bEQo+lBKn({&OVCla6~&O{
z*-$C4S&%J1*lf@-Gtn_IGBUwzH(0`srZoiF4~|}F60-!YX9X7_2pJ=2)ock`cnU73
zEn#QEBI^e?c!Ns}O(1TB1p^tIY%JjmL=ldFWOR`8z|IA^1<ZiP7_s&u@5;fb9xUN&
zL~+_{01FHf?ZtTtg(ZBMC~i9;E4_*LBBZ^5={@)YO59dKy$7AyKs$(tSQi+%26%(E
zRzYUE{R>j_a`RIlOL8%K?V#(MAZy)mIf^p37~*paTJsbX|DfWVNPpsPjDjMS6oVj*
zQ|L?`C>(HXFCf)ilvzB8x!^_gkkZ}~v;-AWpTjO70^Mc>*=7hTZ@?MM5_E1YxI_Tu
zdaw{Q?Ryg$PS9obW#ARknZ@z>1+X>JmayYv;pGTynF&0jL+v8cDs!mupaB4AeFIvv
zh2lxL6^5w&W@5dAYzL&ifv)(r1T6|fu?p6^f%%4_q-+8TH{7W?IVZmuI(S54GBE{f
zg#?*3WNs2%2SMwzOw#HzOHEtU`pnQ0vi1#9Eo2f^IfB*{Kr1;&#~EDz;i=FJVXGvJ
z5ZBv))2bzW2^&HNTFAi$3lM1w=2cvE7OY@J8$7XuuWduv0j;lKi#0HkF@CEc1%e)y
znj5rO4w}ZGjX_w=4YvyFJwk~MzY9nz!$HgS@H&bzw~$nZL(c&wGE(uB;YP5<Ca@L_
z-T;G+5W&lEOHEv5IEm(>mEj<Bk;`z<`bgBE2bJN_q;Cq{w{2-;23e+Th?GFV?KPxA
z53l#|l;KA3Za*~Z!3$V;ibQX~;I#_5e1(?bMzHmYFy|wzKs&UX$Y@8l1GNkXt%F1j
zPqh3+QBo$M3@0s_U@61F<u3YVQEo-~c_rXmpNw@3VKY2Npb=cu1t6fq>9H;VK{J5T
zRUe?kqM^+{*zJR`@CL2#1h+dt&I2cCNSO*PN}+u;q)Bi?B)7TxWEz6^@<SHpVKzHa
z57fnP3eNQ%mhjc2a5o~k9?8w1LrkIVD$ttCiee<E5?J7Y-B-9Tm;$*8)b=DQe85XN
zpa%<L_7{xd!<bm&2JAb~A;;iFZ-$njV{gGjcb1@IZ!3zyi!9)4kdXWf8gB$^CM_aC
z=e!!kdj=cEgYN~yu_qaH+A;~g$6+XXzW}~S5@$4%;1@hqj){RLv5tq<H+t46r!HBV
zz}8;kbScT1h~P>Nkfp>ro8a;dOW0AnhHxK1Tx<zj=4T07*$EkjFoEvAF@#-}2y4<2
zllYO=Y(R4WWaR?1bA)zYFgQ7b7e`@cNP-JBND3iih%=xEi9%P0Lc|awb(SO~6~cF4
zkzyL5br_(NYtc${OVHuG;249)DN1aTS{gux-5`+$8l<;`FBe6aZvs6}1<4}VT!bZP
zDJQt7f-5H>`$8;-j`<<qkwr=*qc6Pxr!LgO(k(GL6;xD)lz_Zw1e*FptD-@N(qdbq
z2`+14+9|G};ma~XPJqS~n6iW~%Y@57aw@LCw1hhhGFJndECG+7W7f{5khV8QHpVal
zchwAEdI@(OtZIfW0fM;J62A1(60-CX?n?X>Glo;ZYb_y(5_9Uu64zQw3}f(D$?(;c
zIDKUaUtMVlUtNi$7~JY79Il{4;1PE@qW5=kt*6A|8N@ULw0(h?4Yq`@phR*XJZXU$
z#3m&?bt<kkljv^6QK7<DN#b-PBpDIwQo^+<bn_p<I3iq?!dF7V-41apDCdHM7j(ci
zwD|^K2?@#tU`b+qM4%oe);okNQDRLZZha#t^<izbpeGC>s!w9m7{R&|zJ?JVW0vqW
zj0hoE0S1ablA;PycR|W1P_1GKU$uxZ+zi^IMkEqKs}|7%3}Oqm8j{!;M6Vv*Q}c6!
zQxl7lGmN1Z#~T`fmcA4lnwUUNUqDd-@*3;}hP1@wROsRxL$H@1YZt)V_u=B8rX^(Y
zO-5pIMhR#UBy89bveFT?eunTe^Gb9S4D7(`CqVm-4UH_Ub2IZ&D@q`GjZIB8!5e)I
zEsd;m6Dv?9ObpFH+l8SLMX8{}7C;kyU|nVg7GTNLoYY*TB}Jfk2kp}Z8Hf-zv;-}(
ztSBzYC`wHQ9bBLTI)l*C%(^JG7<7go$W%j39YX`i5qlsXCg+#tL0t)wF*E`%RDu@J
zh~*NNpf#G1!4M1Rjwx6j3R*Y`?VQ79kgFC$BjgwXZwmsgzQL^Np@*(QOFJ}eXv3gL
zRR=z8cq#+D+90;UM(`|*aE{>NH3fCN4s41i==ezRt~5*dvQ8r`AqvjRhz2Vtx<N@9
zwID_fA`_6=uAsFrc!w}RDh%U6-52!C3h76KIvc);6|NP~V~s4~3q^6c(*!yW2BVFk
zN4TT)z))RlXn?TDH3)w3igAi-uu(jyFCK3RQjnTgVw3`Lm~UpDb3S;_JT&}ZXFlO_
zmofBgcbL2Iy2lc3Jmgw7$kpS<DTt++zKIp+_9ImqsJ^p=E5>D-b5SbjKnRFe@TDR%
zh)U?3jAKd)C|IB!Q_%8UoS71ogkVvD>+Um@tZ5E49O7i8;D-#Hxu=%+fDTlG?i{xS
zEmy#6xv2?cVU>v)WLq!H5wLi{by_Woqd?cOK&%C)5=7EQavx+AD9A6^Fy1p0!pQ_D
zA$TN0j+2K>Xo2esYzyHaW1%UCo)*Y~w)hvpS%B_shSVcyYv3$lM*|yzi~&W4p(T8A
zFp`i3bYLDy2)3Np5;PbK?w!IVQ40X1rAf#+2pj|`RWD|F4(ddLV-Lw_s3W0EiNFm+
zu>UYi9()#q`f_;2BvDt_;x!1l|AScbW(lh9QJoHUs-Y!(p(`lhz@C8y7napS7~zF)
zxtk??u_`RI;AM)XIdX8J6*2gH2U&Oqoz;LZKZThO%OhY<fxQCpBMBaYSb(y!4df4`
zG7F#cur6u?og@y@PfU1XTh9i%Z2&SL1oJ0+S{x-9;l77@7uqDi=U(_qHb|4!8fknC
zer_<#Qq0IB-X8e+GFZ&P=A&SS!`%)_j^LPs8cs~0!A*zEM1jw1hOJ%$ooI}j>_O!T
z*alc4gxd|Zl8A7F6cf-uvt|JA_5`299d8ux;TY@z*(?khc`^odK#GkFK<!58u`rgP
z1-li+U{&xzE2w@@L8@Z_Iz$J{<TYq{FT^*nUIwf^30jy7Zcl=&1A7l8=|lZwWQ5(R
zpwb7&NRlPWND@v1VB<&uMVWae!6k{Xb`X52E!<sj7sC$4wgfGUg%sFOUzxxb7(!zN
zmI!d!ZV9*5H7GbAyi5kPwF-4)pE!>g8Y1b0jvad>7H5Pd=D?0$v;?i$MU4eWSX)9D
z)tVVV{Yb3y;TAv|5$4dO;A(B?q-PzFUz}M2I@Jl%>VYrng$F3A<&ahbsZOwf+2RUq
z1E4qoS`i|3AK*)D;jVys*%Iw&Y61xlPgetaI4&UrAe|09YikB9*$I@HV7bst)3(?M
zX_=_0xe1oLWGyXW3t^1Spd;WApBWi|7T!X$7(~X%0JJt1G%jys09suNO~VjLv`Wns
z>^f*e3v#RiwD`d6(m{`rg!&v+CwToOTqk&*4c^~0GJr2j#pZZ$sQ^uqSk1<@#u98U
zs0PET3l;&;M1t%Z*oIP^u7M<7{BDA@qtQdj$iM()zY2ON!E|AS5bWF$oNhA2elZ=Y
z1)$+wzu<VJx)s`l##)LR85p4Kn?p4V6s5S$LN3JcdKztD6n3MNkpXC}F|@dXCrwaV
z0H<gW15#ud8bQ}R;Y+#T3!O59ydlBsn37@$?o@#-!IeTxK*~Vn3Ph7<UNWdz8c>v4
zkXQs+aEI4lh9;;wpoOwyN=k5I8C*Z=0R_mJ4b(vcsYJK{s+9<*Tfmf}Xm-slD1qE`
zj@RKJ&0wWa7sD>UGBSWK&&HVuAsGWg5*07OsU@zN@Q8tJs4y~sugHd51vAdb0JL%%
zRG@)NFQkM3u@;&uVRbJ`nuRn|!56w%gL=4`d9a9pFOi1XXJi06vK3^Fp%L_?C9us%
z_TjV19BMo$44w3>K`95C;^2#KVKzaMF~V?s#v=DyT=PnbDv=F_4Uyn7m~i-5!p(%b
z8@!(^u_P5V43U~wQtY3X3f?XV^@{=Iv@>kwtC0yVz0ko8aNEZ>HP<sQ4RT<XkpXDM
zF^(J$S@URW3Eh%_FN_S0kd217tYAHM=&mLs1Na(bSa_LQLWaN*Q9(Gw;QcOWH3DyI
z;H`XcHXrcof>l}A#xtM|AEcH8^mb7A;gXmago4hS*TFWPVPpVbJq;UFGBN<Iat1XT
z43UK3;~CIVKqCXtf?SX)BhUpbkP}^D>d^{lq)G_XKZkk%GDQsOs6eL@ph1gNL&MkI
zA{h<Jm_`P$>n_1D2lgMq@eCA;K|Knb;~7Q<(8YPs1dq=LkUf0R77tP`g|Fj<J00v)
zBhUsD@Jcaoz=1pi3ockmhR-(SaseZu!<UM}tTZ!#WGA%Xf>}s}?;yz@+$%ORfFGZU
zFdx?TgjLO8Kf=r>#zPPbkjFEO42)1(PI!X@R9=CKFh|6lUto8F>Uv_Vhg1~c9vPzg
z11+Y76i?99ZV2-xN~%Ne!w?aiIL9-L4B)G55rK;ydC<a;2z%fo4zQSmk7q!A1amt$
zIg*?RLBlYh(j8+w!^i->ycXsSaCri<0iFn9cEgeh5&nS`6VOa%&EV(~Qk0tNm!Fbq
z2Hl@;4C;0kn^=GjBM0|GjV)mlgy3Vv5n_;HFfFkpBee)23Yi=%PAvclf$qZwP52ob
zS%a^%1eGktmKK_zJ^J8jyMj!RGDA%rW5~o>UUEiePD(LM&=h<_SW+VBx-sx+&7fW4
zpcD!9si7f~2TVZfTrtK{j11t1q=Nhg3P5NoGz5DM<R;YgfMP1n2|2L2xZDCh_a8Kk
z2U(5g4Dl7nTX?k@8X>C(B@E<)J@Qh^!L0~GBj_30*g_$%9DG8GJ>p0Y@HvK%R-2)b
zF-r3p#iuDbuHgOi_SWc2p^*X^JckSIXoEV?C=0EO3_yoFU|VQ~ZUTjCt&9vnOP(Pm
zizOs;L*vxQ0JP8;+z2&>$)FUa&@h8elz^rKQ33%{nS$yx9Q`|El#S^GjDd}nVb;gS
z@E$YFm9Uc07}{V1yV}SAv<MpN9eAG(dHR@u3&2aQ;H?7G)k#JM(38rE3ls2k1KcR|
z?lyb{G(6bRJY-}5Ix_*%(1EXjMp8_Yr;tyZ!|)VJ5+@Kope{M$G(51UkQ$_*_1QR6
z8MGuJ-racGu*Q%pNr;LFtjn*AKzHdt=MM0uD73l)fAFA<G+-P}2euWd48(65p=DS`
z2Jki6@N@yo1&~Z(WB^+83`s+vwat)mI8X(JS&HL#3(`_7Xp)AMyx;<sG;a}FjzyYj
zL@miOG9V?v5L}i;nsEe{W*HfP)|#W_8Y9rT<B-*Ju!seh)1Vv#j#3bVq=X4+#z10D
z!$8N#0JLrz(@v1NNH!TmCfv!e33^Nl(jsQkVikRvmM_u)a(SsG>7Z@C&|Vd6tC0z)
zZ;yGH9G3PCd`&k<3&_jxx)5~oFSzo6$q;i$8*<kY_aSmd2GB!+p=B28IZ&u(V4f#u
zWB^)O4vsLG(~unv@&w6Fg&r{kIl2ydS{y7pqZF@roQms|IU@tm5^}umMN3t9EP<R=
z1lb`AJ8;ejbcive{DudJks+kUH!^^&A2%`pt<?sXdT>?HwkxcUhR0smVR7KY=Zp+M
ztGQ8p1nc}88Ng1VgPKh^ieW}0H&+b}prH?4qE3VhK}XVoZc&76fQNO<6%1hmxTYxm
zSv+1%E&~loKpW7YBjjLCflUe^1s^D}!M$u`09rQ=s$mR~lpxpfcx;6oRtG7Uu;f(G
zf^(E0M^E%d2B4MbAnU<V4RRXs=?XI0WCS^S4s#mC6mm#9;mCv>VFxt_7737Pb|VA$
zN_1H8fI|%AP~<R#F8Uxk{lLp}$dP;K?J)S#a+sy);YNHQ;c0}SuDQh%&+rzQH3RN~
z+yq{bgWCE;73A<$=q8|LQ;;SNBxFJ7@Inf5s0?YPqX~F}C!}P=Oc-V;6aTpU4Kf2;
zK@M6D4@uKdr@`X}I=x1+Q}Gn!W+=nwc$|v6Acrp;#}>$-&?Yi1nYg-QFUUcwKu`k2
z$N;o>91?xd%~nPRpw-eKAtM9Onq)|kjtD?-fkUXkG;wu>mAlX)9li<~<~Jh)`0`^D
z6IdG>8ZX5A6J`r?>23tMpxem63^L(Kgky1(?nb82!WnIuC(3c9hz5m;t1GV39lm53
z=6&>}Xk-9dtqUq%4Uv?91Dimx2#Re`J>nXK*y{r>rI5zhLFdAvL_8MjO(3WIfn%Mt
z#73ZWHzzh{;4R(Zi&kO50}e5eLyZie$;QaQ9Q8C9!XZbXbce45g;|OoZp5clJf*ui
z%CG>Qq64XPhcwAephKge9AsN;XqE}twP0ibTCbW|q+y_Au4!#xZ4A0U47`Zf05rad
zrTT}TRSPl+6sT~InnMOl&^!uV9%O=N3=;J)TDS(-ycg!c6X-BpNFs%n2k349`#{4$
z2N#XojhUI5kcofHD#je80gmKMBt7Wa3ADNwuS*S0pcf?Jc2bZx>^3J%H(3}UZy`r=
z6GBsPei3AOn~?$bfHncCfHpJXEhSh}3BHCH9^KH<4A^=jboax`P?%d%3qjYc8pMNV
zRXkyhVEFo8xUoji<3qruJv3b*7iuu`j67lK!V_{js*wTe0ksH)uoXg{kn^B%D>R1s
z-V;_I;4;w!rVv`%gH|J>MlYr}z~O}>b`3qD(^ZIofsLsm1q{5-gLI`}W}$~PG-RCg
ztPvpt>1$xB1P2XBB|K<wsRV}&NF_XMaH#|b4oD>|aNzwEST02@=76Ohl)yoECM0x_
zrj_86_@GH{lyEmgF99ISD_}tdE#6^C12WABoehI89fpOtDfBd8aFYj|67jkcVwe+X
zO&@YYIy0|CA==O=7T$seb&7~;L4!`wg?P>s-UR_U!^i-%JQ&p8GBkn85YsGmG&G9G
zbCAA~frSNf8G>4=Ks94)GlNzI<8&s-y~H~XPh;5<rGUfcINYsd`1)g5aKRG^?9^I(
zeLINPu{V!F>z7gdZe##jIt+;$Xgz3T06U-8$N;`(7|A8z!VQ+>v3U&EVuiMg;p=!|
zW*8ZOra2*Ynn6Yd!AS%Z_Gk$opYbRSVl!yog&tju&w0sZ0iYnla{RtI%n&@S6_D0U
zoQ+`kf?b$j(PPfY05ow2X#_*_0uf#&G`3*~S~QCiuNd|kA{Nm?*9{S2FaBmP$X2BJ
z2W)YLuf1ysTFi^$XmH?wQUN?U7=fG(5+TB|_#3*0@I|}0qJ{`7aJO(l#uEq&q^2!K
zd25C!Zb2;!Vv1Ws&=I(h;6vS`X=DgG02h+IpfW_2Xl8gzTab0QJqXr|rLZ-GuZlH+
zIt7+6L5>7PEYVKGUDkpu!S6JjMXe!xjVLbn;ZGGHe`76a4dE+7VFnu+!WM!Wnm~6j
z7#V`rae`vX$Pl!A6EYBi2q=6dEy!lng4PheR1;yPp)qs^pNS=;VF6AYpl~PbFPP~l
z<*Wsyhy-~L6y!L{S&-vE#jII8mQvOdYJ?%mWIkHy0W|?S9*um{iIE{_ZU$$<2l>s&
z5VTklQplo(CbU6?#WsRvEPRb2EO^nAvXLQd%^^5CK`tR2$^?p7Lwr>cD3I`$u%Hu7
zQ9=qFG9ZV+(*fAgAQ8e&B~ZYEmQtcvh8{kIZNO8$LeI;@9e?<WR}*;gY6>clh$>z|
z2eN{_1JVKwAP{9_2wKhw%Cv?s8KTNC6a2-i5x({!STDBX6}~(Z<`j6C8$t3r(N4ot
zyc*%ls351|E?(g$uHteZ{xktH9DDH!UnB}M*vJsHt`ib1&`TbS3_(jEK|)4`pmmAh
z;uWSIW-*Qu3}iE`0EDz-4dJJI!i+F71T7N;nQsm~WdNKuK#2rqI`-s>Y&df1Y7CvU
zGlVW3#p5<m>1u+dbTx(!RDfIyDxh&Y0LM74A$-9e%$w**%E%D3&<tF<A}ImaRk&><
zSh~X3lp&c33Q2g78X1BXmw~JW$0kUQa3~WfT~XJQp;Zc?Si)Pnf~J;GN(gYsfE<SE
zXpjhDrxGY#K}*q4EJKeL!ZzS3UD4MxA~jGw9YFzsRJuB47N;hIwo3XJfXHI#0Iey=
zg~i53`K2YGBeC-mb5lV}E<rg1d~8c@4!SU84Qo+qT4r8qZfYKeHppt$wA9?>{1muZ
zmS#G};B#tnQp-|vKsJDmhB2}LiIx_p<|Y<pr-Bw;f((Q$Wi>Pdy8vQSd<J-REa+%n
zLrd@ySEx8#*vQBPWE#fsts#7w9mucHSOrl=hM?7Tpn|{<Nd_fHn4yOn?qOw+HON^P
zQ!8vB5!=R!u+(H5V>`sY3RBR6ApGY<n1jy1fJ6osKf~5sgA4+B7M|`v>+>Lf2E{RG
zA&?>H08&T}1uZ6nl($exwB(7p9NsuAHQCcA)7aG&z62D{B11!Clz|@tMj<aUgpU$K
zo1mZtZIDa_b)}IZXk{D73x+WN8W|cx7P%UMyax>d<haFeKO|d2moQ-lFDQ0UT!0+B
zV9UT`c;G04%t;e(HE1Di0Aj`rZK<ZA31mDJT4v+-1!Q{&bZG@*dx)V4Y^W3#T98(U
zks)YF7$|WV8G<h2LUK8*PQz~>z6~OV@FiC;OU*4{)<UBS!X+umK}JI%L#u|MrCX@6
zVrmMRcLVzj5ic++i1QW14wPLYAkQFWb^@V-ZJ!9pu^<zOa{{)VB8KqgV6Xtio(K(L
zfdKP0QAr2BSp=GTK)WuW*$=*~3uZ57M4}FK5(plU(U6G_aA^cT5)@`U+yjVMgqlxW
z;KBE!APwz9=bH>ctHe-}47ex(r4)Dp0=J%|@PiZ}(7>~10G01Xu(5eF(4bzip;-m=
z#0yi<i4Hg>Kn+2w*T60W=>j<n9?PJ`XrRn$h$Mqt%s`80Bk)2N$kI8C89k8kpkxS5
z6G&QMwH0RJ2Rg_Ur;9)?gq>T07GOw*8@WP`Pr~9J$kGUG?ty7RoAfjUtv(~lJw&++
zW(#_`i+U<4l3xs6Q($^9Tn4+m04at*0c2zdEtg;}LZ|VD9Nan#C!(xYLvtcdH^N#9
zq`DF2W&&=7*^A*=l-2;cV_~{5LKJoY1uoYbnj)6+<ID_@t7<`ejj$v@lxcSqcOh#-
zcNl!R9Uh0F&ZlA~ap<`Trr=Y<Jbf~aTwNjO)M2_3b=@7BEB%7wv6+G4WY}r$c$^Gr
zLlGaSh<zdG@r}A-4$Z~EhVd|+=#4$lGCtJYZDs^H6cV0`2s;l_8bh}5fh!sK3Ou;q
zF-#?xz7vC?r5Mb~8k*1=624vzX1WEup$g6TSerVCKuOMlOw>c%3Tv&wOfxfsEPXLD
zgfEhVZ)*g%J7HN8RzIQ}j8qXS7{bPeL96Rf1KP+Kx)cCp52)P2V{Tc1cM7Nycha-=
zNzF?y$$$j~dhr4aR<Pso8)^nI3B_3GA`C<5b?lfvG>2${tvK+@FUn2K0c}qQbvlg<
z;miGCfeBAMh~xxqU0`HTOSoYmCqZ`oLWH261uYyz4MLbLMEDkTEFr`+km=wFez%;&
z64aY@!9{}+(t(0zpm}lp9YsSk(A7DRo+2!%!rDLZ<$oY^K(S$D2w(1pBm|p}KoWwj
z$}<G5e*^a!VUn;^0u4XZ)*s~jM57c)j}ekDp{*p$mIi#48<NpbM?z92G#i0Eh}2sl
z!e&S>3sNFr9H?$&h`RC(pBEr~ET}<9eJs#IH%MB5-P#V$nns486>Xqqn-M7FK)!*6
z7c4E~vkh+#3%*Vb7F?FFGer!|QNjx~f#b6e()WS(ui&f8;O0YD--0~__6pdKq<9Eo
z0cz(8b>$mA=V3ci(8v(FN{yKC#M-ffFG7R)6W-)T2}aPhJ77-}ADr-B6*Rqox<1gd
z12ln*l3p+)k9d3FT^?A>!A@;~`3UBAM9e`ACnnI~rX$rOumS~stR>7QaDf5}CL>rP
zgxL*CCPd^`NI3xwG;4<BlFEWq@cp!~E|obbZ5ErFm?#?Q7#JCV>@zep*0hGQjSNA@
zVOA7_HO7M)LRO$l0F&c$K*!If#)HKny)h%u5wnmn%y{4Ul++xEFlgWrGKd-P8()%N
zT2PQ$1d%ZXt?n^|3}u4la`MYj<sjpjAUVj{--h679ki$nQVxKYgn>#>(5w%1a5LUF
zzC1G}H7`Ct8)AzE<g{-Kkh8%u5Md)j(DE(Bf?6X}Q_zxHkZNNi&>laqhP=e0qWp3s
z9Xduv7T_&_NYcnwnHU>c=vY{1rda8N&k%r4;{}(Pf<wwP+0`gB#nl>omU%qnuyBw%
zGsv_jk}^XBgc>a83>bozn1NG0C<4K;25Z`Y>NQaC7{X+X3_%O5z=M3|pb8DCyhpMS
zGIao2|LF+2;1InPY5{58BWnPUG{QB2(ndUDfx01l=@rZw@Uj3}tQ#3ZPqhH05Dfzz
z=+Y=y;zqU!bn9Vm5{@k;Ae%rvabyjks|gVrFq{U<)Ht06O3JXb1`1rFoR}Hljj$6v
z{460$=FuF9q$Lw_ma~x|Xf+y2%t9?UG>4wc3QFx720HLHhv43tH<HOlnZ>TT$)JV2
zU_XEswV@bj0vX$b8wlk?DI-JB3N}y*f?5QN5p*j+w+S1?2jqe*vMx?e%!^0(0oH$q
zg)r1?xRp?&p~6rKn?>eOgI#kW)*&p*%ZDzPHnfCv-;v#C0oIn6UxK0!R{TI(wD84i
zFh@c|6c)}<F06b<3Se`nL9R%F4C`OR41@UzW||?SM+g(fXKFxlfH&A=>yms(nFx0<
zD4`?!(;TV-)g?GpAxwr8g*eSM1<NB$HG;3W!{tAy$DkC{x7cD7YBVG|10eo{ma|~z
z7=Z@)P|_<jB48m2;X+7k7MMXyb4>=D4ax#W@TGUexgKgZB*ySr0r5mK)ED3q8`3H<
zf-k>=*$6N0;CTR>!KPp{T+6^`v?k{y78hqG$AiUS=9*~Qrh)tfDm_8*jFc=5jZt)f
zOmVb_Z4E)3G+_i<FNYGq=tVQkWk_a&CsQFCCc#A;s&Qxz0neTqIzsPk0EGbdTUU)h
zPC?fTx~aj?QOgii9fRvIYwMJJc*_BtL=2&)xFY8SLj!~=*JNu$CoLm{3E%*O9)OBd
z6C`sOI%$D7*C1O>AQM8Z2CITtZ9=rw5KVBa!Hp1v)!<wRiFs^(wn)i`w00o2nxWbX
z%Cn%rMfNa42PEkRfJ4e0p%E0;ko*I&7L*&&4Y5qgkB6uM<uOAiEenKxP)LKa8y-Cn
zn?aez&`HY@p%vaji_e7J%wc2%TEB!Xt>Vo^hQ{z_Ajl}!GHU}TJ&^GbSEHGa?o>mQ
zcweahz$&5Ip*I$Q8pjNIrMdA1r6u5&v`%qJij`G9<O1!a(zN)@l2q_v;SgB`uyk@p
zVo`iaQDSCEF<976!A=3u1UJ^SEw)fJL}`FyHX+TSwt}z0w!~r~Si%s=D$itBS4e{s
zV~D^Az7o_Nx{U%>2pEBua)PRVLzoP-vPP5&WV*!J2z*==mgXhMHK1q+E+N-w*lMwY
zQfRBd2(<hZnsOk0cq1dw^3#f9EFlMu6G%iN1s_-x6i=Wgpdoy+4rB~i1a$ZahykfC
zV9hi{%}%CYj0;LjT!W3{gMv#eGV_bUy{_aWP{RkDxj-vtG5rN{2SFzqnm`7CVKM1V
zc6eiTV-CWNMw*cGC16Pi9QH)H5iPM%*@@uBDkK;YodBfdL6j4rqq{T;#R^0y!uFVv
z9*Tybqb0zep-pnEL^u*Q$WFQ=VI$f!3dj_M8_md0j)n%%IRhFwG8N%SM4yS&1ZiXm
z*;Yg&HwJh^&Pa*(jZZJiFD-~q%gIlK6|?X)*Rc49)+6x32-+in=4(h<2^E9&5+Dt2
z*xo?OeF3h)k(}xp;GLYC9BdXJkQ{6tACw7hUBi6?x||K%tTciz=_bW5hOlaYX1*~(
zb}Ga#$aO<Na<C<mrwS5NpiMXUl5SkyfkXy$E&$CN&?ur_q?pC~#up@}xF%<!IzGTV
z85SepsxBU>YXMq%j#Ae_6PFRFk%C;jkzJAD>DNF{6DHqi&`J=bUXU@4o9m2>D7Fc$
z&jhk5B_Gs6g{A}p$mkvUm8luTI4sQ)q>h%cCa5uQWMl|A<AwUR<$$_%u-+NmHpp^)
z>f4qJvJKXggWCof$D_V&6(HMS-9NZ(kTr|cx2+On8?3(ww+*s{i2AmrfNX<xGU2vC
z#=NO-TPnylSeF!T8+?qA%vL$B8~~~uk@~TqtcTuHOixAbDP~gAQv_W%Lu5}eJ+%bf
zA+*F|Ay|Ugo+5nHjX89{9$pB8w!1)kijXBb&`KUwgF}l>(p`e5rwHAhL%z|lo??0`
ztO*0(Tm$o<kr8NfO+_)5kOTJ#3HB5r1MFa9K(!Q{0dFf98G-idK;~qi`;(1~K$~_z
zLO6Poqz9vMdMdaliPo763NA4;iucSf2H}##lGGqX?-IW22PG^(o-j0rx(n_bI0MyN
zV8yV7JFuhx%jPun1fu&1+NT6<HbBxP!lk5m!pI1;IS3RZhM*G@!Tv()PSMaiCGegB
zX#Wv)yki785z4|G5-fC#oGgTI5FKxbE69!<=t>&2LWd414Ka!U+5|+Ms4;^?4UVw|
z8m2SEcmrrx4|P0a0r3pZQ3x7(2Qe}M+OR_v@4&YCfNvN;nmwmON=t)h8_*6TD)?pq
z%4Nhj41E6)WtEGO5q#GYuF)A9CNRW^4SX{bW!^EgfF9IH$Kn`t`2uK41f$0d-{A!F
z6LMn@xdtS=0fwi?Z35|lkZ&|vkJ}7qkDFqf(0bfpn|$L_D@uwIlS@zr$Us}esE~0D
zp_{%aPb7w*kr))0g1Yqt+lax%;9;|r{CJd>Gicg|{DKhFt0KOUiZs?>1|4fKg6xK%
zj|))7AK)%P9a|y2<RK*tN<e*E*q8*&1;(i3Ewpt(7RUv#aSFH#P)9{*>jLn&3~cNI
z?gB{BL7#L09;<<kXTV*6I>JEPFaVF=z{WV>E}&;hOar9@*z7&b1t#=NiQrKsLj%y5
zqamoa0x^t?Oi+gdX&VjTp(aDvQWbb8ppM$n)(HWiwL)5ku*EK*iU@i93Dm_0VNf^U
zssz+{H3zK+OpOnY2hA;K<`;ux;#2cdtjtVJjm>nd89>V8-EvFPauU;vA$lx8XDz|>
zq(P-nG=U5Qt+TYUig!-T$w^8~&W_K?EG~hbmS6!oDySG-^W>#glz^sN3@t!c4uDT8
zi1&?8%Y%r4uB3zPxQO?S&&*3ntpJN_>cCeP7p0~p=a=R|%m7`L2VGqZY04m0pc=vV
zO@l0lHeJ9J*kVvnSb%H;F(9k&;o{IvKGOWVp$V$DoQ+%q+{!@v#Zt>s^GaNUg5xt?
zGXroQ01I*qsHVhcq#=GIed7~TQsRqJ(^894^O7NVY#ABBw{GL~Bw^2hqXorq&_){2
zGGO2MqSV~{vQ$iKVapxi){-2AhL-V|#zQT(W<W|$;rT@=uv0fIK+}!I7NDu5^8BI{
zP}(pyfZZ7fN-;1|@KOh8Lc&NH@Qv6Y<G@J*vUu9i0(!VDtdWHjU1srMci=w9!pIbI
zq!AW9pk^s((<HLD!085>yFg=ssi{SY#i{WSVFR7a+@w+%&rl~hzo0TLw*)F{q=W4w
z3@686*Ld(1#a321nR%(jB}JvlC5V$TEI_^{u;Ix9w95_>YG@mtjNrS=LFRzM%*Y6|
z2OK;)h9m^r@Ps4;9i}ld0_}eWE!Q`MNy35|>qZ7c1B8zZ4G=y;-T;N`oCzcN)@CHD
zppJ%2u0tn1!5#*c0MJ}QhD#tDqTpMxz^fyXN+Hzk&!~HH@Ou%mK?-UxVuO?s{9+Hd
z-@tj#$Ov?DEoe9%9MT})!onB(-W2?{<J&N01izRA<^&@n&^}&hVu0S$Yh;QN)Z~XS
zWUCWoM~)Hv-U^u2h8AX!RZZXk0DBu89+Y?<Vk64tDv+N+B{&%=1nXuikVin~krOxA
zHeVUR_xr*F22!XQp~V9%xM1N!hKJz0v7m_)u^Y<>zCRe|3UJB*#R|e5<fjDq1}a!$
zfUS^%*@h)CKy4!@oZ;4C?$<IhgROglSq&}^Q9Vk9poVPVf`+p-1ENSoF63d2EKmVk
zY-j}DR|G25G<CqutK7s2Bq2!go}3RpObT3iLYh|A3{Hu~sYoYb!Au41Z!R`6DakKM
z%mJGN-r8ga>Fz*NkD)1=W=ptoSL@Q^ROo&v0-8)fDqXE}5{uGPlXCKtvmuusKsQn0
zvdYi^p~2NUEwiY&1ftK#2)@nRf`HAYU~M3)Q8a@xC@yPFauSP6T&)X=Qp=F6gM>J^
zjjL|}UT6q9LLCz3kiY|pg7!K=LcJir7@`{_1Zl|@loUaPKnseX`{TgJi-8RU3qm%=
zp;ytMhysr^fqV-|iJ<#lAf+<Me7Jic3`@u;y^#@U`!%FuhN?m<8!%!Qk}7fS{W5}Y
zj)pk|K8Oek49vY=xPllw4UDwu0I6Cvhx9aY>IBX5LUe+9CCF=vK-;uYLJ8e*gglRD
z0Mi`O`NZWa=)MR&YsZ|N{AB3b9QaKx6b2UjL`#sjgHq9~z?YJd&a(vR1r-4y`3TqH
z(`p7$?3!y0(&<)|3hS2;2npCJmmpp6Yw3*4;l(*D9YR(Hf-^EGK@*A^=n<BN(CPuS
z!yYB7p^Ms#j6fUYAx$OdDey3pVbugil7|`Xq-PDfn+Fy=utl4=BG1Uk47$n=S`Xn$
z8?X~24Io#l1*ewyfsPae^>B=gKwIBYJc6EPp&o<z0+%JAJ61u)f)){i?;H*&f~|f;
zyIlaiJU0ca1b5FscQz*_f;J|BPB=^|O(U=-0&;`1C1?*;v8fT#>J?K>(4GuPIS=02
zYXsVe4=$TPyOJRcLrq%<!wAMO*0ilCj?XVhEG$ipFDS_<N=;0O&n+!Ut%xtN(lIhM
z(=j$R0+|eQ9(3=n5omjLMKO3AL}qe4NCxa`J1ZSC0}CB96Oj5`(7B8HAeR`MYU-Go
zgHKD0_l<`PktC(2Lkt5QEp2XKqGM=a1l9(TPA-LRYX(Uhnj2Z5$%ASOgnlD)QzN*7
zq|!8q45(~3Ha7<!`55mT586xusWU)s0%Zwf3j^@Uk5I`(P%)BN0;*j>DnRQqj4e#S
zbw0RnotToD2X!<=4(_G$qRbLhIZFe`DUiPL1)#wjh=VLNb&M^I&2+%Kox$Sq$)!c{
z#ffF95CvctTbjWf1yPckmx89m#J~XNDu@!$wj78FAcvV4Kq|<1-+0j3m&s)%5IG|w
z(C%|cJ_YUi2GvnUMxb5a6~$16;8@fFZQ9U@kIzkl#6^jnm5zx4q=JnH&&q(p5@IIE
z#U=)jnie4k-HQzhMbMdgCeUgZssK`*gCs%u%mjMACR7qMbOuf|V3T3n&@sjwK-<m1
zSs0WNpk*1DvNVKrlZ=ck;1x1x4>!oOhDeIR+kAsd42@Cp9@dpykiFc{MkB@<NJh}C
zPNZ4jZQd}meB+agQWHy3p@{@iI)S#A<MkS>SR&3wLsL*XK(YqBkrBLqD+M&Fkza{w
zJ8Z8BQdpq`EQz**X2($Mfmx3crIwI32ywo`GjK$jIk3Sba9Ng$+_E9b92f9GKcGRr
zc<_(~(yD9H%z?NEy4MpF2e|fnl4dMug4GDN4HRN*CaBa;&4X6Gpo?5kQXQxy0aq24
zCXibIjEta14Zu=>H*ryA4z&nW+d+DMa64h0K9GY@6B#TUgGvYu107He0_uW6YklG@
zHpwqY%>&iVAm!j;c>jXbJW#a+uB?-D@{3{ZE6^T&6yF=d9DrbuWVb2UbdZ-pX~H)i
zR=31w=9Q!t<t65TiqN#wB3S5?lJ<?v<8d1Z%U+-}LyAj^@+)!K2x(h`f`)JjXoQ@L
z(R={4us9>XJU20~64nj}UDAURqM(WZ6ov4BB`G{D;pT(w(}GMFg3^t(b!lEoYEfEF
zemN{KVP}1Tq`}DwGJ-{>&2Zx(HbW-qKt@|zgKOWk#LOI+&9HHNkTf=%Ar2wU=Wt70
zGp#{8xgqmoARDZ$^UIKZZVW$G03=H#`z>I0K&^*t7ywyd4Qk~j=jWBB7L~-8<b#%L
zfRi>PFpS{`4}jEy90M&;5IG9c9f6J=lN2aMpiMkjtac4Z1_hFyb#ZD5s2vEdQH?=u
zWt6lCJ-Lq>E-{1H1hp8t*&XC#YwP0F)a?AUG+1!J7HWbdLBU}R^*Ah9Lf0xn&$1$`
z#50Fl=~@9_lN_9ynw^qa1j^^AMWCUh7G&iE$UP9pf<{lEA!Y*I;s=t%>R8AiG5M}6
z$S-ycE-^_f$S?LZG(>7_x?*)IWQhdGHQ-bS@+T}@(JK-B;c5wK9>VG{kY$9+KTv@S
zvJx{~QJfC$wHH_B!OK9%Y8v9~Fac?V6fuz17!c2bXRcsgg>Ae9Nr7So+HZxYW9T_+
zq<PL1Yz64L$`UikIaHux1~LN<vdG%HqBuRZ1X^hu!*{xa<Uo$VDA>u*)@Bf!V3`TB
zA_HU($PERh2sfaY@aS%UHXEQhjHIH)tfClX6R6z)DoS8$0U)jb9iEk1l$)6cYP9E<
zmcaMNgErqn_Gp6?f`SF3v?IwC;OX2FG^@a=1Ke^iPAx%cKp2De;-eIC=t+Z|pa38I
zfUpW&0q9vn)<c+TS~Ea04s=@wr0a<5cn0X`w<UPO+0X<uY+-1mYiR}wDMJGz15NAr
zvVxK#6%->uWfAhKhIGhE1#tHe6ywltE_zSG7`~?;WF5o?aK&q63|e>r>Hr%eWT1-{
zU{wMvmNG$W5`s$%O(8CV?=r&LLj<=cF?xo^p!>5RkzxoRwE{T@><%ykQi{U!Yo@cI
zYXFIsq7BFz!|%63coE@wkfnwOP%k5_#oc=`2Hk~)*AGahp!Qu1EpV+D1sMt&1`W=1
zLG71-wZO*V?d+h%9{lPon2(H%LF-?@1Mx^gkgzl|1|>0292*&fHXwsf_W`MgcwH0H
z@6RkMb`5X~3eF6U2On(=>gX85H~1nM3fTz`k1#_6V|YxWnp9#5Nhq25#aNR_urabt
zup^K0*@SkwL@><Fh+`~*4Uo-*cif?g4ID}!U&CDw-V+59LBv^6u`6i15@dwe7_<Wo
zC9<)YVGP>e1qyphw5|ML2L=^`@&Y6{;T1C;2N^?VRZ$&Of-nPK<KZ*|eM>dO8Q|Nj
zA<lq}DdP@nBO_x-;z4yr7Qz|u`Ua;n4AHj&K-`goFazE<z-fk&5w!7#>JIQpePF+U
zHc%0YA0uPPJ|I*xD&X#bZ-2rY9#~R&P$j|)V$zxkwx~@*m;rAO;|ve%K?**T9TFVy
zRw_;-z?&N|lYA<|8H7_Q#*P9=)PffML-P&%Y9D-I3ND3FVuiG#9$Nc>Dn%n>_~s&*
zjiCAv5-1kX;TmJmjvdIbCp36qmU{<hnn6qh4Ny4gS;Lp08-q6IprkNJy8&K=z>Ema
z1n-6fU*`a})`W;64edyLs2M@Y@W_DgXo2|(v-|-K3xkFcz@dqrO2KXlNX|quk#H(Q
zF%ivwpzfp*G?L-_Q*b*DdM7VdXMwhf64jVMay;R*3AGW;*+km~3VBfMgVuvVYg_1w
z-{4I1^wbiNIH)y_<R-}4bW~}uF`4Ok`9;tHHDmZT4cvi+aI+aiD=0C-!kTcQ4M~`o
zh9F9Q_+m~xZU84EoJN7ovMxwX&4vXIbWvia2}ls)U~ABf8B87)Iwl}NP{@IU2E-}O
z&xHpLdQ||=yoS(v2x}KA(*kA=+~9)J67cjAq)-4YNkqxKnArzCX_`Y#0G&<gsApZ8
zhg1|mP80~vGzE)7Jqzlk!aNHP43HpHz9cm#CmpH)zQ_?40nn~FmeRx&tPg4isH+V(
zgK$-YQF)t!4ZtzH0E!TBi3PvW2Ie}<&_W%OL8LXP1sJ0&AhTg7L?TvKfSTlx<}P^F
z0yIgFrLO?laS;sa0+fLV5+MC7a8Cu>0#&!7{JawIIsC>thOoo$EI};<0;^KN=Uzj)
zPOzpn^wex)&{aN=1O#oD85x5XL4jI(h9ILs6{(RiXf-IPfsHI=4qg0W3|ch_sdS)5
zz(Nf=iH^2p(-6rUu0ENDuC9>9Ft8aiNcRRQEa7V<VeWx;bivL7xeuC|!M+4l6wnSG
zvDQP@pF)rL#*8LY$fgjYO)~^7V~3iCSafO(THc8g&=6M}8H3hig2L7a6p&!=K-~{p
zNkK6D@U1j8hOcUbg`SZyXo(>-f}tx-K@kkL8d5ky9YCy?A&Whs<v9EX5}194mL`w^
zIIveSZ6Vs55NlA@l!AN*%4fv76zf7#V>9S>6JkxlwtCbUzMK*6Z%AVpBb7o!gVam{
zUor|!T8Jg1#-K%dC`k((#h{QzSWaS;!WVABVijIH!}2(0tU|3IIzHicK&A-6M}ESJ
zCD5`vG%uN=r&X8>NDWCyaRUuTYX+p^2sUH|I+UQ;#GnAOiV##F>KN!4S{hlW=j10P
z=0JAUfEN=Qn;L0?WI*?S=A|Vjr<UXw<bzfOfn^OLN9P+GS*L-D?mXDCL`}#d8zbxF
zoYLYF(2@P1HH{$E7LbJ?7-b;*@)%3R_&X?q4RI8DhRCHXxDEnspTI2K%uot9R9%p<
z9nfK;-~lJ35*f7I5gI#?NP(8faMu`{Lb5zKcrc4(RC}P8PeJZyF-~#y3ywE509SLK
zpzTZG(HhK)QjE<ZZ4qduMKuz%<iamF9>0+R&^8Nvu_Y{QVXika1}$|2mCc5hu;FF2
zM2=#uVF0K#<D_Tp4o)Uu*Td|9cS@k~2MZan9pIS6YqBAdkx)w<Q&QX#VcVFD@kFFK
zR3%8Cqn>pTcta9#S`7^ls-fD$5_2-4=i?c}mm9)D4pyg`LmRdD{A3C?31q61p0!JA
zGB_XlL8r%!;VTc}Mnj5IcoKtHg_ieGqr(7UD&$ftYcxAh%Y78h=1`?j_u*`gfx;27
zMaUA=9w*co!?{Js7__JolCMl5zBMuiUFHL+s}Vxb?!F<&Hc-+rG6pUC1F14H2Ce&p
zG{&In(LxBdF<}YzCCCrRn}Tq*#NgM{z-)%*dn04$%?i*+u>^S$Guz|0n?N(n0<vBL
zn%xN)g`*7yzv%|<esFuo$k+n9>k1rxpdf{X8urQzJ=pLzz(5Np(IOSJC=V1}hL-S3
z4K)Cv`GzRZLYhU;dfEa$dJ2nvOW5WfkSD=j1N)gY4?^rfZF*Tiat~2%#NO_*fOI#B
z3uCN}F3?Iwl(5B>AfbT(^EFY9#?#_LtVM(dvyrg{q$WgU1ybS=-V}mGBYe03YQ7<6
zG(ycMF7)6QU}<ZC)=HuTBDmB6B^88cVAhiqfY=&Zpt1(m%mQ^Zi;av-(xB}u=w-BK
zW+v8!rHMJ<yBHu+pex(WAj?tnb5g*KDTsuHrjDVRC1`zZMq+VBSz=CUDntUL1GI+N
z7_*UOXb#!^0bT!x)UH9S!-VEbXmT>dS)hTMbs#U{X<|WF?ZJ~ATnp~@75wTSSowhN
z4r9bYg5WtROv9m#DofA}(5ZPPMk%<OQI?QZVVEXB+fe8xfP4#W)F2iRLW2wDEF)vs
z3PM9mGe}DimT(Zs6&4OAAQPe0Y+z|(jx*@e4wwz_;ZUdzkO~HDJ}80WHq{bt6x3+X
zyyT+P+|;}hxCG1&w2B4hE<+1UEg-YNO{n5j;>|G$N<~%<H3QuGfjI$DYC}p9=t=@2
z{E5<v^2{p(wayYtV8f2M(w?CKLMhZe9*M;nVc`4&8CkT1j80+3mnB>!NGE7FM`{UN
z2<9PpcM%qEkoF8BBcW767>Uo&C>Y65XyeTp+F2stK69u#sOzj59D{-!BW?0hOVUA0
zUm?8Ayb>J+13N9GrW<JUSFxcP;uZ-bW6%Xh6~&l$VPcwL4PNbu*lS=2J8vPsJTEsj
z7qW656syp6^B5gGOL&P7+NJ<$JAqQTkqKypAEXZglff2treHUD2KWUV#CvA?WE!}-
zLYj1-W)*ah61+?qxwu2={NXmi5E=m(1*QpN!5_?Rkaz~W8svOvel-ED#{+r7!VtAM
zfW|y-yNy7jY48Rqnh!wsBMtxHHU-=sgqwoaoi%}9HDrmy7e*!qus!%Dpu2w{4QzyB
z<T4nyBS1wm?5<&qo;b)6M0zF28(|9AD~OfnCh*%{aCrq*ViF2?7o4lxL3R-1Qt)0q
z(5ORjY6)T-)C94Z3=w{yl!BIIh;=7uLl($%Xc7%hEdlS_0|`OOB9Mi|I2T;x!1Y3n
zb1WzT?M?xgI3}QLT_Eiwn9IQ#5$<p!6VQz*pmx2H321Q^D3y?!-pwF3f|lk%ldDT=
zPAaTCC)O`!5KT}|Ksh<!+K<FQF-FmhVjyAwlGqesXaF${q!Uy+JA(TCAm2IWK$lY!
zYmz0zAjg~>s5xlU8YYPKV9>ZSG65|F14WJ@ENh@f94HMEo9G}7TSx?fCYDSP%f67U
z0~v{I3FJ^?(ky`<&=HhMdWtrKXmZW8HgMFlwq^ir^h*VeO+xn*g2q^j4UIAlLCM<?
zy45|?2+D&@7iAhlxsb*;W(|N?dxqGi3Gp{*85yX^H?%~^AQzMtF!$hYJDV7y^f3`S
za5kMy5G%(Jw!+;5VnAvn6cXB#f)<GgTZ~*YGa>WAn8mdrr0<HM#}KZ^ngM*447}mt
z>F44a5pRUl;sABLij52qEe=z26HUy?L=(``H%P&Q*p378ijfIy;hUi)RK~~zv;q#4
z%#BPyi`zhD5L^;H<$>Mj2^uGbx((i92=Wewq;AY{SCDIv%Trv2fVvL|L%{VEqUm7*
zzn22$aA*$)>?M%jh<7V^K?)XkLhpot#*is)cft(7a3g#k8XCA5ZUmSA#Jdz%T7sU)
zPS~X|129|)A6>@fQbX(mAUHz>)K>8ejz=11gSIKLHziGsAgdq<dm+dhx3Q=V1>!;l
zt^RjRfsRd^fYvPH%rxK>297;514xUN$XGOXOi6Lg4Dt?!P9Hg@q!>c0R3ntK3RlRQ
zfOH_6;F*^UD%t~zQVSAcYiUe~^Shx5s%B{Q?3j`g4BB4|D!Gk7;Xp)o1<etFG$FeT
zY7S}svw-PBGs!i#prjHOQYb?XxIzkK5?B}1TfwmF224PA3lI^tkTRas2o6pyam_@E
zVRwYJ@LoDJm%)rSG67vY0IKW2g)Sm-L0kY!TClD(E)PRSI5R;9R$IHLmiT1mLA&uL
z@H++IrKJ&QkvzERVFX#POqx~ZQ1d}S3+lmVR>7@;_d}tcgcuC=BG_Zl%94<0kcY%w
z^Gb>;L5Un1|DY8ssPPBRiU>QvTw;UG5^g-yH{eEmVo54==&RU2FVzWrMglZLpp5t6
ziVPzYT*g3KoZ#aed{c8h^U|P4eV8ED*}}pOQntW7W{S2IiBKRK8X?=_8WfxlP8_ht
zZgeaxKrw16_(&WmX@cX8q%eh#_d^?eNIgvAs#QGQM<NY?HSw^`yg9mr6s4y6<)@^Y
zAx3bF3_#(JfAY=905tsyY1+a<5xV5S#28wMg3SR1nURSxtk(}R8Y*N8U2BUZ1ig8{
z$ON=R7u4c{8v#qh(0UQ6It>8LavLIf1k(0`^emu_erVt$HJgl~tJ0AzfV&ICfaX@P
zHz9*a$W3m-_Jc-?0uaqM^zjyB$o4j(y@TTfJ`>P7Wl#x#@CP_~8kv9=BSQ*OV`#aC
zR^AZy0&>X%i(`0Bfvs7FTWn+kT7e8|FdG_MLYC1RnSc@rDD=RNA~_BrnFlhHV*)y8
zAH}Ce2GCs|V9z4r3}y|iOd#x0h&?EiawgF8gNcqHtP^t}M}yi7#5)C2b%6^fL^Tat
zJq-_Qc>5V8eBl8C3jv}X51*ie_EkXR3eXY}b_ze-Zg4z<LK_jg(3C~Ab?`YfSkyw>
z0|+}XqZVoh@gWJf1aqR!1a=-T!a`69h2kml0~1p6Kts}+A-SZoAQe1H4VkSoG6YRp
z7aNuq6oA$%8G+g!h8B<`?1D=S4G`*0!Ae}M!54E|fseW~!l4qZ#ML@6rywH{ze=zY
zSL>9_^vn|cD#1!ztt-GZUZrLbHLljh1)y~_cwGus0y?V>G=73tWdZ0wC|B$BqQrs>
zyw;n7mAG0b=am%Y;8zJ&;%Z$`nwLz#_h2QU5X~#i#cyU}PF`uQE0)m)*cy6pjDr#v
zJlmRBLhC+IS^&uynSj>2fC^6|6VS?UaNP%!gk@*c)B!nJCmu&P(gae!plJaQB|x-T
zGvGS63OuM~WCU8tQEXxoA77lA4r=d#k1>WEodneh5;Zab9le`aq+y_Au4!#xZ45r*
z0d!as=*TJqYikB@-HUqA70d<`&}HMG6Lvs{U_oY^jZ8pG%OMpPXhk(BcbI^tw;>EO
z@PS+L#crNHu2zr(4q^JC#{s24%mK|1L0TtZ`%~kQ>VDX%wML+$xIm!~j~CDaY_Q!h
z86y+WnrTS9fYu#@;{_&(6fZ_#TcE!5Om>At4*XO?^xEGPp4~Atf};qo5q*3Dw8R-)
z2_m@x<O3rU=w=op6VNhch#$<MJ0YM&C-_(cP(g&^%m5=}Xmp|<V1^@QnL%3p*i18Y
zC14upP#I{w0$Mta;u9n<K>Y$;DTq?X;R$oxN7g__dx|TO5A895Ew6^B0!#;h6C)^i
zVNSpuk`^%2T?5SHA?XuzbOGwI5zyUGCa@LGc&vsrKS{AS6RUmJ;InjKVGgTjVW|hx
z%Xq>U<yao*xkNC7p|jL*4}r2DILCs9#=)u41Z~GF=l~@2#E*8`Q5tLn3btk!ZagG(
z;I7BxXrr_gXg3pF#z2ZJ*s5E&dGLrP#z+&8Nl-(<*&g}iD-+lnSh(59g$pPx;c+aU
zz8>_DQ7pL*K063GdjT3Fp!K#W*$cK!3RY0SG8e%z0_98x&@3dxo_HgeO|ae`EK%XN
ziD-K*VD^AdlL$77hvZ_&DHkxip_^FYfsJlAbblgL7*Eh6pPm8oBxn~D{QMac*cw{6
zJ<wqZcyfXA(YGLjF1JK2adDqA11@eso57%dgRVoy663JbPQdO&Jvzz+w(u41QmEJA
zF2xfDDCb0h47X+=aI_(;A~)BxEjBblOurbJfL7ar4!MLH5TBS-47raARHH$TyTCjt
z5qu_6Jmee|wBr{|%wg+5GNA`J8$!=w4lXf=TH=|UjB=!mt5IeOwsslp2vhL+jnFI!
zqKr&HtAD|z73gF~kPvhd5)o)P?E)VV3<@;N87^~JTMV~m&>6uH&7kl>ZmfWoAwx4V
zydMv88Q8H<2Do=kl2ZdR!5ibTO!in9Xo7mIctRC)&IVjN*ttl9TCj!9Si=_NTq1%J
zTnr-Z^g}hCfa}1;p`jz>5J0dia2x_?0$m4<$EV;EHbBRQLGJUhFxRxUPRWnYFNI#$
zYXVya4Ymj5J0lZNv$-O*D7CmWr^E^@1Ufks%rF5hU<S1%LFck+80bKYLMUZq0_qon
zq@e=PvI|BTW4T-uSHcI~<CR|ux(mi2KEOLU*d#tE*d#t6IoK>dDA+6>bYBwqj3ThR
z!G0>uE6&JFgZm57P=F*lu)n~bK=BPo1m-?yMM8=*4GmKA<DoXY1_c{{Txc901X`41
z=ma?q4B^5|=pjm=KnJA((EVDt{BN3)AD@{AF%rA?!N+$Y3{TBV&Ig}R3uz$0Rzrgo
zfISJ$V&Fqy!CYuV5T6?i4O8;tVdjEPA9B(HAH)SV6CCD=Ipv9!#qoLhpu^aprM?Ah
zToP6S!CF$VaEIj|yjFwaAH{Tt{ou2)5ccP!=B1Zpz$}JNSAeC#enj=M5h(S7#G$DZ
zN}-iH_*`X~k{=JT5-DQ9H>iORBm`Rw4wu}-3aCPu+aTQ~e1;nur{u>YG(!9fJ_8GC
zE_6q*p|K{`ad7a;MvS$LkcEo`M#4c$@^m15P1q<YY$P1CE&*J4gBC!70?)|A(h#=W
z*AlXy+{gs9NEuw~AgMBjsxkqs|IG$9rD2k=1b}_C4Z3I!I>!uNy$BhPGJ$map$%4~
z-Z1D6E)+My8?2Vly+@FQ0QM$mEEk&giMAgy5)L1UNAC+mm&X!s9C!&L)HtNMFxWAq
z;5Y+?HLQ~WI!zNCyr8fIOF(^qV=NI%5aJyP2d!;I2|yzg&>B=wNe)}I299H})!?us
zISL^I)R56qQ_wP2H2XkzkAc#Ni6x}ChIk!j4e|bj*n>I{ZV73<5FI~Q2f{&)1{c~y
z8-r~i+!VG-77@p=c|r8F3ayQxF-^R!;7M!5x=_SGxG8AiEn2Wc&tyTwE~(bR$EaaZ
z3+qe4GCO!BBq-q+!J`&x2l0^#w*+$_+!VAL7u{2k!~^#f+zGJcM08+63La=q1q}p3
zhV3Ao1{2Up2*nl_@dcnN*Ai41LIxL$Kzu_@Xo(tM4&_3Z%|UJg)G*KipP*%FrekPk
z4L(C3qzo)(XlMi$OiBX@8JU8X8H0Kv#h^1ybwH=7gO1qGNlF8+F$WcB@O{Ve$@%%&
znW-Rs;M-`7j35WmL&i`*8O#*4EErsvn1Ytyf(}RnZ8ioS^lxMx4?1E5q}j+6w89mt
z%u>@9rVP3#8PvCgC<85Sg(w3pvjizKG6k)!1X~4K3JE&C*%%ZSkgx%rl@E5Fktt}o
zAXKX<+y-M)(5S0*d}$KId;?QW9b*d<@TCIaYjz>Rprw6Kz2-;*su5N?CMJ+=)1|Oj
zFtLExTnY+3h_fs-Z4u+W;AW&HC?l8}8tH&;A;?3hv_w{E0BS@-R6>sN&p~pesR_i9
zNjcd_(E_>;!Pv+;K0ht3I2C-$A~<+-%uEbGUIHa4XbX_I{v|Z!f%>M!7K%nX21W*;
zyk!V#6hPTVrl7@s@H72Eo%d8LYj}&_6t>(B+`|QFMzoAgjf|k9`lg_>;J}qOs2>bf
z1zEESZ8kwEaGM__Pq-0nWDM$9rlcm9m7sbSqt|PUujOy#1Uje&sjY7cTi%7kM<Cb1
z+kVt^raAV8KgfZwramaN!L>$k3Gv=H!qe~v84hcbL)w<6uq9A%??a0}P;7$y4lDJ%
zNpXV(%o5Nrp_869N?ROeF|3aTOCzwBK1DVg8l%_+wI9;#hFJ_7tc6*O=3{tlLlY^K
z!t8z$A17uI2cWe3L2-d-u*2L2-77^(U>h1BjDh+Wqm^q4Tlj^DW@yX7)Ce{g2MZ1~
zkCNgP@E{P<$OWRA4RQb|DU)OxXjBMd8rm?6DQw{vR_`DuTT<-9J?;Xsk5t#A4#a>0
z7so&hS%!noK826WKnzD7nIWs>0QH*jm7TCV-@$<n?x{kHFj(S+tXn0iq#$Mp1nxbG
z%0*(wMBui;$}pJk$jO?x#z5efpp;fHn;<&^NDD_$<e?Y_83KR|dBClMltVCk;KK^U
zmt=$nG2k{K$|RU=<b)sYQ4NSi$fFvtrVyyCu(E=VK!Wbs1~*Y`Q}eT}40H+-i%K#R
zbF2(?Qj3c6i>!=vpu<nbI-uqhxRDHLCV?By#TJkj5U9b8Xrd!E(IDd<)(oJt=;KSk
zmk5K~Xbkb7yEDv85Msufu-OENkx*Me&8=cXYlhr{#G=GpNc$AD{IH_fH?=q;GcP>=
zB%WH5T2ySsfGGqu1*D_c(99ygpd>RlvntgXd>=EYxdLv!<`<L{m!uXHLxn*NYS1}D
zFx5tI)sS8WOgU6sQwOrwG{2xEwWz2dAKWDY9a;|SO@J?mOv}v40XZMEU)~bj3<rs%
zfG!>fwc<ftbWnf7%*47lF)g*E5~LPvh>->O)~}+}oXotu)FL!lP?O%w1axsFcwQs}
zq|H!M$HWkPHCK8O<YFzD4WRbBnTd5uYEEKlULx2ILron})7;p|IybQbtQ~ACw8fp9
zTATrjPSBht_-04wxHPosU0ec6FL}kKMW7>@pmu|r-=Na~GE$SXK^I))B!c^Jpq;vq
zTfLG&GN9?5lBE37yp+Tum_tB!m4S4Y7L_HIloq9;`PJOm4AibKPE7}GFE1`gElN#E
z2lsctPPQ-rH~T?J7<|(j)HxQANJ|CZ2wI++nVtdeDnPVYf=x`%EK4mdD1lrwY6yx?
zaI(lvtjI3~r7Td|fSP6ri93+Ti!%}nQXyw2K)Z<skPaazho$D^!F+9K0O|h~=j0a@
zz`SZ`0O{=|C8nh2<YeY%A{=FCXax31acXjYUP^vZN-C_kWoT#t?s29<wiu#F89`#a
zurv{pS~YbHjUc_XOz<rtC8;IFP(K(NL3(XaiK5JOxQwwOIB8-wkc^EWw^kULLJyjR
z4Fj5jmh(b-i%=OOQ}}XTQ_ull;GPbWHspn}hUOS~7XR89Q)84p4cPSKT_R%&J5Ly^
zN06IF&~XfGHlS_UMQX+wL+U0xdST10!3)lzWj|;+GkBN<DU?7?F){_MLxu(}<iG+G
z(AgcJ<c2oRgT<N9ix6Rp*`4zXD&30mb76ME2J|uQgzOv#M<d7;ur3a6Pg%gs25qN>
zE>5y$U|?W~kMc9kOV7znGBWc^cT7p~&x`km7QtcpIi<O&pxAc}2q*)s(l#>mOOFQ$
zWag#EBZ_rbD>rcQWvs~nDvhiHz&sO8hTH-u+Z@CORRf^(4USi^-SJT-hVkGl5i^tH
zLD%?YCgx;TC6;97=fx-HrNpOzu4pSw2J;OvGa2+ti;MI#^OAE)Q&RQQlauvR5{t?+
z^Yl&h4D^$=we>UeAvYB;kZ7<KXyC@sKoeZ&WrC{L%&OFQh~o`Gq6I~n`9+x}m0<4}
zYBDf@gDgHVB_%&kFS($AA>Q4`-^tM@KHf7P9-@Xuun_gk%Pes%gWG~o?Lv%dLcWIh
zIjJ-a!&Rd?A?bqr5K4n+B{^i1GE0i};ddhFWe`ldu#`(E$_9_Yu%IF2Vq$$pta&7d
zz<?V}?GPC3=Fu#MAej+V(orkXk?d4r!+z9Yc&&^P6d08!tPO%uV}jZhpn92L@rg(o
z=!O$YDFdq$(O59dt%N%TH1Y;+Q-S<ml95>qZg&`AX?F~6-%&3vaD<bAYZ>-%ijNBM
zgf?sqkwezi${joh2HIQ)>GgoN!GUKNGvmQM#dz>;pUk|p{P=?6)Y6oED+bUG3S5%l
z;TcnqA;pH^^FoYFLA&TcCSn+*1MW0Jn!upVaFA{TXm1$QDW;&gJh(P19Z0_zG^_&`
zggFXouMubi2FOIv1>(?iOF&%EkUwl>2d)>oeaRHGBdpj0a^)C864YfegBlSZ4<1SY
z-!+CX3fkvHtmZR?U36gtG85Vp0Z~S#pbc73|3SJ%us%v~i6z`l@OU+3)g*XP398AE
z@oFU1kd7I2VIasxT;tIukUk!YRiN=^*d4%N=UQ8%E%q^mZ}$P24fO`>8f_#Y$O;W3
zQ}~7-Q&13rf)Co`L$(((g&q$+cLo}MNS#s8P9aFQ(gYq#P>Ydl0f}G>K1e?tW;Qqk
zQO!mVL1%Es8>Rs?2!gaw)fBW32(M%CcoTGAoOd$#<_6F#XKn$S!B|`p096l8X`nP~
zWD43jgyI6IjYg&>kRC5A6ugmx*aTz(blxZ_H4T0Zi>WDWaXBpV;1hIE<G^u<9J(mx
znn4T$^_b(q`#It11-d#6WC6%yD7sP$F*PXS(~y^n&=4P=SsV{OH9fT`z5qH70NOl-
z5-RB4z#1xsCa8vi14s)z+Ye3tVEHuAMmkV`8_6~dKN*^$sf8G33|bHXH!LMTJ~^=@
z8M++N6tp@7udCsaYGex9&jl(hz-u-%40J5aA={6!d(0p{9%h$ofOl|wKtNfRwUHC#
zLRVzxloli>7DHFWnu2zQ5pWM^rx?f?MyBwcVy2+|TA;!lt!{uU3<fzIXYv89WCyK|
z1v|nuz}wHzAU;01q9QRVv&_)YJRZG90S&T%ya8Eb3QDrp(Ay><eqxA^3JJznr-Am3
zA+75G-;``(s>u)^6<lHwAD@{I8ey>t2G@U}&07`4pm7N+hRpn8$bh~Tm=7J02dOAF
zj1P`4N=<{_JPAIT-vDw_Vld(&J`<2OLu93{*73pdpq&t@De>i*B^mL>B`KNt@O^!t
z^o6e9$N-xbEQ|k5;oH7U5$CCbBEiVi40a#^OvcC*bRPt09iNdY=uAsUHH**&9_K`N
z38Ykoj*(#&X3!flFm!>7R=6(ovK6$83X=K|w!<9_V!)C*hPk*$GR+|C`Y?1rs!8}|
z1xS@D4jp)^VO%<3c7WO@qz&TfrDib16Ej?fsu0ge8Aze4M|@gQVs5G-0|Ns9{vSd`

literal 0
HcmV?d00001

diff --git a/contrib/Tetgen/LICENSE b/contrib/Tetgen/LICENSE
new file mode 100644
index 0000000000..456b0bbe48
--- /dev/null
+++ b/contrib/Tetgen/LICENSE
@@ -0,0 +1,65 @@
+TetGen License
+--------------
+
+The software (TetGen) is licensed under the terms of the  MIT  license
+with the following exceptions:
+
+Distribution of  modified  versions  of this code is permissible UNDER
+THE CONDITION THAT  THIS CODE AND ANY MODIFICATIONS  MADE TO IT IN THE
+SAME SOURCE FILES  tetgen.h AND tetgen.cxx  REMAIN UNDER  COPYRIGHT OF
+THE  ORIGINAL AUTHOR,  BOTH  SOURCE AND OBJECT  CODE  ARE MADE  FREELY
+AVAILABLE  WITHOUT   CHARGE,   AND  CLEAR   NOTICE  IS  GIVEN  OF  THE 
+MODIFICATIONS.
+
+Distribution of this code for  any  commercial purpose  is permissible
+ONLY BY DIRECT ARRANGEMENT WITH THE COPYRIGHT OWNER.
+
+The full  license text is reproduced below.
+
+This means that TetGen is no free software, but for private, research,
+and  educational purposes it  can be  used at  absolutely no  cost and
+without further arrangements.
+
+
+For details, see http://tetgen.berlios.de
+
+==============================================================================
+
+TetGen
+A Quality Tetrahedral Mesh Generator and 3D Delaunay Triangulator
+Version 1.3 (Released on June 13, 2004).
+
+Copyright 2002, 2004  Hang Si
+Rathausstr. 9, 10178 Berlin, Germany
+si@wias-berlin.de
+
+Permission is hereby granted, free  of charge, to any person obtaining
+a  copy  of this  software  and  associated  documentation files  (the
+"Software"), to  deal in  the Software without  restriction, including
+without limitation  the rights to  use, copy, modify,  merge, publish,
+distribute,  sublicense and/or  sell copies  of the  Software,  and to
+permit persons to whom the Software  is furnished to do so, subject to
+the following conditions:
+
+Distribution of  modified  versions  of this code is permissible UNDER
+THE CONDITION THAT  THIS CODE AND ANY MODIFICATIONS  MADE TO IT IN THE
+SAME SOURCE FILES  tetgen.h AND tetgen.cxx  REMAIN UNDER  COPYRIGHT OF
+THE  ORIGINAL AUTHOR,  BOTH  SOURCE AND OBJECT  CODE  ARE MADE  FREELY
+AVAILABLE  WITHOUT   CHARGE,   AND  CLEAR   NOTICE  IS  GIVEN  OF  THE 
+MODIFICATIONS.
+
+Distribution of this code for  any  commercial purpose  is permissible
+ONLY BY DIRECT ARRANGEMENT WITH THE COPYRIGHT OWNER.
+
+The  above  copyright  notice  and  this permission  notice  shall  be
+included in all copies or substantial portions of the Software.
+
+THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
+EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT  SHALL THE AUTHORS OR COPYRIGHT HOLDERS  BE LIABLE FOR ANY
+CLAIM, DAMAGES OR  OTHER LIABILITY, WHETHER IN AN  ACTION OF CONTRACT,
+TORT  OR OTHERWISE, ARISING  FROM, OUT  OF OR  IN CONNECTION  WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+==============================================================================
\ No newline at end of file
diff --git a/contrib/Tetgen/Makefile b/contrib/Tetgen/Makefile
new file mode 100644
index 0000000000..4d58517923
--- /dev/null
+++ b/contrib/Tetgen/Makefile
@@ -0,0 +1,53 @@
+# $Id: Makefile,v 1.1 2005-09-21 17:29:39 geuzaine Exp $
+#
+# Copyright (C) 1997-2005 C. Geuzaine, J.-F. Remacle
+#
+# 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.
+# 
+# Please report all bugs and problems to <gmsh@geuz.org>.
+
+include ../../variables
+
+LIB     = ../../lib/libGmshTetgen.a
+# Do not optimize (same as Triangle...) 
+CFLAGS  =  ${FLAGS} -DTETLIBRARY
+
+SRC = predicates.cxx tetgen.cxx
+OBJ = ${SRC:.cxx=.o}
+
+.SUFFIXES: .o .cxx
+
+${LIB}: ${OBJ} 
+	${AR} ${LIB} ${OBJ} 
+	${RANLIB} ${LIB}
+
+.cxx.o:
+	${CXX} ${CFLAGS} -c $<
+
+clean:
+	rm -f *.o
+
+depend:
+	(sed '/^# DO NOT DELETE THIS LINE/q' Makefile && \
+	${CXX} -MM ${CFLAGS} ${SRC} \
+	) >Makefile.new
+	cp Makefile Makefile.bak
+	cp Makefile.new Makefile
+	rm -f Makefile.new
+
+# DO NOT DELETE THIS LINE
+predicates.o: predicates.cxx tetgen.h
+tetgen.o: tetgen.cxx tetgen.h
diff --git a/contrib/Tetgen/predicates.cxx b/contrib/Tetgen/predicates.cxx
new file mode 100644
index 0000000000..e3dd38ae48
--- /dev/null
+++ b/contrib/Tetgen/predicates.cxx
@@ -0,0 +1,4176 @@
+/*****************************************************************************/
+/*                                                                           */
+/*  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 "tetgen.h"            // Defines the symbol REAL (float or double).
+
+/* 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 Fast_Two_Diff(a, b, x, y) \
+  x = (REAL) (a - b); \
+  Fast_Two_Diff_Tail(a, b, x, y)
+
+#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 Four_Two_Sum(a3, a2, a1, a0, b1, b0, x5, x4, x3, x2, x1, x0) \
+  Four_One_Sum(a3, a2, a1, a0, b0, _k, _2, _1, _0, x0); \
+  Four_One_Sum(_k, _2, _1, _0, b1, x5, x4, x3, x2, x1)
+
+#define Four_Four_Sum(a3, a2, a1, a0, b4, b3, b1, b0, x7, x6, x5, x4, x3, x2, \
+                      x1, x0) \
+  Four_Two_Sum(a3, a2, a1, a0, b1, b0, _l, _2, _1, _0, x1, x0); \
+  Four_Two_Sum(_l, _2, _1, _0, b4, b3, x7, x6, x5, x4, x3, x2)
+
+#define Eight_One_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b, x8, x7, x6, x5, x4, \
+                      x3, x2, x1, x0) \
+  Four_One_Sum(a3, a2, a1, a0, b , _j, x3, x2, x1, x0); \
+  Four_One_Sum(a7, a6, a5, a4, _j, x8, x7, x6, x5, x4)
+
+#define Eight_Two_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b1, b0, x9, x8, x7, \
+                      x6, x5, x4, x3, x2, x1, x0) \
+  Eight_One_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b0, _k, _6, _5, _4, _3, _2, \
+                _1, _0, x0); \
+  Eight_One_Sum(_k, _6, _5, _4, _3, _2, _1, _0, b1, x9, x8, x7, x6, x5, x4, \
+                x3, x2, x1)
+
+#define Eight_Four_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b4, b3, b1, b0, x11, \
+                       x10, x9, x8, x7, x6, x5, x4, x3, x2, x1, x0) \
+  Eight_Two_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b1, b0, _l, _6, _5, _4, _3, \
+                _2, _1, _0, x1, x0); \
+  Eight_Two_Sum(_l, _6, _5, _4, _3, _2, _1, _0, b4, b3, x11, x10, x9, x8, \
+                x7, x6, x5, x4, x3, x2)
+
+/* Macros for multiplying expansions of various fixed lengths.               */
+
+#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 Four_One_Product(a3, a2, a1, a0, b, x7, x6, x5, x4, 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, _i, x2); \
+  Two_Product_Presplit(a2, b, bhi, blo, _j, _0); \
+  Two_Sum(_i, _0, _k, x3); \
+  Fast_Two_Sum(_j, _k, _i, x4); \
+  Two_Product_Presplit(a3, b, bhi, blo, _j, _0); \
+  Two_Sum(_i, _0, _k, x5); \
+  Fast_Two_Sum(_j, _k, x7, x6)
+
+#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.           */
+static 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 o3derrboundA, o3derrboundB, o3derrboundC;
+static REAL iccerrboundA, iccerrboundB, iccerrboundC;
+static REAL isperrboundA, isperrboundB, isperrboundC;
+
+/*****************************************************************************/
+/*                                                                           */
+/*  doubleprint()   Print the bit representation of a double.                */
+/*                                                                           */
+/*  Useful for debugging exact arithmetic routines.                          */
+/*                                                                           */
+/*****************************************************************************/
+
+/*
+void doubleprint(number)
+double number;
+{
+  unsigned long long no;
+  unsigned long long sign, expo;
+  int exponent;
+  int i, bottomi;
+
+  no = *(unsigned long long *) &number;
+  sign = no & 0x8000000000000000ll;
+  expo = (no >> 52) & 0x7ffll;
+  exponent = (int) expo;
+  exponent = exponent - 1023;
+  if (sign) {
+    printf("-");
+  } else {
+    printf(" ");
+  }
+  if (exponent == -1023) {
+    printf(
+      "0.0000000000000000000000000000000000000000000000000000_     (   )");
+  } else {
+    printf("1.");
+    bottomi = -1;
+    for (i = 0; i < 52; i++) {
+      if (no & 0x0008000000000000ll) {
+        printf("1");
+        bottomi = i;
+      } else {
+        printf("0");
+      }
+      no <<= 1;
+    }
+    printf("_%d  (%d)", exponent, exponent - 1 - bottomi);
+  }
+}
+*/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  floatprint()   Print the bit representation of a float.                  */
+/*                                                                           */
+/*  Useful for debugging exact arithmetic routines.                          */
+/*                                                                           */
+/*****************************************************************************/
+
+/*
+void floatprint(number)
+float number;
+{
+  unsigned no;
+  unsigned sign, expo;
+  int exponent;
+  int i, bottomi;
+
+  no = *(unsigned *) &number;
+  sign = no & 0x80000000;
+  expo = (no >> 23) & 0xff;
+  exponent = (int) expo;
+  exponent = exponent - 127;
+  if (sign) {
+    printf("-");
+  } else {
+    printf(" ");
+  }
+  if (exponent == -127) {
+    printf("0.00000000000000000000000_     (   )");
+  } else {
+    printf("1.");
+    bottomi = -1;
+    for (i = 0; i < 23; i++) {
+      if (no & 0x00400000) {
+        printf("1");
+        bottomi = i;
+      } else {
+        printf("0");
+      }
+      no <<= 1;
+    }
+    printf("_%3d  (%3d)", exponent, exponent - 1 - bottomi);
+  }
+}
+*/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  expansion_print()   Print the bit representation of an expansion.        */
+/*                                                                           */
+/*  Useful for debugging exact arithmetic routines.                          */
+/*                                                                           */
+/*****************************************************************************/
+
+/*
+void expansion_print(elen, e)
+int elen;
+REAL *e;
+{
+  int i;
+
+  for (i = elen - 1; i >= 0; i--) {
+    REALPRINT(e[i]);
+    if (i > 0) {
+      printf(" +\n");
+    } else {
+      printf("\n");
+    }
+  }
+}
+*/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  doublerand()   Generate a double with random 53-bit significand and a    */
+/*                 random exponent in [0, 511].                              */
+/*                                                                           */
+/*****************************************************************************/
+
+/*
+double doublerand()
+{
+  double result;
+  double expo;
+  long a, b, c;
+  long i;
+
+  a = random();
+  b = random();
+  c = random();
+  result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8);
+  for (i = 512, expo = 2; i <= 131072; i *= 2, expo = expo * expo) {
+    if (c & i) {
+      result *= expo;
+    }
+  }
+  return result;
+}
+*/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  narrowdoublerand()   Generate a double with random 53-bit significand    */
+/*                       and a random exponent in [0, 7].                    */
+/*                                                                           */
+/*****************************************************************************/
+
+/*
+double narrowdoublerand()
+{
+  double result;
+  double expo;
+  long a, b, c;
+  long i;
+
+  a = random();
+  b = random();
+  c = random();
+  result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8);
+  for (i = 512, expo = 2; i <= 2048; i *= 2, expo = expo * expo) {
+    if (c & i) {
+      result *= expo;
+    }
+  }
+  return result;
+}
+*/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  uniformdoublerand()   Generate a double with random 53-bit significand.  */
+/*                                                                           */
+/*****************************************************************************/
+
+/*
+double uniformdoublerand()
+{
+  double result;
+  long a, b;
+
+  a = random();
+  b = random();
+  result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8);
+  return result;
+}
+*/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  floatrand()   Generate a float with random 24-bit significand and a      */
+/*                random exponent in [0, 63].                                */
+/*                                                                           */
+/*****************************************************************************/
+
+/*
+float floatrand()
+{
+  float result;
+  float expo;
+  long a, c;
+  long i;
+
+  a = random();
+  c = random();
+  result = (float) ((a - 1073741824) >> 6);
+  for (i = 512, expo = 2; i <= 16384; i *= 2, expo = expo * expo) {
+    if (c & i) {
+      result *= expo;
+    }
+  }
+  return result;
+}
+*/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  narrowfloatrand()   Generate a float with random 24-bit significand and  */
+/*                      a random exponent in [0, 7].                         */
+/*                                                                           */
+/*****************************************************************************/
+
+/*
+float narrowfloatrand()
+{
+  float result;
+  float expo;
+  long a, c;
+  long i;
+
+  a = random();
+  c = random();
+  result = (float) ((a - 1073741824) >> 6);
+  for (i = 512, expo = 2; i <= 2048; i *= 2, expo = expo * expo) {
+    if (c & i) {
+      result *= expo;
+    }
+  }
+  return result;
+}
+*/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  uniformfloatrand()   Generate a float with random 24-bit significand.    */
+/*                                                                           */
+/*****************************************************************************/
+
+/*
+float uniformfloatrand()
+{
+  float result;
+  long a;
+
+  a = random();
+  result = (float) ((a - 1073741824) >> 6);
+  return result;
+}
+*/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  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.                */
+/*                                                                           */
+/*****************************************************************************/
+
+REAL exactinit()
+{
+  REAL half;
+  REAL check, lastcheck;
+  int every_other;
+#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 */
+
+  every_other = 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 (every_other) {
+      splitter *= 2.0;
+    }
+    every_other = !every_other;
+    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;
+
+  return epsilon; /* Added by H. Si 30 Juli, 2004. */
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  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, 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, 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;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  expansion_sum()   Sum two expansions.                                    */
+/*                                                                           */
+/*  Sets h = e + f.  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 nonadjacent property as well.  (That is,   */
+/*  if e has one of these properties, so will h.)  Does NOT maintain the     */
+/*  strongly nonoverlapping property.                                        */
+/*                                                                           */
+/*****************************************************************************/
+
+int expansion_sum(int elen, REAL *e, int flen, REAL *f, REAL *h)
+/* e and h can be the same, but f and h cannot. */
+{
+  REAL Q;
+  INEXACT REAL Qnew;
+  int findex, hindex, hlast;
+  REAL hnow;
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+
+  Q = f[0];
+  for (hindex = 0; hindex < elen; hindex++) {
+    hnow = e[hindex];
+    Two_Sum(Q, hnow, Qnew, h[hindex]);
+    Q = Qnew;
+  }
+  h[hindex] = Q;
+  hlast = hindex;
+  for (findex = 1; findex < flen; findex++) {
+    Q = f[findex];
+    for (hindex = findex; hindex <= hlast; hindex++) {
+      hnow = h[hindex];
+      Two_Sum(Q, hnow, Qnew, h[hindex]);
+      Q = Qnew;
+    }
+    h[++hlast] = Q;
+  }
+  return hlast + 1;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  expansion_sum_zeroelim1()   Sum two expansions, eliminating zero         */
+/*                              components from the output expansion.        */
+/*                                                                           */
+/*  Sets h = e + f.  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 nonadjacent property as well.  (That is,   */
+/*  if e has one of these properties, so will h.)  Does NOT maintain the     */
+/*  strongly nonoverlapping property.                                        */
+/*                                                                           */
+/*****************************************************************************/
+
+int expansion_sum_zeroelim1(int elen, REAL *e, int flen, REAL *f, REAL *h)
+/* e and h can be the same, but f and h cannot. */
+{
+  REAL Q;
+  INEXACT REAL Qnew;
+  int index, findex, hindex, hlast;
+  REAL hnow;
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+
+  Q = f[0];
+  for (hindex = 0; hindex < elen; hindex++) {
+    hnow = e[hindex];
+    Two_Sum(Q, hnow, Qnew, h[hindex]);
+    Q = Qnew;
+  }
+  h[hindex] = Q;
+  hlast = hindex;
+  for (findex = 1; findex < flen; findex++) {
+    Q = f[findex];
+    for (hindex = findex; hindex <= hlast; hindex++) {
+      hnow = h[hindex];
+      Two_Sum(Q, hnow, Qnew, h[hindex]);
+      Q = Qnew;
+    }
+    h[++hlast] = Q;
+  }
+  hindex = -1;
+  for (index = 0; index <= hlast; index++) {
+    hnow = h[index];
+    if (hnow != 0.0) {
+      h[++hindex] = hnow;
+    }
+  }
+  if (hindex == -1) {
+    return 1;
+  } else {
+    return hindex + 1;
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  expansion_sum_zeroelim2()   Sum two expansions, eliminating zero         */
+/*                              components from the output expansion.        */
+/*                                                                           */
+/*  Sets h = e + f.  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 nonadjacent property as well.  (That is,   */
+/*  if e has one of these properties, so will h.)  Does NOT maintain the     */
+/*  strongly nonoverlapping property.                                        */
+/*                                                                           */
+/*****************************************************************************/
+
+int expansion_sum_zeroelim2(int elen, REAL *e, int flen, REAL *f, REAL *h)
+/* e and h can be the same, but f and h cannot. */
+{
+  REAL Q, hh;
+  INEXACT REAL Qnew;
+  int eindex, findex, hindex, hlast;
+  REAL enow;
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+
+  hindex = 0;
+  Q = f[0];
+  for (eindex = 0; eindex < elen; eindex++) {
+    enow = e[eindex];
+    Two_Sum(Q, enow, Qnew, hh);
+    Q = Qnew;
+    if (hh != 0.0) {
+      h[hindex++] = hh;
+    }
+  }
+  h[hindex] = Q;
+  hlast = hindex;
+  for (findex = 1; findex < flen; findex++) {
+    hindex = 0;
+    Q = f[findex];
+    for (eindex = 0; eindex <= hlast; eindex++) {
+      enow = h[eindex];
+      Two_Sum(Q, enow, Qnew, hh);
+      Q = Qnew;
+      if (hh != 0) {
+        h[hindex++] = hh;
+      }
+    }
+    h[hindex] = Q;
+    hlast = hindex;
+  }
+  return hlast + 1;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  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, REAL *e, int flen, 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;
+    enow = e[++eindex];
+  } else {
+    Q = fnow;
+    fnow = f[++findex];
+  }
+  hindex = 0;
+  if ((eindex < elen) && (findex < flen)) {
+    if ((fnow > enow) == (fnow > -enow)) {
+      Fast_Two_Sum(enow, Q, Qnew, h[0]);
+      enow = e[++eindex];
+    } else {
+      Fast_Two_Sum(fnow, Q, Qnew, h[0]);
+      fnow = f[++findex];
+    }
+    Q = Qnew;
+    hindex = 1;
+    while ((eindex < elen) && (findex < flen)) {
+      if ((fnow > enow) == (fnow > -enow)) {
+        Two_Sum(Q, enow, Qnew, h[hindex]);
+        enow = e[++eindex];
+      } else {
+        Two_Sum(Q, fnow, Qnew, h[hindex]);
+        fnow = f[++findex];
+      }
+      Q = Qnew;
+      hindex++;
+    }
+  }
+  while (eindex < elen) {
+    Two_Sum(Q, enow, Qnew, h[hindex]);
+    enow = e[++eindex];
+    Q = Qnew;
+    hindex++;
+  }
+  while (findex < flen) {
+    Two_Sum(Q, fnow, Qnew, h[hindex]);
+    fnow = f[++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(int elen, REAL *e, int flen, 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;
+
+  enow = e[0];
+  fnow = f[0];
+  eindex = findex = 0;
+  if ((fnow > enow) == (fnow > -enow)) {
+    Q = enow;
+    enow = e[++eindex];
+  } else {
+    Q = fnow;
+    fnow = f[++findex];
+  }
+  hindex = 0;
+  if ((eindex < elen) && (findex < flen)) {
+    if ((fnow > enow) == (fnow > -enow)) {
+      Fast_Two_Sum(enow, Q, Qnew, hh);
+      enow = e[++eindex];
+    } else {
+      Fast_Two_Sum(fnow, Q, Qnew, hh);
+      fnow = f[++findex];
+    }
+    Q = Qnew;
+    if (hh != 0.0) {
+      h[hindex++] = hh;
+    }
+    while ((eindex < elen) && (findex < flen)) {
+      if ((fnow > enow) == (fnow > -enow)) {
+        Two_Sum(Q, enow, Qnew, hh);
+        enow = e[++eindex];
+      } else {
+        Two_Sum(Q, fnow, Qnew, hh);
+        fnow = f[++findex];
+      }
+      Q = Qnew;
+      if (hh != 0.0) {
+        h[hindex++] = hh;
+      }
+    }
+  }
+  while (eindex < elen) {
+    Two_Sum(Q, enow, Qnew, hh);
+    enow = e[++eindex];
+    Q = Qnew;
+    if (hh != 0.0) {
+      h[hindex++] = hh;
+    }
+  }
+  while (findex < flen) {
+    Two_Sum(Q, fnow, Qnew, hh);
+    fnow = f[++findex];
+    Q = Qnew;
+    if (hh != 0.0) {
+      h[hindex++] = hh;
+    }
+  }
+  if ((Q != 0.0) || (hindex == 0)) {
+    h[hindex++] = Q;
+  }
+  return hindex;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  linear_expansion_sum()   Sum two expansions.                             */
+/*                                                                           */
+/*  Sets h = e + f.  See either version of my paper for details.             */
+/*                                                                           */
+/*  Maintains the nonoverlapping property.  (That is, if e is                */
+/*  nonoverlapping, h will be also.)                                         */
+/*                                                                           */
+/*****************************************************************************/
+
+int linear_expansion_sum(int elen, REAL *e, int flen, REAL *f, REAL *h)
+/* h cannot be e or f. */
+{
+  REAL Q, q;
+  INEXACT REAL Qnew;
+  INEXACT REAL R;
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  int eindex, findex, hindex;
+  REAL enow, fnow;
+  REAL g0;
+
+  enow = e[0];
+  fnow = f[0];
+  eindex = findex = 0;
+  if ((fnow > enow) == (fnow > -enow)) {
+    g0 = enow;
+    enow = e[++eindex];
+  } else {
+    g0 = fnow;
+    fnow = f[++findex];
+  }
+  if ((eindex < elen) && ((findex >= flen)
+                          || ((fnow > enow) == (fnow > -enow)))) {
+    Fast_Two_Sum(enow, g0, Qnew, q);
+    enow = e[++eindex];
+  } else {
+    Fast_Two_Sum(fnow, g0, Qnew, q);
+    fnow = f[++findex];
+  }
+  Q = Qnew;
+  for (hindex = 0; hindex < elen + flen - 2; hindex++) {
+    if ((eindex < elen) && ((findex >= flen)
+                            || ((fnow > enow) == (fnow > -enow)))) {
+      Fast_Two_Sum(enow, q, R, h[hindex]);
+      enow = e[++eindex];
+    } else {
+      Fast_Two_Sum(fnow, q, R, h[hindex]);
+      fnow = f[++findex];
+    }
+    Two_Sum(Q, R, Qnew, q);
+    Q = Qnew;
+  }
+  h[hindex] = q;
+  h[hindex + 1] = Q;
+  return hindex + 2;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  linear_expansion_sum_zeroelim()   Sum two expansions, eliminating zero   */
+/*                                    components from the output expansion.  */
+/*                                                                           */
+/*  Sets h = e + f.  See either version of my paper for details.             */
+/*                                                                           */
+/*  Maintains the nonoverlapping property.  (That is, if e is                */
+/*  nonoverlapping, h will be also.)                                         */
+/*                                                                           */
+/*****************************************************************************/
+
+int linear_expansion_sum_zeroelim(int elen, REAL *e, int flen, REAL *f,
+                                  REAL *h)
+/* h cannot be e or f. */
+{
+  REAL Q, q, hh;
+  INEXACT REAL Qnew;
+  INEXACT REAL R;
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  int eindex, findex, hindex;
+  int count;
+  REAL enow, fnow;
+  REAL g0;
+
+  enow = e[0];
+  fnow = f[0];
+  eindex = findex = 0;
+  hindex = 0;
+  if ((fnow > enow) == (fnow > -enow)) {
+    g0 = enow;
+    enow = e[++eindex];
+  } else {
+    g0 = fnow;
+    fnow = f[++findex];
+  }
+  if ((eindex < elen) && ((findex >= flen)
+                          || ((fnow > enow) == (fnow > -enow)))) {
+    Fast_Two_Sum(enow, g0, Qnew, q);
+    enow = e[++eindex];
+  } else {
+    Fast_Two_Sum(fnow, g0, Qnew, q);
+    fnow = f[++findex];
+  }
+  Q = Qnew;
+  for (count = 2; count < elen + flen; count++) {
+    if ((eindex < elen) && ((findex >= flen)
+                            || ((fnow > enow) == (fnow > -enow)))) {
+      Fast_Two_Sum(enow, q, R, hh);
+      enow = e[++eindex];
+    } else {
+      Fast_Two_Sum(fnow, q, R, hh);
+      fnow = f[++findex];
+    }
+    Two_Sum(Q, R, Qnew, q);
+    Q = Qnew;
+    if (hh != 0) {
+      h[hindex++] = hh;
+    }
+  }
+  if (q != 0) {
+    h[hindex++] = q;
+  }
+  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(int elen, REAL *e, 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;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  compress()   Compress an expansion.                                      */
+/*                                                                           */
+/*  See the long version of my paper for details.                            */
+/*                                                                           */
+/*  Maintains the nonoverlapping property.  If round-to-even is used (as     */
+/*  with IEEE 754), then any nonoverlapping expansion is converted to a      */
+/*  nonadjacent expansion.                                                   */
+/*                                                                           */
+/*****************************************************************************/
+
+int compress(int elen, REAL *e, REAL *h)
+/* e and h may be the same. */
+{
+  REAL Q, q;
+  INEXACT REAL Qnew;
+  int eindex, hindex;
+  INEXACT REAL bvirt;
+  REAL enow, hnow;
+  int top, bottom;
+
+  bottom = elen - 1;
+  Q = e[bottom];
+  for (eindex = elen - 2; eindex >= 0; eindex--) {
+    enow = e[eindex];
+    Fast_Two_Sum(Q, enow, Qnew, q);
+    if (q != 0) {
+      h[bottom--] = Qnew;
+      Q = q;
+    } else {
+      Q = Qnew;
+    }
+  }
+  top = 0;
+  for (hindex = bottom + 1; hindex < elen; hindex++) {
+    hnow = h[hindex];
+    Fast_Two_Sum(hnow, Q, Qnew, q);
+    if (q != 0) {
+      h[top++] = q;
+    }
+    Q = Qnew;
+  }
+  h[top] = Q;
+  return top + 1;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  estimate()   Produce a one-word estimate of an expansion's value.        */
+/*                                                                           */
+/*  See either version of my paper for details.                              */
+/*                                                                           */
+/*****************************************************************************/
+
+REAL estimate(int elen, 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(REAL *pa, REAL *pb, REAL *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 orient2dexact(REAL *pa, REAL *pb, REAL *pc)
+{
+  INEXACT REAL axby1, axcy1, bxcy1, bxay1, cxay1, cxby1;
+  REAL axby0, axcy0, bxcy0, bxay0, cxay0, cxby0;
+  REAL aterms[4], bterms[4], cterms[4];
+  INEXACT REAL aterms3, bterms3, cterms3;
+  REAL v[8], w[12];
+  int vlength, wlength;
+
+  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(pa[0], pc[1], axcy1, axcy0);
+  Two_Two_Diff(axby1, axby0, axcy1, axcy0,
+               aterms3, aterms[2], aterms[1], aterms[0]);
+  aterms[3] = aterms3;
+
+  Two_Product(pb[0], pc[1], bxcy1, bxcy0);
+  Two_Product(pb[0], pa[1], bxay1, bxay0);
+  Two_Two_Diff(bxcy1, bxcy0, bxay1, bxay0,
+               bterms3, bterms[2], bterms[1], bterms[0]);
+  bterms[3] = bterms3;
+
+  Two_Product(pc[0], pa[1], cxay1, cxay0);
+  Two_Product(pc[0], pb[1], cxby1, cxby0);
+  Two_Two_Diff(cxay1, cxay0, cxby1, cxby0,
+               cterms3, cterms[2], cterms[1], cterms[0]);
+  cterms[3] = cterms3;
+
+  vlength = fast_expansion_sum_zeroelim(4, aterms, 4, bterms, v);
+  wlength = fast_expansion_sum_zeroelim(vlength, v, 4, cterms, w);
+
+  return w[wlength - 1];
+}
+
+REAL orient2dslow(REAL *pa, REAL *pb, REAL *pc)
+{
+  INEXACT REAL acx, acy, bcx, bcy;
+  REAL acxtail, acytail;
+  REAL bcxtail, bcytail;
+  REAL negate, negatetail;
+  REAL axby[8], bxay[8];
+  INEXACT REAL axby7, bxay7;
+  REAL deter[16];
+  int deterlen;
+
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  INEXACT REAL c;
+  INEXACT REAL abig;
+  REAL a0hi, a0lo, a1hi, a1lo, bhi, blo;
+  REAL err1, err2, err3;
+  INEXACT REAL _i, _j, _k, _l, _m, _n;
+  REAL _0, _1, _2;
+
+  Two_Diff(pa[0], pc[0], acx, acxtail);
+  Two_Diff(pa[1], pc[1], acy, acytail);
+  Two_Diff(pb[0], pc[0], bcx, bcxtail);
+  Two_Diff(pb[1], pc[1], bcy, bcytail);
+
+  Two_Two_Product(acx, acxtail, bcy, bcytail,
+                  axby7, axby[6], axby[5], axby[4],
+                  axby[3], axby[2], axby[1], axby[0]);
+  axby[7] = axby7;
+  negate = -acy;
+  negatetail = -acytail;
+  Two_Two_Product(bcx, bcxtail, negate, negatetail,
+                  bxay7, bxay[6], bxay[5], bxay[4],
+                  bxay[3], bxay[2], bxay[1], bxay[0]);
+  bxay[7] = bxay7;
+
+  deterlen = fast_expansion_sum_zeroelim(8, axby, 8, bxay, deter);
+
+  return deter[deterlen - 1];
+}
+
+REAL orient2dadapt(REAL *pa, REAL *pb, REAL *pc, 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(REAL *pa, REAL *pb, REAL *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.                                                               */
+/*                                                                           */
+/*****************************************************************************/
+
+REAL 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);
+}
+
+REAL orient3dexact(REAL *pa, REAL *pb, REAL *pc, REAL *pd)
+{
+  INEXACT REAL axby1, bxcy1, cxdy1, dxay1, axcy1, bxdy1;
+  INEXACT REAL bxay1, cxby1, dxcy1, axdy1, cxay1, dxby1;
+  REAL axby0, bxcy0, cxdy0, dxay0, axcy0, bxdy0;
+  REAL bxay0, cxby0, dxcy0, axdy0, cxay0, dxby0;
+  REAL ab[4], bc[4], cd[4], da[4], ac[4], bd[4];
+  REAL temp8[8];
+  int templen;
+  REAL abc[12], bcd[12], cda[12], dab[12];
+  int abclen, bcdlen, cdalen, dablen;
+  REAL adet[24], bdet[24], cdet[24], ddet[24];
+  int alen, blen, clen, dlen;
+  REAL abdet[48], cddet[48];
+  int ablen, cdlen;
+  REAL deter[96];
+  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], 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(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]);
+
+  templen = fast_expansion_sum_zeroelim(4, cd, 4, da, temp8);
+  cdalen = fast_expansion_sum_zeroelim(templen, temp8, 4, ac, cda);
+  templen = fast_expansion_sum_zeroelim(4, da, 4, ab, temp8);
+  dablen = fast_expansion_sum_zeroelim(templen, temp8, 4, bd, dab);
+  for (i = 0; i < 4; i++) {
+    bd[i] = -bd[i];
+    ac[i] = -ac[i];
+  }
+  templen = fast_expansion_sum_zeroelim(4, ab, 4, bc, temp8);
+  abclen = fast_expansion_sum_zeroelim(templen, temp8, 4, ac, abc);
+  templen = fast_expansion_sum_zeroelim(4, bc, 4, cd, temp8);
+  bcdlen = fast_expansion_sum_zeroelim(templen, temp8, 4, bd, bcd);
+
+  alen = scale_expansion_zeroelim(bcdlen, bcd, pa[2], adet);
+  blen = scale_expansion_zeroelim(cdalen, cda, -pb[2], bdet);
+  clen = scale_expansion_zeroelim(dablen, dab, pc[2], cdet);
+  dlen = scale_expansion_zeroelim(abclen, abc, -pd[2], ddet);
+
+  ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+  cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet);
+  deterlen = fast_expansion_sum_zeroelim(ablen, abdet, cdlen, cddet, deter);
+
+  return deter[deterlen - 1];
+}
+
+REAL orient3dslow(REAL *pa, REAL *pb, REAL *pc, REAL *pd)
+{
+  INEXACT REAL adx, ady, adz, bdx, bdy, bdz, cdx, cdy, cdz;
+  REAL adxtail, adytail, adztail;
+  REAL bdxtail, bdytail, bdztail;
+  REAL cdxtail, cdytail, cdztail;
+  REAL negate, negatetail;
+  INEXACT REAL axby7, bxcy7, axcy7, bxay7, cxby7, cxay7;
+  REAL axby[8], bxcy[8], axcy[8], bxay[8], cxby[8], cxay[8];
+  REAL temp16[16], temp32[32], temp32t[32];
+  int temp16len, temp32len, temp32tlen;
+  REAL adet[64], bdet[64], cdet[64];
+  int alen, blen, clen;
+  REAL abdet[128];
+  int ablen;
+  REAL deter[192];
+  int deterlen;
+
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  INEXACT REAL c;
+  INEXACT REAL abig;
+  REAL a0hi, a0lo, a1hi, a1lo, bhi, blo;
+  REAL err1, err2, err3;
+  INEXACT REAL _i, _j, _k, _l, _m, _n;
+  REAL _0, _1, _2;
+
+  Two_Diff(pa[0], pd[0], adx, adxtail);
+  Two_Diff(pa[1], pd[1], ady, adytail);
+  Two_Diff(pa[2], pd[2], adz, adztail);
+  Two_Diff(pb[0], pd[0], bdx, bdxtail);
+  Two_Diff(pb[1], pd[1], bdy, bdytail);
+  Two_Diff(pb[2], pd[2], bdz, bdztail);
+  Two_Diff(pc[0], pd[0], cdx, cdxtail);
+  Two_Diff(pc[1], pd[1], cdy, cdytail);
+  Two_Diff(pc[2], pd[2], cdz, cdztail);
+
+  Two_Two_Product(adx, adxtail, bdy, bdytail,
+                  axby7, axby[6], axby[5], axby[4],
+                  axby[3], axby[2], axby[1], axby[0]);
+  axby[7] = axby7;
+  negate = -ady;
+  negatetail = -adytail;
+  Two_Two_Product(bdx, bdxtail, negate, negatetail,
+                  bxay7, bxay[6], bxay[5], bxay[4],
+                  bxay[3], bxay[2], bxay[1], bxay[0]);
+  bxay[7] = bxay7;
+  Two_Two_Product(bdx, bdxtail, cdy, cdytail,
+                  bxcy7, bxcy[6], bxcy[5], bxcy[4],
+                  bxcy[3], bxcy[2], bxcy[1], bxcy[0]);
+  bxcy[7] = bxcy7;
+  negate = -bdy;
+  negatetail = -bdytail;
+  Two_Two_Product(cdx, cdxtail, negate, negatetail,
+                  cxby7, cxby[6], cxby[5], cxby[4],
+                  cxby[3], cxby[2], cxby[1], cxby[0]);
+  cxby[7] = cxby7;
+  Two_Two_Product(cdx, cdxtail, ady, adytail,
+                  cxay7, cxay[6], cxay[5], cxay[4],
+                  cxay[3], cxay[2], cxay[1], cxay[0]);
+  cxay[7] = cxay7;
+  negate = -cdy;
+  negatetail = -cdytail;
+  Two_Two_Product(adx, adxtail, negate, negatetail,
+                  axcy7, axcy[6], axcy[5], axcy[4],
+                  axcy[3], axcy[2], axcy[1], axcy[0]);
+  axcy[7] = axcy7;
+
+  temp16len = fast_expansion_sum_zeroelim(8, bxcy, 8, cxby, temp16);
+  temp32len = scale_expansion_zeroelim(temp16len, temp16, adz, temp32);
+  temp32tlen = scale_expansion_zeroelim(temp16len, temp16, adztail, temp32t);
+  alen = fast_expansion_sum_zeroelim(temp32len, temp32, temp32tlen, temp32t,
+                                     adet);
+
+  temp16len = fast_expansion_sum_zeroelim(8, cxay, 8, axcy, temp16);
+  temp32len = scale_expansion_zeroelim(temp16len, temp16, bdz, temp32);
+  temp32tlen = scale_expansion_zeroelim(temp16len, temp16, bdztail, temp32t);
+  blen = fast_expansion_sum_zeroelim(temp32len, temp32, temp32tlen, temp32t,
+                                     bdet);
+
+  temp16len = fast_expansion_sum_zeroelim(8, axby, 8, bxay, temp16);
+  temp32len = scale_expansion_zeroelim(temp16len, temp16, cdz, temp32);
+  temp32tlen = scale_expansion_zeroelim(temp16len, temp16, cdztail, temp32t);
+  clen = fast_expansion_sum_zeroelim(temp32len, temp32, temp32tlen, temp32t,
+                                     cdet);
+
+  ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+  deterlen = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, deter);
+
+  return deter[deterlen - 1];
+}
+
+REAL orient3dadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, 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;
+
+  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 * Absolute(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(REAL *pa, REAL *pb, REAL *pc, REAL *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);
+
+  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;
+  }
+
+  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(REAL *pa, REAL *pb, REAL *pc, REAL *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 incircleexact(REAL *pa, REAL *pb, REAL *pc, REAL *pd)
+{
+  INEXACT REAL axby1, bxcy1, cxdy1, dxay1, axcy1, bxdy1;
+  INEXACT REAL bxay1, cxby1, dxcy1, axdy1, cxay1, dxby1;
+  REAL axby0, bxcy0, cxdy0, dxay0, axcy0, bxdy0;
+  REAL bxay0, cxby0, dxcy0, axdy0, cxay0, dxby0;
+  REAL ab[4], bc[4], cd[4], da[4], ac[4], bd[4];
+  REAL temp8[8];
+  int templen;
+  REAL abc[12], bcd[12], cda[12], dab[12];
+  int abclen, bcdlen, cdalen, dablen;
+  REAL det24x[24], det24y[24], det48x[48], det48y[48];
+  int xlen, ylen;
+  REAL adet[96], bdet[96], cdet[96], ddet[96];
+  int alen, blen, clen, dlen;
+  REAL abdet[192], cddet[192];
+  int ablen, cdlen;
+  REAL deter[384];
+  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], 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(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]);
+
+  templen = fast_expansion_sum_zeroelim(4, cd, 4, da, temp8);
+  cdalen = fast_expansion_sum_zeroelim(templen, temp8, 4, ac, cda);
+  templen = fast_expansion_sum_zeroelim(4, da, 4, ab, temp8);
+  dablen = fast_expansion_sum_zeroelim(templen, temp8, 4, bd, dab);
+  for (i = 0; i < 4; i++) {
+    bd[i] = -bd[i];
+    ac[i] = -ac[i];
+  }
+  templen = fast_expansion_sum_zeroelim(4, ab, 4, bc, temp8);
+  abclen = fast_expansion_sum_zeroelim(templen, temp8, 4, ac, abc);
+  templen = fast_expansion_sum_zeroelim(4, bc, 4, cd, temp8);
+  bcdlen = fast_expansion_sum_zeroelim(templen, temp8, 4, bd, bcd);
+
+  xlen = scale_expansion_zeroelim(bcdlen, bcd, pa[0], det24x);
+  xlen = scale_expansion_zeroelim(xlen, det24x, pa[0], det48x);
+  ylen = scale_expansion_zeroelim(bcdlen, bcd, pa[1], det24y);
+  ylen = scale_expansion_zeroelim(ylen, det24y, pa[1], det48y);
+  alen = fast_expansion_sum_zeroelim(xlen, det48x, ylen, det48y, adet);
+
+  xlen = scale_expansion_zeroelim(cdalen, cda, pb[0], det24x);
+  xlen = scale_expansion_zeroelim(xlen, det24x, -pb[0], det48x);
+  ylen = scale_expansion_zeroelim(cdalen, cda, pb[1], det24y);
+  ylen = scale_expansion_zeroelim(ylen, det24y, -pb[1], det48y);
+  blen = fast_expansion_sum_zeroelim(xlen, det48x, ylen, det48y, bdet);
+
+  xlen = scale_expansion_zeroelim(dablen, dab, pc[0], det24x);
+  xlen = scale_expansion_zeroelim(xlen, det24x, pc[0], det48x);
+  ylen = scale_expansion_zeroelim(dablen, dab, pc[1], det24y);
+  ylen = scale_expansion_zeroelim(ylen, det24y, pc[1], det48y);
+  clen = fast_expansion_sum_zeroelim(xlen, det48x, ylen, det48y, cdet);
+
+  xlen = scale_expansion_zeroelim(abclen, abc, pd[0], det24x);
+  xlen = scale_expansion_zeroelim(xlen, det24x, -pd[0], det48x);
+  ylen = scale_expansion_zeroelim(abclen, abc, pd[1], det24y);
+  ylen = scale_expansion_zeroelim(ylen, det24y, -pd[1], det48y);
+  dlen = fast_expansion_sum_zeroelim(xlen, det48x, ylen, det48y, ddet);
+
+  ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+  cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet);
+  deterlen = fast_expansion_sum_zeroelim(ablen, abdet, cdlen, cddet, deter);
+
+  return deter[deterlen - 1];
+}
+
+REAL incircleslow(REAL *pa, REAL *pb, REAL *pc, REAL *pd)
+{
+  INEXACT REAL adx, bdx, cdx, ady, bdy, cdy;
+  REAL adxtail, bdxtail, cdxtail;
+  REAL adytail, bdytail, cdytail;
+  REAL negate, negatetail;
+  INEXACT REAL axby7, bxcy7, axcy7, bxay7, cxby7, cxay7;
+  REAL axby[8], bxcy[8], axcy[8], bxay[8], cxby[8], cxay[8];
+  REAL temp16[16];
+  int temp16len;
+  REAL detx[32], detxx[64], detxt[32], detxxt[64], detxtxt[64];
+  int xlen, xxlen, xtlen, xxtlen, xtxtlen;
+  REAL x1[128], x2[192];
+  int x1len, x2len;
+  REAL dety[32], detyy[64], detyt[32], detyyt[64], detytyt[64];
+  int ylen, yylen, ytlen, yytlen, ytytlen;
+  REAL y1[128], y2[192];
+  int y1len, y2len;
+  REAL adet[384], bdet[384], cdet[384], abdet[768], deter[1152];
+  int alen, blen, clen, ablen, deterlen;
+  int i;
+
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  INEXACT REAL c;
+  INEXACT REAL abig;
+  REAL a0hi, a0lo, a1hi, a1lo, bhi, blo;
+  REAL err1, err2, err3;
+  INEXACT REAL _i, _j, _k, _l, _m, _n;
+  REAL _0, _1, _2;
+
+  Two_Diff(pa[0], pd[0], adx, adxtail);
+  Two_Diff(pa[1], pd[1], ady, adytail);
+  Two_Diff(pb[0], pd[0], bdx, bdxtail);
+  Two_Diff(pb[1], pd[1], bdy, bdytail);
+  Two_Diff(pc[0], pd[0], cdx, cdxtail);
+  Two_Diff(pc[1], pd[1], cdy, cdytail);
+
+  Two_Two_Product(adx, adxtail, bdy, bdytail,
+                  axby7, axby[6], axby[5], axby[4],
+                  axby[3], axby[2], axby[1], axby[0]);
+  axby[7] = axby7;
+  negate = -ady;
+  negatetail = -adytail;
+  Two_Two_Product(bdx, bdxtail, negate, negatetail,
+                  bxay7, bxay[6], bxay[5], bxay[4],
+                  bxay[3], bxay[2], bxay[1], bxay[0]);
+  bxay[7] = bxay7;
+  Two_Two_Product(bdx, bdxtail, cdy, cdytail,
+                  bxcy7, bxcy[6], bxcy[5], bxcy[4],
+                  bxcy[3], bxcy[2], bxcy[1], bxcy[0]);
+  bxcy[7] = bxcy7;
+  negate = -bdy;
+  negatetail = -bdytail;
+  Two_Two_Product(cdx, cdxtail, negate, negatetail,
+                  cxby7, cxby[6], cxby[5], cxby[4],
+                  cxby[3], cxby[2], cxby[1], cxby[0]);
+  cxby[7] = cxby7;
+  Two_Two_Product(cdx, cdxtail, ady, adytail,
+                  cxay7, cxay[6], cxay[5], cxay[4],
+                  cxay[3], cxay[2], cxay[1], cxay[0]);
+  cxay[7] = cxay7;
+  negate = -cdy;
+  negatetail = -cdytail;
+  Two_Two_Product(adx, adxtail, negate, negatetail,
+                  axcy7, axcy[6], axcy[5], axcy[4],
+                  axcy[3], axcy[2], axcy[1], axcy[0]);
+  axcy[7] = axcy7;
+
+
+  temp16len = fast_expansion_sum_zeroelim(8, bxcy, 8, cxby, temp16);
+
+  xlen = scale_expansion_zeroelim(temp16len, temp16, adx, detx);
+  xxlen = scale_expansion_zeroelim(xlen, detx, adx, detxx);
+  xtlen = scale_expansion_zeroelim(temp16len, temp16, adxtail, detxt);
+  xxtlen = scale_expansion_zeroelim(xtlen, detxt, adx, detxxt);
+  for (i = 0; i < xxtlen; i++) {
+    detxxt[i] *= 2.0;
+  }
+  xtxtlen = scale_expansion_zeroelim(xtlen, detxt, adxtail, detxtxt);
+  x1len = fast_expansion_sum_zeroelim(xxlen, detxx, xxtlen, detxxt, x1);
+  x2len = fast_expansion_sum_zeroelim(x1len, x1, xtxtlen, detxtxt, x2);
+
+  ylen = scale_expansion_zeroelim(temp16len, temp16, ady, dety);
+  yylen = scale_expansion_zeroelim(ylen, dety, ady, detyy);
+  ytlen = scale_expansion_zeroelim(temp16len, temp16, adytail, detyt);
+  yytlen = scale_expansion_zeroelim(ytlen, detyt, ady, detyyt);
+  for (i = 0; i < yytlen; i++) {
+    detyyt[i] *= 2.0;
+  }
+  ytytlen = scale_expansion_zeroelim(ytlen, detyt, adytail, detytyt);
+  y1len = fast_expansion_sum_zeroelim(yylen, detyy, yytlen, detyyt, y1);
+  y2len = fast_expansion_sum_zeroelim(y1len, y1, ytytlen, detytyt, y2);
+
+  alen = fast_expansion_sum_zeroelim(x2len, x2, y2len, y2, adet);
+
+
+  temp16len = fast_expansion_sum_zeroelim(8, cxay, 8, axcy, temp16);
+
+  xlen = scale_expansion_zeroelim(temp16len, temp16, bdx, detx);
+  xxlen = scale_expansion_zeroelim(xlen, detx, bdx, detxx);
+  xtlen = scale_expansion_zeroelim(temp16len, temp16, bdxtail, detxt);
+  xxtlen = scale_expansion_zeroelim(xtlen, detxt, bdx, detxxt);
+  for (i = 0; i < xxtlen; i++) {
+    detxxt[i] *= 2.0;
+  }
+  xtxtlen = scale_expansion_zeroelim(xtlen, detxt, bdxtail, detxtxt);
+  x1len = fast_expansion_sum_zeroelim(xxlen, detxx, xxtlen, detxxt, x1);
+  x2len = fast_expansion_sum_zeroelim(x1len, x1, xtxtlen, detxtxt, x2);
+
+  ylen = scale_expansion_zeroelim(temp16len, temp16, bdy, dety);
+  yylen = scale_expansion_zeroelim(ylen, dety, bdy, detyy);
+  ytlen = scale_expansion_zeroelim(temp16len, temp16, bdytail, detyt);
+  yytlen = scale_expansion_zeroelim(ytlen, detyt, bdy, detyyt);
+  for (i = 0; i < yytlen; i++) {
+    detyyt[i] *= 2.0;
+  }
+  ytytlen = scale_expansion_zeroelim(ytlen, detyt, bdytail, detytyt);
+  y1len = fast_expansion_sum_zeroelim(yylen, detyy, yytlen, detyyt, y1);
+  y2len = fast_expansion_sum_zeroelim(y1len, y1, ytytlen, detytyt, y2);
+
+  blen = fast_expansion_sum_zeroelim(x2len, x2, y2len, y2, bdet);
+
+
+  temp16len = fast_expansion_sum_zeroelim(8, axby, 8, bxay, temp16);
+
+  xlen = scale_expansion_zeroelim(temp16len, temp16, cdx, detx);
+  xxlen = scale_expansion_zeroelim(xlen, detx, cdx, detxx);
+  xtlen = scale_expansion_zeroelim(temp16len, temp16, cdxtail, detxt);
+  xxtlen = scale_expansion_zeroelim(xtlen, detxt, cdx, detxxt);
+  for (i = 0; i < xxtlen; i++) {
+    detxxt[i] *= 2.0;
+  }
+  xtxtlen = scale_expansion_zeroelim(xtlen, detxt, cdxtail, detxtxt);
+  x1len = fast_expansion_sum_zeroelim(xxlen, detxx, xxtlen, detxxt, x1);
+  x2len = fast_expansion_sum_zeroelim(x1len, x1, xtxtlen, detxtxt, x2);
+
+  ylen = scale_expansion_zeroelim(temp16len, temp16, cdy, dety);
+  yylen = scale_expansion_zeroelim(ylen, dety, cdy, detyy);
+  ytlen = scale_expansion_zeroelim(temp16len, temp16, cdytail, detyt);
+  yytlen = scale_expansion_zeroelim(ytlen, detyt, cdy, detyyt);
+  for (i = 0; i < yytlen; i++) {
+    detyyt[i] *= 2.0;
+  }
+  ytytlen = scale_expansion_zeroelim(ytlen, detyt, cdytail, detytyt);
+  y1len = fast_expansion_sum_zeroelim(yylen, detyy, yytlen, detyyt, y1);
+  y2len = fast_expansion_sum_zeroelim(y1len, y1, ytytlen, detytyt, y2);
+
+  clen = fast_expansion_sum_zeroelim(x2len, x2, y2len, y2, cdet);
+
+  ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+  deterlen = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, deter);
+
+  return deter[deterlen - 1];
+}
+
+REAL incircleadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, 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;
+
+  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(REAL *pa, REAL *pb, REAL *pc, REAL *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(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *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(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *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 insphereslow(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe)
+{
+  INEXACT REAL aex, bex, cex, dex, aey, bey, cey, dey, aez, bez, cez, dez;
+  REAL aextail, bextail, cextail, dextail;
+  REAL aeytail, beytail, ceytail, deytail;
+  REAL aeztail, beztail, ceztail, deztail;
+  REAL negate, negatetail;
+  INEXACT REAL axby7, bxcy7, cxdy7, dxay7, axcy7, bxdy7;
+  INEXACT REAL bxay7, cxby7, dxcy7, axdy7, cxay7, dxby7;
+  REAL axby[8], bxcy[8], cxdy[8], dxay[8], axcy[8], bxdy[8];
+  REAL bxay[8], cxby[8], dxcy[8], axdy[8], cxay[8], dxby[8];
+  REAL ab[16], bc[16], cd[16], da[16], ac[16], bd[16];
+  int ablen, bclen, cdlen, dalen, aclen, bdlen;
+  REAL temp32a[32], temp32b[32], temp64a[64], temp64b[64], temp64c[64];
+  int temp32alen, temp32blen, temp64alen, temp64blen, temp64clen;
+  REAL temp128[128], temp192[192];
+  int temp128len, temp192len;
+  REAL detx[384], detxx[768], detxt[384], detxxt[768], detxtxt[768];
+  int xlen, xxlen, xtlen, xxtlen, xtxtlen;
+  REAL x1[1536], x2[2304];
+  int x1len, x2len;
+  REAL dety[384], detyy[768], detyt[384], detyyt[768], detytyt[768];
+  int ylen, yylen, ytlen, yytlen, ytytlen;
+  REAL y1[1536], y2[2304];
+  int y1len, y2len;
+  REAL detz[384], detzz[768], detzt[384], detzzt[768], detztzt[768];
+  int zlen, zzlen, ztlen, zztlen, ztztlen;
+  REAL z1[1536], z2[2304];
+  int z1len, z2len;
+  REAL detxy[4608];
+  int xylen;
+  REAL adet[6912], bdet[6912], cdet[6912], ddet[6912];
+  int alen, blen, clen, dlen;
+  REAL abdet[13824], cddet[13824], deter[27648];
+  int deterlen;
+  int i;
+
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  INEXACT REAL c;
+  INEXACT REAL abig;
+  REAL a0hi, a0lo, a1hi, a1lo, bhi, blo;
+  REAL err1, err2, err3;
+  INEXACT REAL _i, _j, _k, _l, _m, _n;
+  REAL _0, _1, _2;
+
+  Two_Diff(pa[0], pe[0], aex, aextail);
+  Two_Diff(pa[1], pe[1], aey, aeytail);
+  Two_Diff(pa[2], pe[2], aez, aeztail);
+  Two_Diff(pb[0], pe[0], bex, bextail);
+  Two_Diff(pb[1], pe[1], bey, beytail);
+  Two_Diff(pb[2], pe[2], bez, beztail);
+  Two_Diff(pc[0], pe[0], cex, cextail);
+  Two_Diff(pc[1], pe[1], cey, ceytail);
+  Two_Diff(pc[2], pe[2], cez, ceztail);
+  Two_Diff(pd[0], pe[0], dex, dextail);
+  Two_Diff(pd[1], pe[1], dey, deytail);
+  Two_Diff(pd[2], pe[2], dez, deztail);
+
+  Two_Two_Product(aex, aextail, bey, beytail,
+                  axby7, axby[6], axby[5], axby[4],
+                  axby[3], axby[2], axby[1], axby[0]);
+  axby[7] = axby7;
+  negate = -aey;
+  negatetail = -aeytail;
+  Two_Two_Product(bex, bextail, negate, negatetail,
+                  bxay7, bxay[6], bxay[5], bxay[4],
+                  bxay[3], bxay[2], bxay[1], bxay[0]);
+  bxay[7] = bxay7;
+  ablen = fast_expansion_sum_zeroelim(8, axby, 8, bxay, ab);
+  Two_Two_Product(bex, bextail, cey, ceytail,
+                  bxcy7, bxcy[6], bxcy[5], bxcy[4],
+                  bxcy[3], bxcy[2], bxcy[1], bxcy[0]);
+  bxcy[7] = bxcy7;
+  negate = -bey;
+  negatetail = -beytail;
+  Two_Two_Product(cex, cextail, negate, negatetail,
+                  cxby7, cxby[6], cxby[5], cxby[4],
+                  cxby[3], cxby[2], cxby[1], cxby[0]);
+  cxby[7] = cxby7;
+  bclen = fast_expansion_sum_zeroelim(8, bxcy, 8, cxby, bc);
+  Two_Two_Product(cex, cextail, dey, deytail,
+                  cxdy7, cxdy[6], cxdy[5], cxdy[4],
+                  cxdy[3], cxdy[2], cxdy[1], cxdy[0]);
+  cxdy[7] = cxdy7;
+  negate = -cey;
+  negatetail = -ceytail;
+  Two_Two_Product(dex, dextail, negate, negatetail,
+                  dxcy7, dxcy[6], dxcy[5], dxcy[4],
+                  dxcy[3], dxcy[2], dxcy[1], dxcy[0]);
+  dxcy[7] = dxcy7;
+  cdlen = fast_expansion_sum_zeroelim(8, cxdy, 8, dxcy, cd);
+  Two_Two_Product(dex, dextail, aey, aeytail,
+                  dxay7, dxay[6], dxay[5], dxay[4],
+                  dxay[3], dxay[2], dxay[1], dxay[0]);
+  dxay[7] = dxay7;
+  negate = -dey;
+  negatetail = -deytail;
+  Two_Two_Product(aex, aextail, negate, negatetail,
+                  axdy7, axdy[6], axdy[5], axdy[4],
+                  axdy[3], axdy[2], axdy[1], axdy[0]);
+  axdy[7] = axdy7;
+  dalen = fast_expansion_sum_zeroelim(8, dxay, 8, axdy, da);
+  Two_Two_Product(aex, aextail, cey, ceytail,
+                  axcy7, axcy[6], axcy[5], axcy[4],
+                  axcy[3], axcy[2], axcy[1], axcy[0]);
+  axcy[7] = axcy7;
+  negate = -aey;
+  negatetail = -aeytail;
+  Two_Two_Product(cex, cextail, negate, negatetail,
+                  cxay7, cxay[6], cxay[5], cxay[4],
+                  cxay[3], cxay[2], cxay[1], cxay[0]);
+  cxay[7] = cxay7;
+  aclen = fast_expansion_sum_zeroelim(8, axcy, 8, cxay, ac);
+  Two_Two_Product(bex, bextail, dey, deytail,
+                  bxdy7, bxdy[6], bxdy[5], bxdy[4],
+                  bxdy[3], bxdy[2], bxdy[1], bxdy[0]);
+  bxdy[7] = bxdy7;
+  negate = -bey;
+  negatetail = -beytail;
+  Two_Two_Product(dex, dextail, negate, negatetail,
+                  dxby7, dxby[6], dxby[5], dxby[4],
+                  dxby[3], dxby[2], dxby[1], dxby[0]);
+  dxby[7] = dxby7;
+  bdlen = fast_expansion_sum_zeroelim(8, bxdy, 8, dxby, bd);
+
+  temp32alen = scale_expansion_zeroelim(cdlen, cd, -bez, temp32a);
+  temp32blen = scale_expansion_zeroelim(cdlen, cd, -beztail, temp32b);
+  temp64alen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                           temp32blen, temp32b, temp64a);
+  temp32alen = scale_expansion_zeroelim(bdlen, bd, cez, temp32a);
+  temp32blen = scale_expansion_zeroelim(bdlen, bd, ceztail, temp32b);
+  temp64blen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                           temp32blen, temp32b, temp64b);
+  temp32alen = scale_expansion_zeroelim(bclen, bc, -dez, temp32a);
+  temp32blen = scale_expansion_zeroelim(bclen, bc, -deztail, temp32b);
+  temp64clen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                           temp32blen, temp32b, temp64c);
+  temp128len = fast_expansion_sum_zeroelim(temp64alen, temp64a,
+                                           temp64blen, temp64b, temp128);
+  temp192len = fast_expansion_sum_zeroelim(temp64clen, temp64c,
+                                           temp128len, temp128, temp192);
+  xlen = scale_expansion_zeroelim(temp192len, temp192, aex, detx);
+  xxlen = scale_expansion_zeroelim(xlen, detx, aex, detxx);
+  xtlen = scale_expansion_zeroelim(temp192len, temp192, aextail, detxt);
+  xxtlen = scale_expansion_zeroelim(xtlen, detxt, aex, detxxt);
+  for (i = 0; i < xxtlen; i++) {
+    detxxt[i] *= 2.0;
+  }
+  xtxtlen = scale_expansion_zeroelim(xtlen, detxt, aextail, detxtxt);
+  x1len = fast_expansion_sum_zeroelim(xxlen, detxx, xxtlen, detxxt, x1);
+  x2len = fast_expansion_sum_zeroelim(x1len, x1, xtxtlen, detxtxt, x2);
+  ylen = scale_expansion_zeroelim(temp192len, temp192, aey, dety);
+  yylen = scale_expansion_zeroelim(ylen, dety, aey, detyy);
+  ytlen = scale_expansion_zeroelim(temp192len, temp192, aeytail, detyt);
+  yytlen = scale_expansion_zeroelim(ytlen, detyt, aey, detyyt);
+  for (i = 0; i < yytlen; i++) {
+    detyyt[i] *= 2.0;
+  }
+  ytytlen = scale_expansion_zeroelim(ytlen, detyt, aeytail, detytyt);
+  y1len = fast_expansion_sum_zeroelim(yylen, detyy, yytlen, detyyt, y1);
+  y2len = fast_expansion_sum_zeroelim(y1len, y1, ytytlen, detytyt, y2);
+  zlen = scale_expansion_zeroelim(temp192len, temp192, aez, detz);
+  zzlen = scale_expansion_zeroelim(zlen, detz, aez, detzz);
+  ztlen = scale_expansion_zeroelim(temp192len, temp192, aeztail, detzt);
+  zztlen = scale_expansion_zeroelim(ztlen, detzt, aez, detzzt);
+  for (i = 0; i < zztlen; i++) {
+    detzzt[i] *= 2.0;
+  }
+  ztztlen = scale_expansion_zeroelim(ztlen, detzt, aeztail, detztzt);
+  z1len = fast_expansion_sum_zeroelim(zzlen, detzz, zztlen, detzzt, z1);
+  z2len = fast_expansion_sum_zeroelim(z1len, z1, ztztlen, detztzt, z2);
+  xylen = fast_expansion_sum_zeroelim(x2len, x2, y2len, y2, detxy);
+  alen = fast_expansion_sum_zeroelim(z2len, z2, xylen, detxy, adet);
+
+  temp32alen = scale_expansion_zeroelim(dalen, da, cez, temp32a);
+  temp32blen = scale_expansion_zeroelim(dalen, da, ceztail, temp32b);
+  temp64alen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                           temp32blen, temp32b, temp64a);
+  temp32alen = scale_expansion_zeroelim(aclen, ac, dez, temp32a);
+  temp32blen = scale_expansion_zeroelim(aclen, ac, deztail, temp32b);
+  temp64blen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                           temp32blen, temp32b, temp64b);
+  temp32alen = scale_expansion_zeroelim(cdlen, cd, aez, temp32a);
+  temp32blen = scale_expansion_zeroelim(cdlen, cd, aeztail, temp32b);
+  temp64clen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                           temp32blen, temp32b, temp64c);
+  temp128len = fast_expansion_sum_zeroelim(temp64alen, temp64a,
+                                           temp64blen, temp64b, temp128);
+  temp192len = fast_expansion_sum_zeroelim(temp64clen, temp64c,
+                                           temp128len, temp128, temp192);
+  xlen = scale_expansion_zeroelim(temp192len, temp192, bex, detx);
+  xxlen = scale_expansion_zeroelim(xlen, detx, bex, detxx);
+  xtlen = scale_expansion_zeroelim(temp192len, temp192, bextail, detxt);
+  xxtlen = scale_expansion_zeroelim(xtlen, detxt, bex, detxxt);
+  for (i = 0; i < xxtlen; i++) {
+    detxxt[i] *= 2.0;
+  }
+  xtxtlen = scale_expansion_zeroelim(xtlen, detxt, bextail, detxtxt);
+  x1len = fast_expansion_sum_zeroelim(xxlen, detxx, xxtlen, detxxt, x1);
+  x2len = fast_expansion_sum_zeroelim(x1len, x1, xtxtlen, detxtxt, x2);
+  ylen = scale_expansion_zeroelim(temp192len, temp192, bey, dety);
+  yylen = scale_expansion_zeroelim(ylen, dety, bey, detyy);
+  ytlen = scale_expansion_zeroelim(temp192len, temp192, beytail, detyt);
+  yytlen = scale_expansion_zeroelim(ytlen, detyt, bey, detyyt);
+  for (i = 0; i < yytlen; i++) {
+    detyyt[i] *= 2.0;
+  }
+  ytytlen = scale_expansion_zeroelim(ytlen, detyt, beytail, detytyt);
+  y1len = fast_expansion_sum_zeroelim(yylen, detyy, yytlen, detyyt, y1);
+  y2len = fast_expansion_sum_zeroelim(y1len, y1, ytytlen, detytyt, y2);
+  zlen = scale_expansion_zeroelim(temp192len, temp192, bez, detz);
+  zzlen = scale_expansion_zeroelim(zlen, detz, bez, detzz);
+  ztlen = scale_expansion_zeroelim(temp192len, temp192, beztail, detzt);
+  zztlen = scale_expansion_zeroelim(ztlen, detzt, bez, detzzt);
+  for (i = 0; i < zztlen; i++) {
+    detzzt[i] *= 2.0;
+  }
+  ztztlen = scale_expansion_zeroelim(ztlen, detzt, beztail, detztzt);
+  z1len = fast_expansion_sum_zeroelim(zzlen, detzz, zztlen, detzzt, z1);
+  z2len = fast_expansion_sum_zeroelim(z1len, z1, ztztlen, detztzt, z2);
+  xylen = fast_expansion_sum_zeroelim(x2len, x2, y2len, y2, detxy);
+  blen = fast_expansion_sum_zeroelim(z2len, z2, xylen, detxy, bdet);
+
+  temp32alen = scale_expansion_zeroelim(ablen, ab, -dez, temp32a);
+  temp32blen = scale_expansion_zeroelim(ablen, ab, -deztail, temp32b);
+  temp64alen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                           temp32blen, temp32b, temp64a);
+  temp32alen = scale_expansion_zeroelim(bdlen, bd, -aez, temp32a);
+  temp32blen = scale_expansion_zeroelim(bdlen, bd, -aeztail, temp32b);
+  temp64blen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                           temp32blen, temp32b, temp64b);
+  temp32alen = scale_expansion_zeroelim(dalen, da, -bez, temp32a);
+  temp32blen = scale_expansion_zeroelim(dalen, da, -beztail, temp32b);
+  temp64clen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                           temp32blen, temp32b, temp64c);
+  temp128len = fast_expansion_sum_zeroelim(temp64alen, temp64a,
+                                           temp64blen, temp64b, temp128);
+  temp192len = fast_expansion_sum_zeroelim(temp64clen, temp64c,
+                                           temp128len, temp128, temp192);
+  xlen = scale_expansion_zeroelim(temp192len, temp192, cex, detx);
+  xxlen = scale_expansion_zeroelim(xlen, detx, cex, detxx);
+  xtlen = scale_expansion_zeroelim(temp192len, temp192, cextail, detxt);
+  xxtlen = scale_expansion_zeroelim(xtlen, detxt, cex, detxxt);
+  for (i = 0; i < xxtlen; i++) {
+    detxxt[i] *= 2.0;
+  }
+  xtxtlen = scale_expansion_zeroelim(xtlen, detxt, cextail, detxtxt);
+  x1len = fast_expansion_sum_zeroelim(xxlen, detxx, xxtlen, detxxt, x1);
+  x2len = fast_expansion_sum_zeroelim(x1len, x1, xtxtlen, detxtxt, x2);
+  ylen = scale_expansion_zeroelim(temp192len, temp192, cey, dety);
+  yylen = scale_expansion_zeroelim(ylen, dety, cey, detyy);
+  ytlen = scale_expansion_zeroelim(temp192len, temp192, ceytail, detyt);
+  yytlen = scale_expansion_zeroelim(ytlen, detyt, cey, detyyt);
+  for (i = 0; i < yytlen; i++) {
+    detyyt[i] *= 2.0;
+  }
+  ytytlen = scale_expansion_zeroelim(ytlen, detyt, ceytail, detytyt);
+  y1len = fast_expansion_sum_zeroelim(yylen, detyy, yytlen, detyyt, y1);
+  y2len = fast_expansion_sum_zeroelim(y1len, y1, ytytlen, detytyt, y2);
+  zlen = scale_expansion_zeroelim(temp192len, temp192, cez, detz);
+  zzlen = scale_expansion_zeroelim(zlen, detz, cez, detzz);
+  ztlen = scale_expansion_zeroelim(temp192len, temp192, ceztail, detzt);
+  zztlen = scale_expansion_zeroelim(ztlen, detzt, cez, detzzt);
+  for (i = 0; i < zztlen; i++) {
+    detzzt[i] *= 2.0;
+  }
+  ztztlen = scale_expansion_zeroelim(ztlen, detzt, ceztail, detztzt);
+  z1len = fast_expansion_sum_zeroelim(zzlen, detzz, zztlen, detzzt, z1);
+  z2len = fast_expansion_sum_zeroelim(z1len, z1, ztztlen, detztzt, z2);
+  xylen = fast_expansion_sum_zeroelim(x2len, x2, y2len, y2, detxy);
+  clen = fast_expansion_sum_zeroelim(z2len, z2, xylen, detxy, cdet);
+
+  temp32alen = scale_expansion_zeroelim(bclen, bc, aez, temp32a);
+  temp32blen = scale_expansion_zeroelim(bclen, bc, aeztail, temp32b);
+  temp64alen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                           temp32blen, temp32b, temp64a);
+  temp32alen = scale_expansion_zeroelim(aclen, ac, -bez, temp32a);
+  temp32blen = scale_expansion_zeroelim(aclen, ac, -beztail, temp32b);
+  temp64blen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                           temp32blen, temp32b, temp64b);
+  temp32alen = scale_expansion_zeroelim(ablen, ab, cez, temp32a);
+  temp32blen = scale_expansion_zeroelim(ablen, ab, ceztail, temp32b);
+  temp64clen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                           temp32blen, temp32b, temp64c);
+  temp128len = fast_expansion_sum_zeroelim(temp64alen, temp64a,
+                                           temp64blen, temp64b, temp128);
+  temp192len = fast_expansion_sum_zeroelim(temp64clen, temp64c,
+                                           temp128len, temp128, temp192);
+  xlen = scale_expansion_zeroelim(temp192len, temp192, dex, detx);
+  xxlen = scale_expansion_zeroelim(xlen, detx, dex, detxx);
+  xtlen = scale_expansion_zeroelim(temp192len, temp192, dextail, detxt);
+  xxtlen = scale_expansion_zeroelim(xtlen, detxt, dex, detxxt);
+  for (i = 0; i < xxtlen; i++) {
+    detxxt[i] *= 2.0;
+  }
+  xtxtlen = scale_expansion_zeroelim(xtlen, detxt, dextail, detxtxt);
+  x1len = fast_expansion_sum_zeroelim(xxlen, detxx, xxtlen, detxxt, x1);
+  x2len = fast_expansion_sum_zeroelim(x1len, x1, xtxtlen, detxtxt, x2);
+  ylen = scale_expansion_zeroelim(temp192len, temp192, dey, dety);
+  yylen = scale_expansion_zeroelim(ylen, dety, dey, detyy);
+  ytlen = scale_expansion_zeroelim(temp192len, temp192, deytail, detyt);
+  yytlen = scale_expansion_zeroelim(ytlen, detyt, dey, detyyt);
+  for (i = 0; i < yytlen; i++) {
+    detyyt[i] *= 2.0;
+  }
+  ytytlen = scale_expansion_zeroelim(ytlen, detyt, deytail, detytyt);
+  y1len = fast_expansion_sum_zeroelim(yylen, detyy, yytlen, detyyt, y1);
+  y2len = fast_expansion_sum_zeroelim(y1len, y1, ytytlen, detytyt, y2);
+  zlen = scale_expansion_zeroelim(temp192len, temp192, dez, detz);
+  zzlen = scale_expansion_zeroelim(zlen, detz, dez, detzz);
+  ztlen = scale_expansion_zeroelim(temp192len, temp192, deztail, detzt);
+  zztlen = scale_expansion_zeroelim(ztlen, detzt, dez, detzzt);
+  for (i = 0; i < zztlen; i++) {
+    detzzt[i] *= 2.0;
+  }
+  ztztlen = scale_expansion_zeroelim(ztlen, detzt, deztail, detztzt);
+  z1len = fast_expansion_sum_zeroelim(zzlen, detzz, zztlen, detzzt, z1);
+  z2len = fast_expansion_sum_zeroelim(z1len, z1, ztztlen, detztzt, z2);
+  xylen = fast_expansion_sum_zeroelim(x2len, x2, y2len, y2, detxy);
+  dlen = fast_expansion_sum_zeroelim(z2len, z2, xylen, detxy, ddet);
+
+  ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+  cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet);
+  deterlen = fast_expansion_sum_zeroelim(ablen, abdet, cdlen, cddet, deter);
+
+  return deter[deterlen - 1];
+}
+
+REAL insphereadapt(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe,
+                   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 * Absolute(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(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *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 aezplus, bezplus, cezplus, dezplus;
+  REAL aexbeyplus, bexaeyplus, bexceyplus, cexbeyplus;
+  REAL cexdeyplus, dexceyplus, dexaeyplus, aexdeyplus;
+  REAL aexceyplus, cexaeyplus, bexdeyplus, dexbeyplus;
+  REAL det;
+  REAL permanent, errbound;
+
+  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);
+
+  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/Tetgen/tetgen.cxx b/contrib/Tetgen/tetgen.cxx
new file mode 100644
index 0000000000..34721f2b83
--- /dev/null
+++ b/contrib/Tetgen/tetgen.cxx
@@ -0,0 +1,22807 @@
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// TetGen                                                                    //
+//                                                                           //
+// A Quality Tetrahedral Mesh Generator and 3D Delaunay Triangulator         //
+//                                                                           //
+// Version 1.3                                                               //
+// June 13, 2004                                                             //
+//                                                                           //
+// Copyright 2002, 2004                                                      //
+// Hang Si                                                                   //
+// Rathausstr. 9, 10178 Berlin, Germany                                      //
+// si@wias-berlin.de                                                         //
+//                                                                           //
+// You can obtain TetGen via internet: http://tetgen.berlios.de.  It may be  //
+//   freely copied, modified, and redistributed under the copyright notices  //
+//   given in the file LICENSE.                                              //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// tetgen.cxx                                                                //
+//                                                                           //
+// The C++ implementation file of the TetGen library.                        //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+#include "tetgen.h"
+
+//
+// Begin of class 'tetgenio' implementation
+//
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// initialize()    Initialize all variables of 'tetgenio'.                   //
+//                                                                           //
+// It is called by the only class constructor 'tetgenio()' implicitly. Thus, //
+// all variables are guaranteed to be initialized. Each array is initialized //
+// to be a 'NULL' pointer, and its length is equal zero. Some variables have //
+// their default value, 'firstnumber' equals zero, 'mesh_dim' equals 3,  and //
+// 'numberofcorners' equals 4.  Another possible use of this routine is to   //
+// call it before to re-use an object.                                       //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenio::initialize()
+{
+  firstnumber = 0;              // Default item index is numbered from Zero.
+  mesh_dim = 3;                              // Default mesh dimension is 3.
+
+  pointlist = (REAL *) NULL;
+  pointattributelist = (REAL *) NULL;
+  addpointlist = (REAL *) NULL;
+  pointmarkerlist = (int *) NULL;
+  numberofpoints = 0;
+  numberofpointattributes = 0;
+  numberofaddpoints = 0;
+
+  tetrahedronlist = (int *) NULL;
+  tetrahedronattributelist = (REAL *) NULL;
+  tetrahedronvolumelist = (REAL *) NULL;
+  neighborlist = (int *) NULL;
+  numberoftetrahedra = 0;
+  numberofcorners = 4;                   // Default is 4 nodes per element.
+  numberoftetrahedronattributes = 0;
+
+  trifacelist = (int *) NULL;
+  trifacemarkerlist = (int *) NULL;
+  numberoftrifaces = 0; 
+
+  facetlist = (facet *) NULL;
+  facetmarkerlist = (int *) NULL;
+  numberoffacets = 0; 
+
+  edgelist = (int *) NULL;
+  edgemarkerlist = (int *) NULL;
+  numberofedges = 0;
+
+  holelist = (REAL *) NULL;
+  numberofholes = 0;
+
+  regionlist = (REAL *) NULL;
+  numberofregions = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// deinitialize()    Free the memory allocated in 'tetgenio'.                //
+//                                                                           //
+// It is called by the class destructor '~tetgenio()' implicitly. Hence, the //
+// occupied memory by arrays of an object will be automatically released on  //
+// the deletion of the object. However, this routine assumes that the memory //
+// is allocated by C++ memory allocation operator 'new', thus it is freed by //
+// the C++ array deletor 'delete []'. If one uses the C/C++ library function //
+// 'malloc()' to allocate memory for arrays, one has to free them with the   //
+// 'free()' function, and call routine 'initialize()' once to disable this   //
+// routine on deletion of the object.                                        //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenio::deinitialize()
+{
+  if (pointlist != (REAL *) NULL) {
+    delete [] pointlist;
+  }
+  if (pointattributelist != (REAL *) NULL) {
+    delete [] pointattributelist;
+  }
+  if (addpointlist != (REAL *) NULL) {
+    delete [] addpointlist;
+  }
+  if (pointmarkerlist != (int *) NULL) {
+    delete [] pointmarkerlist;
+  }
+
+  if (tetrahedronlist != (int *) NULL) {
+    delete [] tetrahedronlist;
+  }
+  if (tetrahedronattributelist != (REAL *) NULL) {
+    delete [] tetrahedronattributelist;
+  }
+  if (tetrahedronvolumelist != (REAL *) NULL) {
+    delete [] tetrahedronvolumelist;
+  }
+  if (neighborlist != (int *) NULL) {
+    delete [] neighborlist;
+  }
+
+  if (trifacelist != (int *) NULL) {
+    delete [] trifacelist;
+  }
+  if (trifacemarkerlist != (int *) NULL) {
+    delete [] trifacemarkerlist;
+  }
+
+  if (edgelist != (int *) NULL) {
+    delete [] edgelist;
+  }
+  if (edgemarkerlist != (int *) NULL) {
+    delete [] edgemarkerlist;
+  }
+
+  if (facetlist != (facet *) NULL) {
+    facet *f;
+    polygon *p;
+    int i, j;
+    for (i = 0; i < numberoffacets; i++) {
+      f = &facetlist[i];
+      for (j = 0; j < f->numberofpolygons; j++) {
+        p = &f->polygonlist[j];
+        delete [] p->vertexlist;
+      }
+      delete [] f->polygonlist;
+      if (f->holelist != (REAL *) NULL) {
+        delete [] f->holelist;
+      }
+    }
+    delete [] facetlist;
+  }
+  if (facetmarkerlist != (int *) NULL) {
+    delete [] facetmarkerlist;
+  }
+
+  if (holelist != (REAL *) NULL) {
+    delete [] holelist;
+  }
+
+  if (regionlist != (REAL *) NULL) {
+    delete [] regionlist;
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// load_node_call()    Load a list of nodes.                                 //
+//                                                                           //
+// It is a support routine for routines: 'load_nodes()', 'load_poly()', and  //
+// 'load_tetmesh()'.  'infile' is the file handle contains the node list. It //
+// may point to a .node, or .poly or .smesh file.  'markers' indicates each  //
+// node contains an additional marker (integer) or not. 'infilename' is the  //
+// name of the file being read,  it is only appeared in error message.       //
+//                                                                           //
+// The 'firstnumber' (0 or 1) is automatically determined by the number of   //
+// the first index of the first point.                                       //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenio::load_node_call(FILE* infile, int markers, char* infilename)
+{
+  char inputline[INPUTLINESIZE];
+  char *stringptr;
+  REAL x, y, z, attrib;
+  int firstnode, currentmarker;
+  int index, attribindex;
+  int i, j;
+
+  // Initialize 'pointlist', 'pointattributelist', and 'pointmarkerlist'.
+  pointlist = new REAL[numberofpoints * mesh_dim];
+  if (pointlist == (REAL *) NULL) {
+    printf("Error:  Out of memory.\n");
+    exit(1);
+  }
+  if (numberofpointattributes > 0) {
+    pointattributelist = new REAL[numberofpoints * numberofpointattributes];
+    if (pointattributelist == (REAL *) NULL) {
+      printf("Error:  Out of memory.\n");
+      exit(1);
+    }
+  }
+  if (markers) {
+    pointmarkerlist = new int[numberofpoints];
+    if (pointmarkerlist == (int *) NULL) {
+      printf("Error:  Out of memory.\n");
+      exit(1);
+    }
+  }
+
+  // Read the point section.
+  index = 0;
+  attribindex = 0;
+  for (i = 0; i < numberofpoints; i++) {
+    stringptr = readnumberline(inputline, infile, infilename);
+    if (i == 0) {
+      firstnode = (int) strtol (stringptr, &stringptr, 0);
+      if ((firstnode == 0) || (firstnode == 1)) {
+        firstnumber = firstnode;
+      }
+    }
+    stringptr = findnextnumber(stringptr);
+    if (*stringptr == '\0') {
+      printf("Error:  Point %d has no x coordinate.\n", firstnumber + i);
+      break;
+    }
+    x = (REAL) strtod(stringptr, &stringptr);
+    stringptr = findnextnumber(stringptr);
+    if (*stringptr == '\0') {
+      printf("Error:  Point %d has no y coordinate.\n", firstnumber + i);
+      break;
+    }
+    y = (REAL) strtod(stringptr, &stringptr);
+    stringptr = findnextnumber(stringptr);
+    if (*stringptr == '\0') {
+      printf("Error:  Point %d has no z coordinate.\n", firstnumber + i);
+      break;
+    }
+    z = (REAL) strtod(stringptr, &stringptr);
+    pointlist[index++] = x;
+    pointlist[index++] = y;
+    pointlist[index++] = z;
+    // Read the point attributes.
+    for (j = 0; j < numberofpointattributes; j++) {
+      stringptr = findnextnumber(stringptr);
+      if (*stringptr == '\0') {
+        attrib = 0.0;
+      } else {
+        attrib = (REAL) strtod(stringptr, &stringptr);
+      }
+      pointattributelist[attribindex++] = attrib;
+    }
+    if (markers) {
+      // Read a point marker.
+      stringptr = findnextnumber(stringptr);
+      if (*stringptr == '\0') {
+        currentmarker = 0;
+      } else {
+        currentmarker = (int) strtol (stringptr, &stringptr, 0);
+      }
+      pointmarkerlist[i] = currentmarker;
+    }
+  }
+  if (i < numberofpoints) {
+    // Failed to read points due to some error.
+    delete [] pointlist;
+    pointlist = (REAL *) NULL;
+    if (markers) {
+      delete [] pointmarkerlist;
+      pointmarkerlist = (int *) NULL;
+    }
+    if (numberofpointattributes > 0) {
+      delete [] pointattributelist;
+      pointattributelist = (REAL *) NULL;
+    }
+    numberofpoints = 0;
+    return false;
+  }
+  return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// load_node()    Load a list of nodes from a .node file.                    //
+//                                                                           //
+// 'filename' is the inputfile without suffix. The node list is in 'filename.//
+// node'. On completion, the node list is returned in 'pointlist'.           //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenio::load_node(char* filename)
+{
+  FILE *infile;
+  char innodefilename[FILENAMESIZE];
+  char inputline[INPUTLINESIZE];
+  char *stringptr;
+  int markers;
+
+  // Assembling the actual file names we want to open.
+  strcpy(innodefilename, filename);
+  strcat(innodefilename, ".node");
+
+  // Try to open a .node file.
+  infile = fopen(innodefilename, "r");
+  if (infile == (FILE *) NULL) {
+    printf("File I/O Error:  Cannot access file %s.\n", innodefilename);
+    return false;
+  }
+  printf("Opening %s.\n", innodefilename);  
+  // Read number of points, number of dimensions, number of point
+  //   attributes, and number of boundary markers.
+  stringptr = readnumberline(inputline, infile, innodefilename);
+  numberofpoints = (int) strtol (stringptr, &stringptr, 0);
+  stringptr = findnextnumber(stringptr);
+  if (*stringptr == '\0') {
+    mesh_dim = 3;
+  } else {
+    mesh_dim = (int) strtol (stringptr, &stringptr, 0);
+  }
+  stringptr = findnextnumber(stringptr);
+  if (*stringptr == '\0') {
+    numberofpointattributes = 0;
+  } else {
+    numberofpointattributes = (int) strtol (stringptr, &stringptr, 0);
+  }
+  stringptr = findnextnumber(stringptr);
+  if (*stringptr == '\0') {
+    markers = 0;
+  } else {
+    markers = (int) strtol (stringptr, &stringptr, 0);
+  }
+
+  if (mesh_dim != 3) {
+    printf("Error:  load_node() only works for 3D points.\n");
+    fclose(infile);
+    return false;
+  }
+  if (numberofpoints < 4) {
+    printf("File I/O error:  There should have at least 4 points.\n");
+    fclose(infile);
+    return false;
+  }
+
+  // Load the list of nodes.
+  if (!load_node_call(infile, markers, innodefilename)) {
+    fclose(infile);
+    return false;
+  }
+  fclose(infile);
+  return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// load_addnodes()    Load a list of additional nodes into 'addpointlists'.  //
+//                                                                           //
+// 'filename' is the filename of the original inputfile without suffix. The  //
+// additional nodes are found in file 'filename-a.node'.                     //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenio::load_addnodes(char* filename)
+{
+  FILE *infile;
+  char addnodefilename[FILENAMESIZE];
+  char inputline[INPUTLINESIZE];
+  char *stringptr;
+  REAL x, y, z;
+  int index;
+  int i;
+
+  // Additional nodes are saved in file "filename-a.node".
+  strcpy(addnodefilename, filename);
+  strcat(addnodefilename, "-a.node");
+  infile = fopen(addnodefilename, "r");
+  if (infile != (FILE *) NULL) {
+    printf("Opening %s.\n", addnodefilename);
+  } else {
+    // Strange! However, it is not a fatal error.
+    printf("Warning:  Can't opening %s. Skipped.\n", addnodefilename);
+    numberofaddpoints = 0;
+    return false;
+  }
+
+  // Read the number of additional points.
+  stringptr = readnumberline(inputline, infile, addnodefilename);
+  numberofaddpoints = (int) strtol (stringptr, &stringptr, 0);
+  if (numberofaddpoints == 0) {
+    // It looks this file contains no point.
+    fclose(infile);
+    return false; 
+  }
+  // Initialize 'addpointlist';
+  addpointlist = new REAL[numberofaddpoints * mesh_dim];
+  if (addpointlist == (REAL *) NULL) {
+    printf("Error:  Out of memory.\n");
+    exit(1);
+  }
+
+  // Read the list of additional points.
+  index = 0;
+  for (i = 0; i < numberofaddpoints; i++) {
+    stringptr = readnumberline(inputline, infile, addnodefilename);
+    stringptr = findnextnumber(stringptr);
+    if (*stringptr == '\0') {
+      printf("Error:  Point %d has no x coordinate.\n", firstnumber + i);
+      break;
+    }
+    x = (REAL) strtod(stringptr, &stringptr);
+    stringptr = findnextnumber(stringptr);
+    if (*stringptr == '\0') {
+      printf("Error:  Point %d has no y coordinate.\n", firstnumber + i);
+      break;
+    }
+    y = (REAL) strtod(stringptr, &stringptr);
+    stringptr = findnextnumber(stringptr);
+    if (*stringptr == '\0') {
+      printf("Error:  Point %d has no z coordinate.\n", firstnumber + i);
+      break;
+    }
+    z = (REAL) strtod(stringptr, &stringptr);
+    addpointlist[index++] = x;
+    addpointlist[index++] = y;
+    addpointlist[index++] = z;
+  }
+  fclose(infile);
+
+  if (i < numberofaddpoints) {
+    // Failed to read to additional points due to some error.
+    delete [] addpointlist;
+    addpointlist = (REAL *) NULL;
+    numberofaddpoints = 0;
+    return false;
+  }
+  return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// load_poly()    Load a piecewise linear complex described in a .poly or    //
+//                .smesh file.                                               //
+//                                                                           //
+// 'filename' is the inputfile without suffix. The PLC is in 'filename.poly' //
+// or 'filename.smesh', and possibly plus 'filename.node' (when the first    //
+// line of the file starts with a zero). On completion, the PLC is returned  //
+// in 'pointlist', 'facetlist', 'holelist' and 'regionlist'.                 //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenio::load_poly(char* filename)
+{
+  FILE *infile, *polyfile;
+  char innodefilename[FILENAMESIZE];
+  char inpolyfilename[FILENAMESIZE];
+  char insmeshfilename[FILENAMESIZE];
+  char inputline[INPUTLINESIZE];
+  char *stringptr, *infilename;
+  int smesh, markers, currentmarker;
+  int readnodefile, index;
+  int i, j, k;
+
+  // Assembling the actual file names we want to open.
+  strcpy(innodefilename, filename);
+  strcpy(inpolyfilename, filename);
+  strcpy(insmeshfilename, filename);
+  strcat(innodefilename, ".node");
+  strcat(inpolyfilename, ".poly");
+  strcat(insmeshfilename, ".smesh");
+
+  // First assume it is a .poly file.
+  smesh = 0;
+  // Try to open a .poly file.
+  polyfile = fopen(inpolyfilename, "r");
+  if (polyfile == (FILE *) NULL) {
+    // .poly doesn't exist! Try to open a .smesh file.
+    polyfile = fopen(insmeshfilename, "r");
+    if (polyfile == (FILE *) NULL) {
+      printf("File I/O Error:  Cannot access file %s and %s.\n",
+             inpolyfilename, insmeshfilename);
+      return false;
+    } else {
+      printf("Opening %s.\n", insmeshfilename);
+    }
+    smesh = 1;
+  } else {
+    printf("Opening %s.\n", inpolyfilename);
+  }
+  // Read number of points, number of dimensions, number of point
+  //   attributes, and number of boundary markers.
+  stringptr = readnumberline(inputline, polyfile, inpolyfilename);
+  numberofpoints = (int) strtol (stringptr, &stringptr, 0);
+  stringptr = findnextnumber(stringptr);
+  if (*stringptr == '\0') {
+    mesh_dim = 3; // If it is not provided, set the default value.
+  } else {
+    mesh_dim = (int) strtol (stringptr, &stringptr, 0);      
+  }
+  stringptr = findnextnumber(stringptr);
+  if (*stringptr == '\0') {
+    numberofpointattributes = 0; // The default value.
+  } else {
+    numberofpointattributes = (int) strtol (stringptr, &stringptr, 0);
+  }
+  stringptr = findnextnumber(stringptr);
+  if (*stringptr == '\0') {
+    markers = 0; // If it is not provided, set the default value.
+  } else {
+    markers = (int) strtol (stringptr, &stringptr, 0);
+  }
+  if (numberofpoints > 0) {
+    readnodefile = 0;
+    if (smesh) {
+      infilename = insmeshfilename;
+    } else {
+      infilename = inpolyfilename;
+    } 
+    infile = polyfile;
+  } else {
+    // If the .poly or .smesh file claims there are zero points, that
+    //   means the points should be read from a separate .node file.
+    readnodefile = 1;
+    infilename = innodefilename;
+  }
+
+  if (readnodefile) {
+    // Read the points from the .node file.
+    printf("Opening %s.\n", innodefilename);
+    infile = fopen(innodefilename, "r");
+    if (infile == (FILE *) NULL) {
+      printf("File I/O Error:  Cannot access file %s.\n", innodefilename);
+      return false;
+    }
+    // Read number of points, number of dimensions, number of point
+    //   attributes, and number of boundary markers.
+    stringptr = readnumberline(inputline, infile, innodefilename);
+    numberofpoints = (int) strtol (stringptr, &stringptr, 0);
+    stringptr = findnextnumber(stringptr);
+    if (*stringptr == '\0') {
+      mesh_dim = 3;
+    } else {
+      mesh_dim = (int) strtol (stringptr, &stringptr, 0);
+    }
+    stringptr = findnextnumber(stringptr);
+    if (*stringptr == '\0') {
+      numberofpointattributes = 0;
+    } else {
+      numberofpointattributes = (int) strtol (stringptr, &stringptr, 0);
+    }
+    stringptr = findnextnumber(stringptr);
+    if (*stringptr == '\0') {
+      markers = 0;
+    } else {
+      markers = (int) strtol (stringptr, &stringptr, 0);
+    }
+  }
+
+  if (mesh_dim != 3) {
+    printf("Error:  load_poly() only works for 3D points.\n");
+    fclose(infile);
+    return false;
+  }
+  if (numberofpoints < 4) {
+    printf("File I/O error:  There should have at least 4 points.\n");
+    fclose(infile);
+    return false;
+  }
+
+  // Load the list of nodes.
+  if (!load_node_call(infile, markers, infilename)) {
+    fclose(infile);
+    return false;
+  }
+
+  if (readnodefile) {
+    fclose(infile);
+  }
+
+  // Read number of facets and number of boundary markers.
+  stringptr = readnumberline(inputline, polyfile, inpolyfilename);
+  numberoffacets = (int) strtol (stringptr, &stringptr, 0);
+  stringptr = findnextnumber(stringptr);
+  if (*stringptr == '\0') {
+    markers = 0;
+  } else {
+    markers = (int) strtol (stringptr, &stringptr, 0);
+  }
+
+  if (numberoffacets <= 0) {
+    // This input file is trivial, return anyway.
+    fclose(polyfile);
+    return true;
+  }
+
+  // Initialize the 'facetlist', 'facetmarkerlist'.
+  facetlist = new facet[numberoffacets];
+  if (markers == 1) {
+    facetmarkerlist = new int[numberoffacets];
+  }
+
+  facet *f;
+  polygon *p;
+
+  // Read data into 'facetlist', 'facetmarkerlist'.
+  if (smesh == 0) {
+    // Facets are in .poly file format.
+    for (i = 1; i <= numberoffacets; i++) {
+      f = &(facetlist[i - 1]);
+      init(f);
+      f->numberofholes = 0;
+      currentmarker = 0;
+      // Read number of polygons, number of holes, and a boundary marker.
+      stringptr = readnumberline(inputline, polyfile, inpolyfilename);
+      f->numberofpolygons = (int) strtol (stringptr, &stringptr, 0);
+      stringptr = findnextnumber(stringptr);
+      if (*stringptr != '\0') {
+        f->numberofholes = (int) strtol (stringptr, &stringptr, 0);
+        if (markers == 1) {
+          stringptr = findnextnumber(stringptr);
+          if (*stringptr != '\0') {
+            currentmarker = (int) strtol(stringptr, &stringptr, 0);
+          } 
+        }
+      } 
+      // Initialize facetmarker if it needs.
+      if (markers == 1) {
+        facetmarkerlist[i - 1] = currentmarker; 
+      }
+      // Each facet should has at least one polygon.
+      if (f->numberofpolygons <= 0) {
+        printf("Error:  Wrong number of polygon in %d facet.\n", i);
+        break; 
+      }
+      // Initialize the 'f->polygonlist'.
+      f->polygonlist = new polygon[f->numberofpolygons];
+      // Go through all polygons, read in their vertices.
+      for (j = 1; j <= f->numberofpolygons; j++) {
+        p = &(f->polygonlist[j - 1]);
+        init(p);
+        // Read number of vertices of this polygon.
+        stringptr = readnumberline(inputline, polyfile, inpolyfilename);
+        p->numberofvertices = (int) strtol(stringptr, &stringptr, 0);
+        if (p->numberofvertices < 1) {
+          printf("Error:  Wrong polygon %d in facet %d\n", j, i);
+          break;
+        }
+        // Initialize 'p->vertexlist'.
+        p->vertexlist = new int[p->numberofvertices];
+        // Read all vertices of this polygon.
+        for (k = 1; k <= p->numberofvertices; k++) {
+          stringptr = findnextnumber(stringptr);
+          if (*stringptr == '\0') {
+            // Try to load another non-empty line and continue to read the
+            //   rest of vertices.
+            stringptr = readnumberline(inputline, polyfile, inpolyfilename);
+            if (*stringptr == '\0') {
+              printf("Error:  Missing %d endpoints of polygon %d in facet %d",
+                     p->numberofvertices - k, j, i);
+              break;
+            }
+          }
+          p->vertexlist[k - 1] = (int) strtol (stringptr, &stringptr, 0);
+        }
+      } 
+      if (j <= f->numberofpolygons) {
+        // This must be caused by an error. However, there're j - 1 polygons
+        //   have been read. Reset the 'f->numberofpolygon'.
+        if (j == 1) {
+          // This is the first polygon.
+          delete [] f->polygonlist;
+        }
+        f->numberofpolygons = j - 1;
+        // No hole will be read even it exists.
+        f->numberofholes = 0;
+        break;
+      }
+      // If this facet has holes pints defined, read them.
+      if (f->numberofholes > 0) {
+        // Initialize 'f->holelist'.
+        f->holelist = new REAL[f->numberofholes * 3];
+        // Read the holes' coordinates.
+        index = 0;
+        for (j = 1; j <= f->numberofholes; j++) {
+          stringptr = readnumberline(inputline, polyfile, inpolyfilename);
+          for (k = 1; k <= 3; k++) {
+            stringptr = findnextnumber(stringptr);
+            if (*stringptr == '\0') {
+              printf("Error:  Hole %d in facet %d has no coordinates", j, i);
+              break;
+            }
+            f->holelist[index++] = (REAL) strtod (stringptr, &stringptr);
+          }
+          if (k <= 3) {
+            // This must be caused by an error.
+            break;
+          }
+        }
+        if (j <= f->numberofholes) {
+          // This must be caused by an error.
+          break;
+        }
+      }
+    } 
+    if (i <= numberoffacets) {
+      // This must be caused by an error.
+      numberoffacets = i - 1;
+      fclose(polyfile);
+      return false;
+    }
+  } else { // poly == 0
+    // Read the facets from a .smesh file.
+    for (i = 1; i <= numberoffacets; i++) {
+      f = &(facetlist[i - 1]);
+      init(f);
+      // Initialize 'f->facetlist'. In a .smesh file, each facetlist only
+      //   contains exactly one polygon, no hole.
+      f->numberofpolygons = 1;
+      f->polygonlist = new polygon[f->numberofpolygons];
+      p = &(f->polygonlist[0]);
+      init(p);
+      // Read number of vertices of this polygon.
+      stringptr = readnumberline(inputline, polyfile, insmeshfilename);
+      p->numberofvertices = (int) strtol (stringptr, &stringptr, 0);
+      if (p->numberofvertices < 1) {
+        printf("Error:  Wrong number of vertex in facet %d\n", i);
+        break;
+      }
+      // Initialize 'p->vertexlist'.
+      p->vertexlist = new int[p->numberofvertices];
+      for (k = 1; k <= p->numberofvertices; k++) {
+        stringptr = findnextnumber(stringptr);
+        if (*stringptr == '\0') {
+          // Try to load another non-empty line and continue to read the
+          //   rest of vertices.
+          stringptr = readnumberline(inputline, polyfile, inpolyfilename);
+          if (*stringptr == '\0') {
+            printf("Error:  Missing %d endpoints in facet %d",
+                   p->numberofvertices - k, i);
+            break;
+          }
+        }
+        p->vertexlist[k - 1] = (int) strtol (stringptr, &stringptr, 0);
+      }
+      if (k <= p->numberofvertices) {
+        // This must be caused by an error.
+        break;
+      }
+      // Read facet's boundary marker at last.
+      if (markers == 1) {
+        stringptr = findnextnumber(stringptr);
+        if (*stringptr == '\0') {
+          currentmarker = 0;
+        } else {
+          currentmarker = (int) strtol(stringptr, &stringptr, 0);
+        }
+        facetmarkerlist[i - 1] = currentmarker;
+      }
+    } 
+    if (i <= numberoffacets) {
+      // This must be caused by an error.
+      numberoffacets = i - 1;
+      fclose(polyfile);
+      return false;
+    }
+  }
+
+  // Read the hole section.
+  stringptr = readnumberline(inputline, polyfile, inpolyfilename);
+  if (*stringptr != '\0') {
+    numberofholes = (int) strtol (stringptr, &stringptr, 0);
+  } else {
+    numberofholes = 0;
+  }
+  if (numberofholes > 0) {
+    // Initialize 'holelist'.
+    holelist = new REAL[numberofholes * 3];
+    for (i = 0; i < 3 * numberofholes; i += 3) {
+      stringptr = readnumberline(inputline, polyfile, inpolyfilename);
+      stringptr = findnextnumber(stringptr);
+      if (*stringptr == '\0') {
+        printf("Error:  Hole %d has no x coord.\n", firstnumber + (i / 3));
+        break;
+      } else {
+        holelist[i] = (REAL) strtod(stringptr, &stringptr);
+      }
+      stringptr = findnextnumber(stringptr);
+      if (*stringptr == '\0') {
+        printf("Error:  Hole %d has no y coord.\n", firstnumber + (i / 3));
+        break;
+      } else {
+        holelist[i + 1] = (REAL) strtod(stringptr, &stringptr);
+      }
+      stringptr = findnextnumber(stringptr);
+      if (*stringptr == '\0') {
+        printf("Error:  Hole %d has no z coord.\n", firstnumber + (i / 3));
+        break;
+      } else {
+        holelist[i + 2] = (REAL) strtod(stringptr, &stringptr);
+      }
+    }
+    if (i < 3 * numberofholes) {
+      // This must be caused by an error.
+      fclose(polyfile);
+      return false;
+    }
+  }
+
+  // Read the region section.  The 'region' section is optional, if we don't
+  //   reach the end of the file, try read it in.
+  do {
+    stringptr = fgets(inputline, INPUTLINESIZE, polyfile);
+    if (stringptr == (char *) NULL) {
+      break;
+    }
+    // Skip anything that doesn't look like a number, a comment,
+    //   or the end of a line.
+    while ((*stringptr != '\0') && (*stringptr != '#')
+           && (*stringptr != '.') && (*stringptr != '+') && (*stringptr != '-')
+           && ((*stringptr < '0') || (*stringptr > '9'))) {
+      stringptr++;
+    }
+  // If it's a comment or end of line, read another line and try again.
+  } while ((*stringptr == '#') || (*stringptr == '\0'));
+  
+  if (stringptr != (char *) NULL && *stringptr != '\0') {
+    numberofregions = (int) strtol (stringptr, &stringptr, 0);
+  } else {
+    numberofregions = 0;
+  }
+  if (numberofregions > 0) {
+    // Initialize 'regionlist'.
+    regionlist = new REAL[numberofregions * 5];
+    index = 0;
+    for (i = 0; i < numberofregions; i++) {
+      stringptr = readnumberline(inputline, polyfile, inpolyfilename);
+      stringptr = findnextnumber(stringptr);
+      if (*stringptr == '\0') {
+        printf("Error:  Region %d has no x coordinate.\n", firstnumber + i);
+        break;
+      } else {
+        regionlist[index++] = (REAL) strtod(stringptr, &stringptr);
+      }
+      stringptr = findnextnumber(stringptr);
+      if (*stringptr == '\0') {
+        printf("Error:  Region %d has no y coordinate.\n", firstnumber + i);
+        break;
+      } else {
+        regionlist[index++] = (REAL) strtod(stringptr, &stringptr);
+      }
+      stringptr = findnextnumber(stringptr);
+      if (*stringptr == '\0') {
+        printf("Error:  Region %d has no z coordinate.\n", firstnumber + i);
+        break;
+      } else {
+        regionlist[index++] = (REAL) strtod(stringptr, &stringptr);
+      }
+      stringptr = findnextnumber(stringptr);
+      if (*stringptr == '\0') {
+        printf("Error:  Region %d has no region attrib.\n", firstnumber + i);
+        break;
+      } else {
+        regionlist[index++] = (REAL) strtod(stringptr, &stringptr);
+      }
+      stringptr = findnextnumber(stringptr);
+      if (*stringptr == '\0') {
+        regionlist[index] = regionlist[index - 1];
+      } else {
+        regionlist[index] = (REAL) strtod(stringptr, &stringptr);
+      }
+      index++;
+    }
+    if (i < numberofregions) {
+      // This must be caused by an error.
+      fclose(polyfile);
+      return false;
+    }
+  }
+
+  // End of reading poly/smesh file.
+  fclose(polyfile);
+  return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// load_off()    Load a polyhedron described in a .off file.                 //
+//                                                                           //
+// The .off format is one of file formats of the Geomview, an interactive    //
+// program for viewing and manipulating geometric objects.  More information //
+// is available form: http://www.geomview.org.                               //
+//                                                                           //
+// 'filename' is a input filename with extension .off or without extension ( //
+// the .off will be added in this case). On completion, the polyhedron is    //
+// returned in 'pointlist' and 'facetlist'.                                  //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenio::load_off(char* filename)
+{
+  FILE *fp;
+  tetgenio::facet *f;
+  tetgenio::polygon *p;
+  char infilename[FILENAMESIZE];
+  char buffer[INPUTLINESIZE];
+  char *bufferp;
+  double *coord;
+  int nverts = 0, iverts = 0;
+  int nfaces = 0, ifaces = 0;
+  int nedges = 0;
+  int line_count = 0, i;
+
+  strncpy(infilename, filename, 1024 - 1);
+  infilename[FILENAMESIZE - 1] = '\0';
+  if (infilename[0] == '\0') {
+    printf("Error:  No filename.\n");
+    return false;
+  }
+  if (strcmp(&infilename[strlen(infilename) - 4], ".off") != 0) {
+    strcat(infilename, ".off");
+  }
+
+  if (!(fp = fopen(infilename, "r"))) {
+    printf("File I/O Error:  Unable to open file %s\n", infilename);
+    return false;
+  }
+  printf("Opening %s.\n", infilename);
+
+  // OFF requires the index starts from '0'.
+  firstnumber = 0;
+
+  while ((bufferp = readline(buffer, fp, &line_count)) != NULL) {
+    // Check section
+    if (nverts == 0) {
+      // Read header 
+      bufferp = strstr(bufferp, "OFF");
+      if (bufferp != NULL) {
+        // Read mesh counts
+        bufferp = findnextnumber(bufferp); // Skip field "OFF".
+        if (*bufferp == '\0') {
+          // Read a non-empty line.
+          bufferp = readline(buffer, fp, &line_count);
+        }
+        if ((sscanf(bufferp, "%d%d%d", &nverts, &nfaces, &nedges) != 3) 
+            || (nverts == 0)) {
+          printf("Syntax error reading header on line %d in file %s\n",
+                 line_count, infilename);
+          fclose(fp);
+          return false;
+        }
+        // Allocate memory for 'tetgenio'
+        if (nverts > 0) {
+          numberofpoints = nverts;
+          pointlist = new REAL[nverts * 3];
+          assert(pointlist != NULL);
+        }
+        if (nfaces > 0) {        
+          numberoffacets = nfaces;
+          facetlist = new tetgenio::facet[nfaces];
+          assert(facetlist);
+        }
+      }
+    } else if (iverts < nverts) {
+      // Read vertex coordinates
+      coord = &pointlist[iverts * 3];
+      for (i = 0; i < 3; i++) {
+        if (*bufferp == '\0') {
+          printf("Syntax error reading vertex coords on line %d in file %s\n",
+                 line_count, infilename);
+          fclose(fp);
+          return false;
+        }
+        coord[i] = (REAL) strtod(bufferp, &bufferp);
+        bufferp = findnextnumber(bufferp);
+      }
+      iverts++;
+    } else if (ifaces < nfaces) {
+      // Get next face
+      f = &facetlist[ifaces];
+      init(f);      
+      // In .off format, each facet has one polygon, no hole.
+      f->numberofpolygons = 1;
+      f->polygonlist = new tetgenio::polygon[1];
+      p = &f->polygonlist[0];
+      init(p);
+      // Read the number of vertices, it should be greater than 0.
+      p->numberofvertices = (int) strtol(bufferp, &bufferp, 0);
+      if (p->numberofvertices == 0) {
+        printf("Syntax error reading polygon on line %d in file %s\n",
+               line_count, infilename);
+        fclose(fp);
+        return false;
+      }
+      // Allocate memory for face vertices
+      p->vertexlist = new int[p->numberofvertices];
+      for (i = 0; i < p->numberofvertices; i++) {
+        bufferp = findnextnumber(bufferp);
+        if (*bufferp == '\0') {
+          printf("Syntax error reading polygon on line %d in file %s\n",
+                 line_count, infilename);
+          fclose(fp);
+          return false;
+        }
+        p->vertexlist[i] = (int) strtol(bufferp, &bufferp, 0);
+      }
+      ifaces++;
+    } else {
+      // Should never get here
+      printf("Found extra text starting at line %d in file %s\n", line_count,
+             infilename);
+      break;
+    }
+  }
+
+  // Close file
+  fclose(fp);
+
+  // Check whether read all points
+  if (iverts != nverts) {
+    printf("Expected %d vertices, but read only %d vertices in file %s\n",
+           nverts, iverts, infilename);
+    return false;
+  }
+
+  // Check whether read all faces
+  if (ifaces != nfaces) {
+    printf("Expected %d faces, but read only %d faces in file %s\n",
+           nfaces, ifaces, infilename);
+    return false;
+  }
+
+  return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// load_ply()    Load a polyhedron described in a .ply file.                 //
+//                                                                           //
+// 'filename' is the file name with extension .ply or without extension (the //
+// .ply will be added in this case).                                         //
+//                                                                           //
+// This is a simplified version of reading .ply files, which only reads the  //
+// set of vertices and the set of faces. Other informations (such as color,  //
+// material, texture, etc) in .ply file are ignored. Complete routines for   //
+// reading and writing ,ply files are available from: http://www.cc.gatech.  //
+// edu/projects/large_models/ply.html.  Except the header section, ply file  //
+// format has exactly the same format for listing vertices and polygons as   //
+// off file format.                                                          //
+//                                                                           //
+// On completion, 'pointlist' and 'facetlist' together return the polyhedron.//
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenio::load_ply(char* filename)
+{
+  FILE *fp;
+  tetgenio::facet *f;
+  tetgenio::polygon *p;
+  char infilename[FILENAMESIZE];
+  char buffer[INPUTLINESIZE];
+  char *bufferp, *str;
+  double *coord;
+  int endheader = 0, format = 0;
+  int nverts = 0, iverts = 0;
+  int nfaces = 0, ifaces = 0;
+  int line_count = 0, i;
+
+  strncpy(infilename, filename, FILENAMESIZE - 1);
+  infilename[FILENAMESIZE - 1] = '\0';
+  if (infilename[0] == '\0') {
+    printf("Error:  No filename.\n");
+    return false;
+  }
+  if (strcmp(&infilename[strlen(infilename) - 4], ".ply") != 0) {
+    strcat(infilename, ".ply");
+  }
+
+  if (!(fp = fopen(infilename, "r"))) {
+    printf("Error:  Unable to open file %s\n", infilename);
+    return false;
+  }
+  printf("Opening %s.\n", infilename);
+
+  // PLY requires the index starts from '0'.
+  firstnumber = 0;
+
+  while ((bufferp = readline(buffer, fp, &line_count)) != NULL) {
+    if (!endheader) {
+      // Find if it is the keyword "end_header".
+      str = strstr(bufferp, "end_header");
+      // strstr() is case sensitive.
+      if (!str) str = strstr(bufferp, "End_header");
+      if (!str) str = strstr(bufferp, "End_Header");
+      if (str) {
+        // This is the end of the header section.
+        endheader = 1; 
+        continue;
+      }
+      // Parse the number of vertices and the number of faces.
+      if (nverts == 0 || nfaces == 0) {
+        // Find if it si the keyword "element".
+        str = strstr(bufferp, "element");
+        if (!str) str = strstr(bufferp, "Element");
+        if (str) {
+          bufferp = findnextfield(str);
+          if (*bufferp == '\0') {
+            printf("Syntax error reading element type on line%d in file %s\n",
+                   line_count, infilename);
+            fclose(fp);
+            return false;
+          }
+          if (nverts == 0) {
+            // Find if it is the keyword "vertex".
+            str = strstr(bufferp, "vertex");
+            if (!str) str = strstr(bufferp, "Vertex");
+            if (str) {
+              bufferp = findnextnumber(str);
+              if (*bufferp == '\0') {
+                printf("Syntax error reading vertex number on line");
+                printf(" %d in file %s\n", line_count, infilename);
+                fclose(fp);
+                return false;
+              }
+              nverts = (int) strtol(bufferp, &bufferp, 0);
+              // Allocate memory for 'tetgenio'
+              if (nverts > 0) {
+                numberofpoints = nverts;
+                pointlist = new REAL[nverts * 3];
+                assert(pointlist != NULL);
+              }
+            }
+          }
+          if (nfaces == 0) {
+            // Find if it is the keyword "face".
+            str = strstr(bufferp, "face");
+            if (!str) str = strstr(bufferp, "Face");
+            if (str) {
+              bufferp = findnextnumber(str);
+              if (*bufferp == '\0') {
+                printf("Syntax error reading face number on line");
+                printf(" %d in file %s\n", line_count, infilename);
+                fclose(fp);
+                return false;
+              }
+              nfaces = (int) strtol(bufferp, &bufferp, 0);
+              // Allocate memory for 'tetgenio'
+              if (nfaces > 0) {        
+                numberoffacets = nfaces;
+                facetlist = new tetgenio::facet[nfaces];
+                assert(facetlist);
+              }
+            }
+          }
+        } // It is not the string "element". 
+      }
+      if (format == 0) {
+        // Find the keyword "format".
+        str = strstr(bufferp, "format");
+        if (!str) str = strstr(bufferp, "Format");
+        if (str) {
+          format = 1;
+          bufferp = findnextfield(str);
+          // Find if it is the string "ascii".
+          str = strstr(bufferp, "ascii");
+          if (!str) str = strstr(bufferp, "ASCII");
+          if (!str) {
+            printf("This routine only reads ascii format of ply files.\n");
+            printf("Hint: You can convert the binary to ascii format by\n");
+            printf("  using the provided ply tools:\n");
+            printf("  ply2ascii < %s > ascii_%s\n", infilename, infilename);
+            fclose(fp);
+            return false;
+          }
+        }
+      }
+    } else if (iverts < nverts) {
+      // Read vertex coordinates
+      coord = &pointlist[iverts * 3];
+      for (i = 0; i < 3; i++) {
+        if (*bufferp == '\0') {
+          printf("Syntax error reading vertex coords on line %d in file %s\n",
+                 line_count, infilename);
+          fclose(fp);
+          return false;
+        }
+        coord[i] = (REAL) strtod(bufferp, &bufferp);
+        bufferp = findnextnumber(bufferp);
+      }
+      iverts++;
+    } else if (ifaces < nfaces) {
+      // Get next face
+      f = &facetlist[ifaces];
+      init(f);      
+      // In .off format, each facet has one polygon, no hole.
+      f->numberofpolygons = 1;
+      f->polygonlist = new tetgenio::polygon[1];
+      p = &f->polygonlist[0];
+      init(p);
+      // Read the number of vertices, it should be greater than 0.
+      p->numberofvertices = (int) strtol(bufferp, &bufferp, 0);
+      if (p->numberofvertices == 0) {
+        printf("Syntax error reading polygon on line %d in file %s\n",
+               line_count, infilename);
+        fclose(fp);
+        return false;
+      }
+      // Allocate memory for face vertices
+      p->vertexlist = new int[p->numberofvertices];
+      for (i = 0; i < p->numberofvertices; i++) {
+        bufferp = findnextnumber(bufferp);
+        if (*bufferp == '\0') {
+          printf("Syntax error reading polygon on line %d in file %s\n",
+                 line_count, infilename);
+          fclose(fp);
+          return false;
+        }
+        p->vertexlist[i] = (int) strtol(bufferp, &bufferp, 0);
+      }
+      ifaces++;
+    } else {
+      // Should never get here
+      printf("Found extra text starting at line %d in file %s\n", line_count,
+             infilename);
+      break;
+    }
+  }
+
+  // Close file
+  fclose(fp);
+
+  // Check whether read all points
+  if (iverts != nverts) {
+    printf("Expected %d vertices, but read only %d vertices in file %s\n",
+           nverts, iverts, infilename);
+    return false;
+  }
+
+  // Check whether read all faces
+  if (ifaces != nfaces) {
+    printf("Expected %d faces, but read only %d faces in file %s\n",
+           nfaces, ifaces, infilename);
+    return false;
+  }
+
+  return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// load_stl()    Load a surface mesh described in a .stl file.               //
+//                                                                           //
+// 'filename' is the file name with extension .stl or without extension (the //
+// .stl will be added in this case).                                         //
+//                                                                           //
+// The .stl or stereolithography format is an ASCII or binary file used in   //
+// manufacturing.  It is a list of the triangular surfaces that describe a   //
+// computer generated solid model. This is the standard input for most rapid //
+// prototyping machines.                                                     //
+//                                                                           //
+// On completion, 'pointlist' and 'facetlist' together return the polyhedron.//
+// Note: After load_stl(), there exist many duplicated points in 'pointlist'.//
+// They will be unified during the Delaunay tetrahedralization process.      //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenio::load_stl(char* filename)
+{
+  FILE *fp;
+  tetgenmesh::list *plist;
+  tetgenio::facet *f;
+  tetgenio::polygon *p;
+  char infilename[FILENAMESIZE];
+  char buffer[INPUTLINESIZE];
+  char *bufferp, *str;
+  double *coord;
+  int solid = 0;
+  int nverts = 0, iverts = 0;
+  int nfaces = 0;
+  int line_count = 0, i;
+
+  strncpy(infilename, filename, FILENAMESIZE - 1);
+  infilename[FILENAMESIZE - 1] = '\0';
+  if (infilename[0] == '\0') {
+    printf("Error:  No filename.\n");
+    return false;
+  }
+  if (strcmp(&infilename[strlen(infilename) - 4], ".stl") != 0) {
+    strcat(infilename, ".stl");
+  }
+
+  if (!(fp = fopen(infilename, "r"))) {
+    printf("Error:  Unable to open file %s\n", infilename);
+    return false;
+  }
+  printf("Opening %s.\n", infilename);
+
+  // STL file has no number of points available. Use a list to read points.
+  plist = new tetgenmesh::list(sizeof(double) * 3, NULL, 1024); 
+
+  while ((bufferp = readline(buffer, fp, &line_count)) != NULL) {
+    // The ASCII .stl file must start with the lower case keyword solid and
+    //   end with endsolid.
+    if (solid == 0) {
+      // Read header 
+      bufferp = strstr(bufferp, "solid");
+      if (bufferp != NULL) {
+        solid = 1;
+      }
+    } else {
+      // We're inside the block of the solid.
+      str = bufferp;
+      // Is this the end of the solid.
+      bufferp = strstr(bufferp, "endsolid");
+      if (bufferp != NULL) {
+        solid = 0;
+      } else {
+        // Read the XYZ coordinates if it is a vertex.
+        bufferp = str;
+        bufferp = strstr(bufferp, "vertex");
+        if (bufferp != NULL) {
+          coord = (double *) plist->append(NULL);
+          for (i = 0; i < 3; i++) {
+            bufferp = findnextnumber(bufferp);
+            if (*bufferp == '\0') {
+              printf("Syntax error reading vertex coords on line %d\n",
+                   line_count);
+              delete plist;
+              fclose(fp);
+              return false;
+            }
+            coord[i] = (REAL) strtod(bufferp, &bufferp);
+          }
+        }
+      }
+    }
+  }
+  fclose(fp);
+
+  nverts = plist->len();
+  // nverts should be an integer times 3 (every 3 vertices denote a face).
+  if (nverts == 0 || (nverts % 3 != 0)) {
+    printf("Error:  Wrong number of vertices in file %s.\n", infilename);
+    delete plist;
+    return false;
+  }
+  numberofpoints = nverts;
+  pointlist = new REAL[nverts * 3];
+  assert(pointlist != NULL);
+  for (i = 0; i < nverts; i++) {
+    coord = (double *) (* plist)[i];
+    iverts = i * 3;
+    pointlist[iverts] = (REAL) coord[0];
+    pointlist[iverts + 1] = (REAL) coord[1];
+    pointlist[iverts + 2] = (REAL) coord[2];
+  }
+
+  nfaces = (int) (nverts / 3);
+  numberoffacets = nfaces;
+  facetlist = new tetgenio::facet[nfaces];
+  assert(facetlist != NULL);
+
+  // Default use '1' as the array starting index.
+  firstnumber = 1;
+  iverts = firstnumber;
+  for (i = 0; i < nfaces; i++) {
+    f = &facetlist[i];
+    init(f);      
+    // In .stl format, each facet has one polygon, no hole.
+    f->numberofpolygons = 1;
+    f->polygonlist = new tetgenio::polygon[1];
+    p = &f->polygonlist[0];
+    init(p);
+    // Each polygon has three vertices.
+    p->numberofvertices = 3;
+    p->vertexlist = new int[p->numberofvertices];
+    p->vertexlist[0] = iverts;
+    p->vertexlist[1] = iverts + 1;
+    p->vertexlist[2] = iverts + 2;
+    iverts += 3;
+  }
+
+  delete plist;
+  return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// load_medit()    Load a surface mesh described in .mesh file.              //
+//                                                                           //
+// 'filename' is the file name with extension .mesh or without entension (   //
+// the .mesh will be added in this case). .mesh is the file format of Medit, //
+// a user-friendly interactive mesh viewing program.                         //
+//                                                                           //
+// This routine ONLY reads the sections containing vertices and triangles,   //
+// other sections (such as tetrahedra, edges, ...) are ignored.              //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenio::load_medit(char* filename)
+{
+  FILE *fp;
+  tetgenio::facet *f;
+  tetgenio::polygon *p;
+  char infilename[FILENAMESIZE];
+  char buffer[INPUTLINESIZE];
+  char *bufferp, *str;
+  double *coord;
+  int nverts = 0;
+  int nfaces = 0;
+  int line_count = 0;
+  int corners = 0; // 3 (triangle) or 4 (quad).
+  int i, j;
+
+  strncpy(infilename, filename, FILENAMESIZE - 1);
+  infilename[FILENAMESIZE - 1] = '\0';
+  if (infilename[0] == '\0') {
+    printf("Error:  No filename.\n");
+    return false;
+  }
+  if (strcmp(&infilename[strlen(infilename) - 5], ".mesh") != 0) {
+    strcat(infilename, ".mesh");
+  }
+  
+  if (!(fp = fopen(infilename, "r"))) {
+    printf("Error:  Unable to open file %s\n", infilename);
+    return false;
+  }
+  printf("Opening %s.\n", infilename);
+
+  // Default uses the index starts from '1'.
+  firstnumber = 1;
+
+  while ((bufferp = readline(buffer, fp, &line_count)) != NULL) {
+    if (*bufferp == '#') continue;  // A comment line is skipped.
+    if (nverts == 0) {
+      // Find if it is the keyword "Vertices".
+      str = strstr(bufferp, "Vertices");
+      if (!str) str = strstr(bufferp, "vertices");
+      if (!str) str = strstr(bufferp, "VERTICES");
+      if (str) {
+        // Read the number of vertices.
+        bufferp = findnextnumber(str); // Skip field "Vertices".
+        if (*bufferp == '\0') {
+          // Read a non-empty line.
+          bufferp = readline(buffer, fp, &line_count);
+        }
+        nverts = (int) strtol(bufferp, &bufferp, 0);
+        // Allocate memory for 'tetgenio'
+        if (nverts > 0) {
+          numberofpoints = nverts;
+          pointlist = new REAL[nverts * 3];
+          assert(pointlist != NULL);
+        }
+        // Read the follwoing node list.
+        for (i = 0; i < nverts; i++) {
+          bufferp = readline(buffer, fp, &line_count);
+          if (bufferp == NULL) {
+            printf("Unexpected end of file on line %d in file %s\n",
+                   line_count, infilename);
+            fclose(fp);
+            return false;
+          }
+          // Read vertex coordinates
+          coord = &pointlist[i * 3];
+          for (j = 0; j < 3; j++) {
+            if (*bufferp == '\0') {
+              printf("Syntax error reading vertex coords on line");
+              printf(" %d in file %s\n", line_count, infilename);
+              fclose(fp);
+              return false;
+            }
+            coord[j] = (REAL) strtod(bufferp, &bufferp);
+            bufferp = findnextnumber(bufferp);
+          }
+        }
+        continue;
+      }
+    } 
+    if (nfaces == 0) {
+      // Find if it is the keyword "Triangles" or "Quadrilaterals".
+      str = strstr(bufferp, "Triangles");
+      if (!str) str = strstr(bufferp, "triangles");
+      if (!str) str = strstr(bufferp, "TRIANGLES");
+      if (str) {
+        corners = 3;
+      } else {
+        str = strstr(bufferp, "Quadrilaterals");
+        if (!str) str = strstr(bufferp, "quadrilaterals");
+        if (!str) str = strstr(bufferp, "QUADRILATERALS");
+        if (str) {
+          corners = 4;
+        }
+      }
+      if (corners == 3 || corners == 4) {
+        // Read the number of triangles (or quadrilaterals).
+        bufferp = findnextnumber(str); // Skip field "Triangles".
+        if (*bufferp == '\0') {
+          // Read a non-empty line.
+          bufferp = readline(buffer, fp, &line_count);
+        }
+        nfaces = strtol(bufferp, &bufferp, 0);
+        // Allocate memory for 'tetgenio'
+        if (nfaces > 0) {        
+          numberoffacets = nfaces;
+          facetlist = new tetgenio::facet[nfaces];
+          assert(facetlist != NULL);
+          facetmarkerlist = new int[nfaces];
+          assert(facetmarkerlist != NULL);
+        }
+        // Read the following list of faces.
+        for (i = 0; i < nfaces; i++) {
+          bufferp = readline(buffer, fp, &line_count);
+          if (bufferp == NULL) {
+            printf("Unexpected end of file on line %d in file %s\n",
+                   line_count, infilename);
+            fclose(fp);
+            return false;
+          }
+          f = &facetlist[i];
+          tetgenio::init(f);
+          // In .mesh format, each facet has one polygon, no hole.
+          f->numberofpolygons = 1;
+          f->polygonlist = new tetgenio::polygon[1];
+          p = &f->polygonlist[0];
+          tetgenio::init(p);
+          p->numberofvertices = corners;
+          // Allocate memory for face vertices
+          p->vertexlist = new int[p->numberofvertices];
+          assert(p->vertexlist != NULL);
+          // Read the vertices of the face.
+          for (j = 0; j < corners; j++) {
+            if (*bufferp == '\0') {
+              printf("Syntax error reading face on line %d in file %s\n",
+                     line_count, infilename);
+              fclose(fp);
+              return false;
+            }
+            p->vertexlist[j] = (int) strtol(bufferp, &bufferp, 0);
+            if (firstnumber == 1) {
+              // Check if a '0' index appears.
+              if (p->vertexlist[j] == 0) {
+                // The first index is set to be 0.
+                firstnumber = 0;
+              }
+            }
+            bufferp = findnextnumber(bufferp);
+          }
+          // Read the marker of the face if it exists.
+          facetmarkerlist[i] = 0;
+          if (*bufferp != '\0') {
+            facetmarkerlist[i] = (int) strtol(bufferp, &bufferp, 0);
+          }
+        }
+        continue;
+      }
+    }
+    if (nverts > 0 && nfaces > 0) break; // Ignore other data.
+  }
+
+  // Close file
+  fclose(fp);
+
+  return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// load_plc()    Load a piecewise linear complex from file.                  //
+//                                                                           //
+// This is main entrance for loading plcs from different file formats into   //
+// tetgenio.  'filename' is the input file name without extention. 'object'  //
+// indicates which file format is used to describ the plc.                   //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenio::load_plc(char* filename, int object)
+{
+  enum tetgenbehavior::objecttype type;
+
+  type = (enum tetgenbehavior::objecttype) object;
+  switch (type) {
+  case tetgenbehavior::NODES:
+    return load_node(filename);
+  case tetgenbehavior::POLY:
+    return load_poly(filename);
+  case tetgenbehavior::OFF:
+    return load_off(filename);
+  case tetgenbehavior::PLY:
+    return load_ply(filename);
+  case tetgenbehavior::STL:
+    return load_stl(filename);
+  case tetgenbehavior::MEDIT:
+    return load_medit(filename);
+  default:
+    return load_poly(filename);
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// load_tetmesh()    Load a tetrahedral mesh from files.                     //
+//                                                                           //
+// 'filename' is the inputfile without suffix.  The nodes of the tetrahedral //
+// mesh is in "filename.node",  the elements is in "filename.ele", if the    //
+// "filename.face" and "filename.vol" exists, they will also be read.        //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenio::load_tetmesh(char* filename)
+{
+  FILE *infile;
+  char innodefilename[FILENAMESIZE];
+  char inelefilename[FILENAMESIZE];
+  char infacefilename[FILENAMESIZE];
+  char inedgefilename[FILENAMESIZE];
+  char involfilename[FILENAMESIZE];
+  char inputline[INPUTLINESIZE];
+  char *stringptr, *infilename;
+  REAL attrib, volume;
+  int volelements;
+  int markers, corner;
+  int index, attribindex;
+  int i, j;
+
+  // Assembling the actual file names we want to open.
+  strcpy(innodefilename, filename);
+  strcpy(inelefilename, filename);
+  strcpy(infacefilename, filename);
+  strcpy(inedgefilename, filename);
+  strcpy(involfilename, filename);
+  strcat(innodefilename, ".node");
+  strcat(inelefilename, ".ele");
+  strcat(infacefilename, ".face");
+  strcat(inedgefilename, ".edge");
+  strcat(involfilename, ".vol");
+
+  // Read the points from a .node file.
+  infilename = innodefilename;
+  printf("Opening %s.\n", infilename);
+  infile = fopen(infilename, "r");
+  if (infile == (FILE *) NULL) {
+    printf("File I/O Error:  Cannot access file %s.\n", infilename);
+    return false;
+  }
+  // Read number of points, number of dimensions, number of point
+  //   attributes, and number of boundary markers.
+  stringptr = readnumberline(inputline, infile, infilename);
+  numberofpoints = (int) strtol (stringptr, &stringptr, 0);
+  stringptr = findnextnumber(stringptr);
+  if (*stringptr == '\0') {
+    mesh_dim = 3;
+  } else {
+    mesh_dim = (int) strtol (stringptr, &stringptr, 0);
+  }
+  stringptr = findnextnumber(stringptr);
+  if (*stringptr == '\0') {
+    numberofpointattributes = 0;
+  } else {
+    numberofpointattributes = (int) strtol (stringptr, &stringptr, 0);
+  }
+  stringptr = findnextnumber(stringptr);
+  if (*stringptr == '\0') {
+    markers = 0;  // Default value.
+  } else {
+    markers = (int) strtol (stringptr, &stringptr, 0);
+  }
+
+  if (mesh_dim != 3) {
+    printf("Error:  load_tetmesh() only works for 3D points.\n");
+    fclose(infile);
+    return false;
+  }
+  if (numberofpoints < 4) {
+    printf("File I/O error:  Input should has at least 4 points.\n");
+    fclose(infile);
+    return false;
+  }
+
+  // Load the list of nodes.
+  if (!load_node_call(infile, markers, infilename)) {
+    fclose(infile);
+    return false;
+  }
+
+  // Read the elements from a .ele file.
+  infilename = inelefilename;
+  printf("Opening %s.\n", infilename);
+  infile = fopen(infilename, "r");
+  if (infile != (FILE *) NULL) {
+    // Read number of elements, number of corners (4 or 10), number of
+    //   element attributes.
+    stringptr = readnumberline(inputline, infile, infilename);
+    numberoftetrahedra = (int) strtol (stringptr, &stringptr, 0);
+    stringptr = findnextnumber(stringptr);
+    if (*stringptr == '\0') {
+      numberofcorners = 4;  // Default read 4 nodes per element.
+    } else {
+      numberofcorners = (int) strtol (stringptr, &stringptr, 0);
+    }
+    stringptr = findnextnumber(stringptr);
+    if (*stringptr == '\0') {
+      numberoftetrahedronattributes = 0; // Default no attribute.
+    } else {
+      numberoftetrahedronattributes = (int) strtol (stringptr, &stringptr, 0);
+    }
+    if (numberofcorners != 4 && numberofcorners != 10) {
+      printf("Error:  Wrong number of corners %d (should be 4 or 10).\n", 
+             numberofcorners);
+      fclose(infile);
+      return false;
+    }
+    // Allocate memory for tetrahedra.
+    if (numberoftetrahedra > 0) {
+      tetrahedronlist = new int[numberoftetrahedra * numberofcorners]; 
+      if (tetrahedronlist == (int *) NULL) {
+        printf("Error:  Out of memory.\n");
+        exit(1);
+      }
+      // Allocate memory for output tetrahedron attributes if necessary.
+      if (numberoftetrahedronattributes > 0) {
+        tetrahedronattributelist = new REAL[numberoftetrahedra *
+                                        numberoftetrahedronattributes];
+        if (tetrahedronattributelist == (REAL *) NULL) {
+          printf("Error:  Out of memory.\n");
+          exit(1);
+        }
+      }
+    }
+    // Read the list of tetrahedra.
+    index = 0;
+    attribindex = 0;
+    for (i = 0; i < numberoftetrahedra; i++) {
+      // Read tetrahedron index and the tetrahedron's corners.
+      stringptr = readnumberline(inputline, infile, infilename);
+      for (j = 0; j < numberofcorners; j++) {
+        stringptr = findnextnumber(stringptr);
+        if (*stringptr == '\0') {
+          printf("Error:  Tetrahedron %d is missing vertex %d in %s.\n",
+                 i + firstnumber, j + 1, infilename);
+          exit(1);
+        }
+        corner = (int) strtol(stringptr, &stringptr, 0);
+        if (corner < firstnumber || corner >= numberofpoints + firstnumber) {
+          printf("Error:  Tetrahedron %d has an invalid vertex index.\n",
+                 i + firstnumber);
+          exit(1);
+        }
+        tetrahedronlist[index++] = corner;
+      }
+      // Read the tetrahedron's attributes.
+      for (j = 0; j < numberoftetrahedronattributes; j++) {
+        stringptr = findnextnumber(stringptr);
+        if (*stringptr == '\0') {
+          attrib = 0.0;
+        } else {
+          attrib = (REAL) strtod(stringptr, &stringptr);
+        }
+        tetrahedronattributelist[attribindex++] = attrib;
+      }
+    }
+    fclose(infile);
+  }
+  
+  // Read the hullfaces or subfaces from a .face file if it exists.
+  infilename = infacefilename;
+  infile = fopen(infilename, "r");
+  if (infile != (FILE *) NULL) {
+    printf("Opening %s.\n", infilename);
+    // Read number of faces, boundary markers.
+    stringptr = readnumberline(inputline, infile, infilename);
+    numberoftrifaces = (int) strtol (stringptr, &stringptr, 0);
+    stringptr = findnextnumber(stringptr);
+    if (*stringptr == '\0') {
+      markers = 0;  // Default there is no marker per face.
+    } else {
+      markers = (int) strtol (stringptr, &stringptr, 0);
+    }
+    if (numberoftrifaces > 0) {
+      trifacelist = new int[numberoftrifaces * 3];
+      if (trifacelist == (int *) NULL) {
+        printf("Error:  Out of memory.\n");
+        exit(1);
+      }
+      if (markers) {
+        trifacemarkerlist = new int[numberoftrifaces * 3];
+        if (trifacemarkerlist == (int *) NULL) {
+          printf("Error:  Out of memory.\n");
+          exit(1);
+        }
+      }
+    }
+    // Read the list of faces.
+    index = 0;
+    for (i = 0; i < numberoftrifaces; i++) {
+      // Read face index and the face's three corners.
+      stringptr = readnumberline(inputline, infile, infilename);
+      for (j = 0; j < 3; j++) {
+        stringptr = findnextnumber(stringptr);
+        if (*stringptr == '\0') {
+          printf("Error:  Face %d is missing vertex %d in %s.\n",
+                 i + firstnumber, j + 1, infilename);
+          exit(1);
+        }
+        corner = (int) strtol(stringptr, &stringptr, 0);
+        if (corner < firstnumber || corner >= numberofpoints + firstnumber) {
+          printf("Error:  Face %d has an invalid vertex index.\n",
+                 i + firstnumber);
+          exit(1);
+        }
+        trifacelist[index++] = corner;
+      }
+      // Read the boundary marker if it exists.
+      if (markers) {
+        stringptr = findnextnumber(stringptr);
+        if (*stringptr == '\0') {
+          attrib = 0.0;
+        } else {
+          attrib = (REAL) strtod(stringptr, &stringptr);
+        }
+        trifacemarkerlist[i] = (int) attrib;
+      }
+    }
+    fclose(infile);
+  }
+
+  // Read the boundary edges from a .edge file if it exists.
+  infilename = inedgefilename;
+  infile = fopen(infilename, "r");
+  if (infile != (FILE *) NULL) {
+    printf("Opening %s.\n", infilename);
+    // Read number of boundary edges.
+    stringptr = readnumberline(inputline, infile, infilename);
+    numberofedges = (int) strtol (stringptr, &stringptr, 0);
+    if (numberofedges > 0) {
+      edgelist = new int[numberofedges * 2];
+      if (edgelist == (int *) NULL) {
+        printf("Error:  Out of memory.\n");
+        exit(1);
+      }
+    }
+    // Read the list of faces.
+    index = 0;
+    for (i = 0; i < numberofedges; i++) {
+      // Read face index and the edge's two endpoints.
+      stringptr = readnumberline(inputline, infile, infilename);
+      for (j = 0; j < 2; j++) {
+        stringptr = findnextnumber(stringptr);
+        if (*stringptr == '\0') {
+          printf("Error:  Edge %d is missing vertex %d in %s.\n",
+                 i + firstnumber, j + 1, infilename);
+          exit(1);
+        }
+        corner = (int) strtol(stringptr, &stringptr, 0);
+        if (corner < firstnumber || corner >= numberofpoints + firstnumber) {
+          printf("Error:  Edge %d has an invalid vertex index.\n",
+                 i + firstnumber);
+          exit(1);
+        }
+        edgelist[index++] = corner;
+      }
+    }
+    fclose(infile);
+  }
+
+  // Read the volume constraints from a .vol file if it exists.
+  infilename = involfilename;
+  infile = fopen(infilename, "r");
+  if (infile != (FILE *) NULL) {
+    printf("Opening %s.\n", infilename);
+    // Read number of tetrahedra.
+    stringptr = readnumberline(inputline, infile, infilename);
+    volelements = (int) strtol (stringptr, &stringptr, 0);
+    if (volelements != numberoftetrahedra) {
+      printf("Warning:  %s and %s disagree on number of tetrahedra.\n",
+             inelefilename, involfilename);
+      volelements = 0;
+    }
+    if (volelements > 0) {
+      tetrahedronvolumelist = new REAL[volelements];
+      if (tetrahedronvolumelist == (REAL *) NULL) {
+        printf("Error:  Out of memory.\n");
+        exit(1);
+      }
+    }
+    // Read the list of volume constraints.
+    for (i = 0; i < volelements; i++) {
+      stringptr = readnumberline(inputline, infile, infilename);
+      stringptr = findnextnumber(stringptr);
+      if (*stringptr == '\0') {
+        volume = -1.0; // No constraint on this tetrahedron.
+      } else {
+        volume = (REAL) strtod(stringptr, &stringptr);
+      }
+      tetrahedronvolumelist[i] = volume;
+    }
+    fclose(infile);
+  }
+
+  return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// save_nodes()    Save points to a .node file.                              //
+//                                                                           //
+// 'filename' is a string containing the file name without suffix.           //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenio::save_nodes(char* filename)
+{
+  FILE *fout;
+  char outnodefilename[FILENAMESIZE];
+  int i, j;
+
+  sprintf(outnodefilename, "%s.node", filename);
+  printf("Saving nodes to %s\n", outnodefilename);
+  fout = fopen(outnodefilename, "w");
+  fprintf(fout, "%d  %d  %d  %d\n", numberofpoints, mesh_dim,
+          numberofpointattributes, pointmarkerlist != NULL ? 1 : 0);
+  for (i = 0; i < numberofpoints; i++) {
+    if (mesh_dim == 2) {
+      fprintf(fout, "%d  %.16g  %.16g", i + firstnumber, pointlist[i * 2],
+              pointlist[i * 2 + 1]);
+    } else {
+      fprintf(fout, "%d  %.16g  %.16g  %.16g", i + firstnumber,
+              pointlist[i * 3], pointlist[i * 3 + 1], pointlist[i * 3 + 2]);
+    }
+    for (j = 0; j < numberofpointattributes; j++) {
+      fprintf(fout, "  %.16g", 
+              pointattributelist[i * numberofpointattributes+j]);
+    }
+    if (pointmarkerlist != NULL) {
+      fprintf(fout, "  %d", pointmarkerlist[i]);
+    }
+    fprintf(fout, "\n");
+  }
+
+  fclose(fout);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// save_elements()    Save elements to a .ele file.                          //
+//                                                                           //
+// 'filename' is a string containing the file name without suffix.           //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenio::save_elements(char* filename)
+{
+  FILE *fout;
+  char outelefilename[FILENAMESIZE];
+  int i, j;
+
+  sprintf(outelefilename, "%s.ele", filename);
+  printf("Saving elements to %s\n", outelefilename);
+  fout = fopen(outelefilename, "w");
+  fprintf(fout, "%d  %d  %d\n", numberoftetrahedra, numberofcorners,
+          numberoftetrahedronattributes);
+  for (i = 0; i < numberoftetrahedra; i++) {
+    fprintf(fout, "%d", i + firstnumber);
+    for (j = 0; j < numberofcorners; j++) {
+      fprintf(fout, "  %5d", tetrahedronlist[i * numberofcorners + j]);
+    }
+    for (j = 0; j < numberoftetrahedronattributes; j++) {
+      fprintf(fout, "  %g",
+        tetrahedronattributelist[i * numberoftetrahedronattributes + j]);
+    }
+    fprintf(fout, "\n");
+  }
+
+  fclose(fout);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// save_faces()    Save faces to a .face file.                               //
+//                                                                           //
+// 'filename' is a string containing the file name without suffix.           //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenio::save_faces(char* filename)
+{
+  FILE *fout;
+  char outfacefilename[FILENAMESIZE];
+  int i;
+
+  sprintf(outfacefilename, "%s.face", filename);
+  printf("Saving faces to %s\n", outfacefilename);
+  fout = fopen(outfacefilename, "w");
+  fprintf(fout, "%d  %d\n", numberoftrifaces, 
+          trifacemarkerlist != NULL ? 1 : 0);
+  for (i = 0; i < numberoftrifaces; i++) {
+    fprintf(fout, "%d  %5d  %5d  %5d", i + firstnumber, trifacelist[i * 3],
+            trifacelist[i * 3 + 1], trifacelist[i * 3 +2]);
+    if (trifacemarkerlist != NULL) {
+      fprintf(fout, "  %d", trifacemarkerlist[i]);
+    }
+    fprintf(fout, "\n");
+  }
+
+  fclose(fout);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// save_edges()    Save egdes to a .edge file.                               //
+//                                                                           //
+// 'filename' is a string containing the file name without suffix.           //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenio::save_edges(char* filename)
+{
+  FILE *fout;
+  char outedgefilename[FILENAMESIZE];
+  int i;
+
+  sprintf(outedgefilename, "%s.edge", filename);
+  printf("Saving edges to %s\n", outedgefilename);
+  fout = fopen(outedgefilename, "w");
+  fprintf(fout, "%d  %d\n", numberofedges, edgemarkerlist != NULL ? 1 : 0);
+  for (i = 0; i < numberofedges; i++) {
+    fprintf(fout, "%d  %4d  %4d", i + firstnumber, edgelist[i * 2],
+            edgelist[i * 2 + 1]);
+    if (edgemarkerlist != NULL) {
+      fprintf(fout, "  %d", edgemarkerlist[i]);
+    }
+    fprintf(fout, "\n");
+  }
+
+  fclose(fout);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// save_neighbors()    Save egdes to a .neigh file.                          //
+//                                                                           //
+// 'filename' is a string containing the file name without suffix.           //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenio::save_neighbors(char* filename)
+{
+  FILE *fout;
+  char outneighborfilename[FILENAMESIZE];
+  int i;
+
+  sprintf(outneighborfilename, "%s.neigh", filename);
+  printf("Saving neighbors to %s\n", outneighborfilename);
+  fout = fopen(outneighborfilename, "w");
+  fprintf(fout, "%d  %d\n", numberoftetrahedra, mesh_dim + 1);
+  for (i = 0; i < numberoftetrahedra; i++) {
+    if (mesh_dim == 2) {
+      fprintf(fout, "%d  %5d  %5d  %5d", i + firstnumber,  neighborlist[i * 3],
+              neighborlist[i * 3 + 1], neighborlist[i * 3 + 2]);
+    } else {
+      fprintf(fout, "%d  %5d  %5d  %5d  %5d", i + firstnumber,
+              neighborlist[i * 4], neighborlist[i * 4 + 1],
+              neighborlist[i * 4 + 2], neighborlist[i * 4 + 3]);
+    }
+    fprintf(fout, "\n");
+  }
+
+  fclose(fout);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// save_poly()    Save segments or facets to a .poly file.                   //
+//                                                                           //
+// 'filename' is a string containing the file name without suffix.  It only  //
+// save the facets, holes and regions.  The nodes are saved in a .node file  //
+// by routine save_nodes().                                                  //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenio::save_poly(char* filename)
+{
+  FILE *fout;
+  facet *f;
+  polygon *p;
+  char outpolyfilename[FILENAMESIZE];
+  int i, j, k;
+
+  sprintf(outpolyfilename, "%s.poly", filename);
+  printf("Saving poly to %s\n", outpolyfilename);
+  fout = fopen(outpolyfilename, "w");
+
+  // The zero indicates that the vertices are in a separate .node file.
+  //   Followed by number of dimensions, number of vertex attributes,
+  //   and number of boundary markers (zero or one).
+  fprintf(fout, "%d  %d  %d  %d\n", 0, mesh_dim, numberofpointattributes,
+          pointmarkerlist != NULL ? 1 : 0);
+
+  // Save segments or facets.
+  if (mesh_dim == 2) {
+    // Number of segments, number of boundary markers (zero or one).
+    fprintf(fout, "%d  %d\n", numberofedges, edgemarkerlist != NULL ? 1 : 0);
+    for (i = 0; i < numberofedges; i++) {
+      fprintf(fout, "%d  %4d  %4d", i + firstnumber, edgelist[i * 2],
+              edgelist[i * 2 + 1]);
+      if (edgemarkerlist != NULL) {
+        fprintf(fout, "  %d", edgemarkerlist[i]);
+      }
+      fprintf(fout, "\n");
+    }
+  } else {
+    // Number of facets, number of boundary markers (zero or one).
+    fprintf(fout, "%d  %d\n", numberoffacets, facetmarkerlist != NULL ? 1 : 0);
+    for (i = 0; i < numberoffacets; i++) {
+      f = &(facetlist[i]);
+      fprintf(fout, "%d  %d  %d  # %d\n", f->numberofpolygons,f->numberofholes,
+            facetmarkerlist != NULL ? facetmarkerlist[i] : 0, i + firstnumber);
+      // Output polygons of this facet.
+      for (j = 0; j < f->numberofpolygons; j++) {
+        p = &(f->polygonlist[j]);
+        fprintf(fout, "%d  ", p->numberofvertices);
+        for (k = 0; k < p->numberofvertices; k++) {
+          if (((k + 1) % 10) == 0) {
+            fprintf(fout, "\n  ");
+          }
+          fprintf(fout, "  %d", p->vertexlist[k]);
+        }
+        fprintf(fout, "\n");
+      }
+      // Output holes of this facet.
+      for (j = 0; j < f->numberofholes; j++) {
+        fprintf(fout, "%d  %.12g  %.12g  %.12g\n", j + firstnumber,
+           f->holelist[j * 3], f->holelist[j * 3 + 1], f->holelist[j * 3 + 2]);
+      }
+    }
+  }
+
+  // Save holes.
+  fprintf(fout, "%d\n", numberofholes);
+  for (i = 0; i < numberofholes; i++) {
+    // Output x, y coordinates.
+    fprintf(fout, "%d  %.12g  %.12g", i + firstnumber, holelist[i * mesh_dim],
+            holelist[i * mesh_dim + 1]);
+    if (mesh_dim == 3) {
+      // Output z coordinate.
+      fprintf(fout, "  %.12g", holelist[i * mesh_dim + 2]);
+    }
+    fprintf(fout, "\n");
+  }
+
+  // Save regions.
+  fprintf(fout, "%d\n", numberofregions);
+  for (i = 0; i < numberofregions; i++) {
+    if (mesh_dim == 2) {
+      // Output the index, x, y coordinates, attribute (region number)
+      //   and maximum area constraint (maybe -1).
+      fprintf(fout, "%d  %.12g  %.12g  %.12g  %.12g\n", i + firstnumber,
+              regionlist[i * 4], regionlist[i * 4 + 1],
+              regionlist[i * 4 + 2], regionlist[i * 4 + 3]);
+    } else {
+      // Output the index, x, y, z coordinates, attribute (region number)
+      //   and maximum volume constraint (maybe -1).
+      fprintf(fout, "%d  %.12g  %.12g  %.12g  %.12g  %.12g\n", i + firstnumber,
+              regionlist[i * 5], regionlist[i * 5 + 1],
+              regionlist[i * 5 + 2], regionlist[i * 5 + 3],
+              regionlist[i * 5 + 4]);
+    }
+  }
+
+  fclose(fout);  
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// readline()   Read a nonempty line from a file.                            //
+//                                                                           //
+// A line is considered "nonempty" if it contains something more than white  //
+// spaces.  If a line is considered empty, it will be dropped and the next   //
+// line will be read, this process ends until reaching the end-of-file or a  //
+// non-empty line.  Return NULL if it is the end-of-file, otherwise, return  //
+// a pointer to the first non-whitespace character of the line.              //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+char* tetgenio::readline(char *string, FILE *infile, int *linenumber)
+{
+  char *result;
+
+  // Search for a non-empty line.
+  do {
+    result = fgets(string, INPUTLINESIZE - 1, infile);
+    (*linenumber)++;
+    if (result == (char *) NULL) {
+      return (char *) NULL;
+    }
+    // Skip white spaces.
+    while ((*result == ' ') || (*result == '\t')) result++;
+    // If it's end of line, read another line and try again.
+  } while (*result == '\0');
+  return result;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// findnextfield()   Find the next field of a string.                        //
+//                                                                           //
+// Jumps past the current field by searching for whitespace or a comma, then //
+// jumps past the whitespace or the comma to find the next field.            //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+char* tetgenio::findnextfield(char *string)
+{
+  char *result;
+
+  result = string;
+  // Skip the current field.  Stop upon reaching whitespace or a comma.
+  while ((*result != '\0') && (*result != ' ') &&  (*result != '\t') && 
+         (*result != ',')) {
+    result++;
+  }
+  // Now skip the whitespace or the comma, stop at anything else that looks
+  //   like a character, or the end of a line. 
+  while ((*result == ' ') || (*result == '\t') || (*result == ',')) {
+    result++;
+  }
+  return result;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// readnumberline()   Read a nonempty number line from a file.               //
+//                                                                           //
+// A line is considered "nonempty" if it contains something that looks like  //
+// a number.  Comments (prefaced by `#') are ignored.                        //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+char* tetgenio::readnumberline(char *string, FILE *infile, char *infilename)
+{
+  char *result;
+
+  // Search for something that looks like a number.
+  do {
+    result = fgets(string, INPUTLINESIZE, infile);
+    if (result == (char *) NULL) {
+      printf("  Error:  Unexpected end of file in %s.\n", infilename);
+      exit(1);
+    }
+    // Skip anything that doesn't look like a number, a comment, 
+    //   or the end of a line. 
+    while ((*result != '\0') && (*result != '#')
+           && (*result != '.') && (*result != '+') && (*result != '-')
+           && ((*result < '0') || (*result > '9'))) {
+      result++;
+    }
+    // If it's a comment or end of line, read another line and try again.
+  } while ((*result == '#') || (*result == '\0'));
+  return result;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// findnextnumber()   Find the next field of a number string.                //
+//                                                                           //
+// Jumps past the current field by searching for whitespace or a comma, then //
+// jumps past the whitespace or the comma to find the next field that looks  //
+// like a number.                                                            //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+char* tetgenio::findnextnumber(char *string)
+{
+  char *result;
+
+  result = string;
+  // Skip the current field.  Stop upon reaching whitespace or a comma.
+  while ((*result != '\0') && (*result != '#') && (*result != ' ') && 
+         (*result != '\t') && (*result != ',')) {
+    result++;
+  }
+  // Now skip the whitespace and anything else that doesn't look like a
+  //   number, a comment, or the end of a line. 
+  while ((*result != '\0') && (*result != '#')
+         && (*result != '.') && (*result != '+') && (*result != '-')
+         && ((*result < '0') || (*result > '9'))) {
+    result++;
+  }
+  // Check for a comment (prefixed with `#').
+  if (*result == '#') {
+    *result = '\0';
+  }
+  return result;
+}
+
+//
+// End of class 'tetgenio' implementation
+//
+
+static REAL PI = 3.14159265358979323846264338327950288419716939937510582;
+
+//
+// Begin of class 'tetgenbehavior' implementation
+//
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// tetgenbehavior()    Initialize veriables of 'tetgenbehavior'.             //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+tetgenbehavior::tetgenbehavior()
+{
+  // Initialize command line switches.
+  plc = 0;
+  refine = 0;
+  quality = 0; 
+  minratio = 2.0;
+  goodratio = 0.0;
+  minangle = 20.0;
+  goodangle = 0.0;
+  varvolume = 0;
+  fixedvolume = 0;
+  maxvolume = -1.0;
+  regionattrib = 0;
+  insertaddpoints = 0;
+  removesliver = 0;
+  maxdihedral = 0.0;
+  detectinter = 0;
+  checkclosure = 0;
+  zeroindex = 0;
+  jettison = 0; 
+  facesout = 0;
+  edgesout = 0;
+  neighout = 0;
+  meditview = 0;
+  gidview = 0;
+  geomview = 0;
+  order = 1;
+  nobound = 0;
+  nonodewritten = 0;
+  noelewritten = 0;
+  nofacewritten = 0;
+  noiterationnum = 0;
+  nobisect = 0;
+  noflip = 0;
+  steiner = -1;
+  dopermute = 0;
+  srandseed = 1;
+  nomerge = 0;
+  docheck = 0;
+  quiet = 0;
+  verbose = 0;
+  useshelles = 0;
+  epsilon = 1.0e-8;
+  object = NONE;
+  // Initialize strings
+  commandline[0] = '\0';
+  infilename[0] = '\0';
+  outfilename[0] = '\0';
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// versioninfo()    Print the version information of TetGen.                 //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenbehavior::versioninfo()
+{
+  printf("Version 1.3.2 (Released on December 13, 2004).\n");
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// syntax()    Print list of command line switches and exit the program.     //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenbehavior::syntax()
+{
+  printf("  tetgen [-pq__a__AriMS__T__dzo_fengGOBNEFICQVvh] input_file\n");
+  printf("    -p  Tetrahedralizes a piecewise linear complex.\n");
+  printf("    -q  Quality mesh generation. A minimum radius-edge ratio may\n");
+  printf("        be specified (default 2.0).\n");
+  printf("    -a  Applies a maximum tetrahedron volume constraint.\n");
+  printf("    -A  Assigns attributes to identify tetrahedra in certain ");
+  printf("regions.\n");
+  printf("    -r  Reconstructs/Refines a previously generated mesh.\n");
+  printf("    -i  Inserts a list of additional points into mesh.\n");
+  printf("    -M  Does not merge coplanar facets.\n");
+  printf("    -S  Specifies maximum number of added Steiner points.\n");
+  printf("    -T  Set a tolerance for coplanar test (default 1e-8).\n");
+  printf("    -d  Detect intersections of PLC facets.\n");
+  printf("    -z  Numbers all output items starting from zero.\n");
+  printf("    -j  Jettison unused vertices from output .node file.\n");
+  printf("    -o2 Generates second-order subparametric elements.\n");
+  printf("    -f  Outputs faces (including non-boundary faces) to .face ");
+  printf("file.\n");
+  printf("    -e  Outputs subsegments to .edge file.\n");
+  printf("    -n  Outputs tetrahedra neighbors to .neigh file.\n");
+  printf("    -g  Outputs mesh to .mesh file for viewing by Medit.\n");
+  printf("    -G  Outputs mesh to .msh file for viewing by Gid.\n");
+  printf("    -O  Outputs mesh to .off file for viewing by Geomview.\n");
+  printf("    -B  Suppresses output of boundary information.\n");
+  printf("    -N  Suppresses output of .node file.\n");
+  printf("    -E  Suppresses output of .ele file.\n");
+  printf("    -F  Suppresses output of .face file.\n");
+  printf("    -I  Suppresses mesh iteration numbers.\n");
+  printf("    -C  Checks the consistency of the final mesh.\n");
+  printf("    -Q  Quiet:  No terminal output except errors.\n");
+  printf("    -V  Verbose:  Detailed information, more terminal output.\n");
+  printf("    -v  Prints the version information.\n");
+  printf("    -h  Help:  A brief instruction for using TetGen.\n");
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// usage()    Print a brief instruction for using TetGen.                    //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenbehavior::usage()
+{
+  printf("TetGen\n");
+  printf("A Quality Tetrahedral Mesh Generator and 3D Delaunay ");
+  printf("Triangulator\n");
+  versioninfo();
+  printf("\n");
+  printf("Copyright 2002, 2004\n");
+  printf("Hang Si\n");
+  printf("Rathausstr. 9, 10178 Berlin, Germany\n");
+  printf("si@wias-berlin.de\n");
+  printf("\n");
+  printf("What Can TetGen Do?\n");
+  printf("\n");
+  printf("  TetGen generates exact Delaunay tetrahedralizations, exact\n");
+  printf("  constrained Delaunay tetrahedralizations, and quality ");
+  printf("tetrahedral\n  meshes. The latter are nicely graded and whose ");
+  printf("tetrahedra have\n  radius-edge ratio bounded, thus are suitable ");
+  printf("for finite element and\n  finite volume analysis.\n"); 
+  printf("\n");
+  printf("Command Line Syntax:\n");
+  printf("\n");
+  printf("  Below is the command line syntax of TetGen with a list of ");
+  printf("short\n");
+  printf("  descriptions. Underscores indicate that numbers may optionally\n");
+  printf("  follow certain switches.  Do not leave any space between a ");
+  printf("switch\n");
+  printf("  and its numeric parameter.  \'input_file\' contains input data\n");
+  printf("  depending on the switches you supplied which may be a ");
+  printf("  piecewise\n");
+  printf("  linear complex or a list of nodes.  File formats and detailed\n");
+  printf("  description of command line switches are found in user's ");
+  printf("manual.\n");
+  printf("\n");
+  syntax();
+  printf("\n");
+  printf("Examples of How to Use TetGen:\n");
+  printf("\n");
+  printf("  \'tetgen object\' reads vertices from object.node, and writes ");
+  printf("their\n  Delaunay tetrahedralization to object.1.node and ");
+  printf("object.1.ele.\n");
+  printf("\n");
+  printf("  \'tetgen -p object\' reads a PLC from object.poly or object.");
+  printf("smesh (and\n  possibly object.node) and writes its constrained ");
+  printf("Delaunay\n  tetrahedralization to object.1.node, object.1.ele and ");
+  printf("object.1.face.\n");
+  printf("\n");
+  printf("  \'tetgen -pq1.414a.1 object\' reads a PLC from object.poly or\n");
+  printf("  object.smesh (and possibly object.node), generates a mesh ");
+  printf("whose\n  tetrahedra have radius-edge ratio smaller than 1.414 and ");
+  printf("have volume\n  of 0.1 or less, and writes the mesh to ");
+  printf("object.1.node, object.1.ele\n  and object.1.face.\n");
+  printf("\n");
+  printf("Please send bugs/comments to Hang Si <si@wias-berlin.de>\n");
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// 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.                           //
+//                                                                           //
+// When TetGen is invoked from an environment. 'argc' is nonzero, switches   //
+// and input filename should be supplied as zero-terminated strings in       //
+// argv[0] through argv[argc - 1] and argv[0] shall be the name used to      //
+// invoke TetGen, i.e. "tetgen".  Switches are previously started with a     //
+// dash '-' to identify them from the input filename.                        //
+//                                                                           //
+// When TetGen is called from within another program. 'argc' is set to zero. //
+// switches are given in one zero-terminated string (no previous dash is     //
+// required.), and 'argv' is a pointer points to this string.  No input      //
+// filename is required (usually the input data has been directly created by //
+// user in the 'tetgenio' structure).  A default filename 'tetgen-tmpfile'   //
+// will be created for debugging output purpose.                             //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+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 switches.
+    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';
+        // Go to the next string directly.
+        continue;                     
+      }
+    }
+    // Parse the individual switch from the string.
+    for (j = startindex; argv[i][j] != '\0'; j++) {
+      if (argv[i][j] == 'p') {
+        plc = 1;
+      } 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);
+        } 
+      } else if (argv[i][j] == 'a') {
+        quality = 1;
+        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);
+          if (maxvolume <= 0.0) {
+            printf("Error:  Number after -a must be greater than zero.\n");
+            return false;
+	  }
+	} else {
+          varvolume = 1;
+	}
+      } else if (argv[i][j] == 'A') {
+        regionattrib = 1;
+      } else if (argv[i][j] == 'i') {
+        insertaddpoints = 1;
+      } else if (argv[i][j] == 's') {
+        removesliver = 1;
+        if ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) {
+          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';
+          maxdihedral = (REAL) strtod(workstring, (char **) NULL);
+          if (maxdihedral <= 0.0 || maxdihedral >= 180.0) {
+            printf("Error:  Number after -s must between 0 and 180.\n");
+            return false;
+          }
+        } else {
+          maxdihedral = 175.0;
+        }
+        maxdihedral = maxdihedral * 3.1415926535897932 / 180.;
+      } else if (argv[i][j] == 'd') {
+        detectinter = 1;
+      } else if (argv[i][j] == 'c') {
+        checkclosure = 1;
+      } else if (argv[i][j] == 'z') {
+        zeroindex = 1;
+      } else if (argv[i][j] == 'j') {
+        jettison = 1;
+      } else if (argv[i][j] == 'e') {
+        edgesout = 1;
+      } else if (argv[i][j] == 'n') {
+        neighout = 1;
+      } else if (argv[i][j] == 'g') {
+        meditview = 1;
+      } else if (argv[i][j] == 'G') {
+        gidview = 1;
+      } else if (argv[i][j] == 'O') {
+        geomview = 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] == 'o') {
+        if (argv[i][j + 1] == '2') {
+          j++;
+          order = 2;
+        }
+      } else if (argv[i][j] == 'Y') {
+        noflip = 1; // nobisect++;
+      } 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';
+          steiner = (int) strtol(workstring, (char **) NULL, 0);
+        } 
+      } else if (argv[i][j] == 'P') {
+        dopermute = 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] == '.') || (argv[i][j + 1] == 'e') ||
+                 (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) {
+            j++;
+            workstring[k] = argv[i][j];
+            k++;
+          }
+          workstring[k] = '\0';
+          srandseed = (int) strtol(workstring, (char **) NULL, 0);
+        } 
+      } else if (argv[i][j] == 'M') {
+        nomerge = 1;
+      } 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);
+        } 
+        if (epsilon <= 0.0) {
+          printf("Error:  Number after -T must be greater than zero.\n");
+          return false;
+        }
+      } 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] == 'v') {
+        versioninfo();
+        exit(0);
+      } else if ((argv[i][j] == 'h') || (argv[i][j] == 'H') ||
+                 (argv[i][j] == '?')) {
+        usage();
+        exit(0);
+      } 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') {
+      // No input file name. Print the syntax and exit.
+      syntax();
+      exit(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;
+      plc = 1;
+    } else if (!strcmp(&infilename[strlen(infilename) - 4], ".ele")) {
+      infilename[strlen(infilename) - 4] = '\0';
+      object = MESH;
+      refine = 1;
+    }
+  }
+  plc = plc || detectinter || checkclosure;
+  useshelles = plc || refine || quality;
+  goodratio = minratio;
+  goodratio *= goodratio;
+
+  // Detect improper combinations of switches.
+  if (plc && refine) {
+    printf("Error:  Switch -r cannot use together with -p.\n");
+    return false;
+  }
+  if (refine && (plc || noiterationnum)) {
+    printf("Error:  Switches %s cannot use together with -r.\n",
+           "-p, -d, -c, and -I");
+    return false;
+  }
+  if (detectinter && (quality || insertaddpoints || (order == 2) || neighout
+      || checkclosure || docheck)) {
+    printf("Error:  Switches %s cannot use together with -d.\n",
+           "-c, -q, -i, -o2, -n, and -C");
+    return false;
+  }
+  if (checkclosure && (quality || insertaddpoints || (order == 2) || neighout
+      || detectinter || docheck)) {
+    printf("Error:  Switches %s cannot use together with -c.\n",
+           "-d, -q, -i, -o2, -n, and -C");
+    return false;
+  }
+
+  // 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;
+  }
+  // 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;
+  }
+  // Calculate the goodangle for testing bad subfaces.
+  goodangle = cos(minangle * PI / 180.0);
+  goodangle *= goodangle;
+
+  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);
+  }
+
+  return true;
+}
+
+//
+// End of class 'tetgenbehavior' implementation
+//
+
+//
+// Begin of class 'tetgenmesh' implementation
+//
+
+//
+// Begin of class 'list', 'memorypool' and 'link' implementation
+//
+
+// Following are predefined compare functions for primitive data types. 
+//   These functions take two pointers of the corresponding date type,
+//   perform the comparation. Return -1, 0 or 1 indicating the default
+//   linear order of two operators.
+
+// Compare two 'integers'.
+int tetgenmesh::compare_2_ints(const void* x, const void* y) {
+  if (* (int *) x < * (int *) y) {
+    return -1;
+  } else if (* (int *) x > * (int *) y) {
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+// Compare two 'longs'.  Note: in 64-bit machine the 'long' type is 64-bit
+//   (8-byte) where the 'int' only 32-bit (4-byte).
+int tetgenmesh::compare_2_longs(const void* x, const void* y) {
+  if (* (long *) x < * (long *) y) {
+    return -1;
+  } else if (* (long *) x > * (long *) y) {
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+// Compare two 'unsigned longs'.
+int tetgenmesh::compare_2_unsignedlongs(const void* x, const void* y) {
+  if (* (unsigned long *) x < * (unsigned long *) y) {
+    return -1;
+  } else if (* (unsigned long *) x > * (unsigned long *) y) {
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// set_compfunc()    Determine the size of primitive data types and set the  //
+//                   corresponding predefined linear order functions.        //
+//                                                                           //
+// 'str' is a zero-end string indicating a primitive data type, like 'int',  //
+// 'long' or 'unsigned long'.  Every string ending with a '*' is though as a //
+// type of pointer and the type 'unsign long' is used for it.                //
+//                                                                           //
+// When the type of 'str' is determined, the size of this type (in byte) is  //
+// returned in 'itbytes', and the pointer of corresponding predefined linear //
+// order functions is returned in 'pcomp'.                                   //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::set_compfunc(char* str, int* itbytes, compfunc* pcomp)
+{
+  // First figure out whether it is a pointer or not.
+  if (str[strlen(str) - 1] == '*') {
+    *itbytes = sizeof(unsigned long);
+    *pcomp = &compare_2_unsignedlongs;
+    return;
+  }
+  // Then determine other types.
+  if (strcmp(str, "int") == 0) {
+    *itbytes = sizeof(int);
+    *pcomp = &compare_2_ints;
+  } else if (strcmp(str, "long") == 0) {
+    *itbytes = sizeof(long);
+    *pcomp = &compare_2_longs;
+  } else if (strcmp(str, "unsigned long") == 0) {
+    *itbytes = sizeof(unsigned long);
+    *pcomp = &compare_2_unsignedlongs;
+  } else {
+    // It is an unknown type.
+    printf("Error in set_compfunc():  unknown type %s.\n", str);
+    exit(1);
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// listinit()    Initialize a list for storing a data type.                  //
+//                                                                           //
+// Determine the size of each item, set the maximum size allocated at onece, //
+// set the expand size in case the list is full, and set the linear order    //
+// function if it is provided (default is NULL).                             //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::list::
+listinit(int itbytes, compfunc pcomp, int mitems,int exsize)
+{
+  assert(itbytes > 0 && mitems > 0 && exsize > 0);
+
+  itembytes = itbytes;
+  comp = pcomp;
+  maxitems = mitems;
+  expandsize = exsize;
+  base = (char *) malloc(maxitems * itembytes); 
+  if (base == (char *) NULL) {
+    printf("Error:  Out of memory.\n");
+    exit(1);
+  }
+  items = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// append()    Add a new item at the end of the list.                        //
+//                                                                           //
+// A new space at the end of this list will be allocated for storing the new //
+// item. If the memory is not sufficient, reallocation will be performed. If //
+// 'appitem' is not NULL, the contents of this pointer will be copied to the //
+// new allocated space.  Returns the pointer to the new allocated space.     //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void* tetgenmesh::list::append(void *appitem)
+{
+  // Do we have enough space?
+  if (items == maxitems) {
+    char* newbase = (char *) realloc(base, (maxitems + expandsize) * 
+                                     itembytes);
+    if (newbase == (char *) NULL) {
+      printf("Error:  Out of memory.\n");
+      exit(1);
+    }
+    base = newbase;
+    maxitems += expandsize;
+  }
+  if (appitem != (void *) NULL) {
+    memcpy(base + items * itembytes, appitem, itembytes);
+  }
+  items++;
+  return (void *) (base + (items - 1) * itembytes);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// insert()    Insert an item before 'pos' (range from 0 to items - 1).      //
+//                                                                           //
+// A new space will be inserted at the position 'pos', that is, items lie    //
+// after pos (including the item at pos) will be moved one space downwords.  //
+// If 'insitem' is not NULL, its contents will be copied into the new        //
+// inserted space. Return a pointer to the new inserted space.               //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void* tetgenmesh::list::insert(int pos, void* insitem)
+{
+  if (pos >= items) {
+    return append(insitem);
+  }
+  // Do we have enough space.
+  if (items == maxitems) {
+    char* newbase = (char *) realloc(base, (maxitems + expandsize) *
+                                     itembytes);
+    if (newbase == (char *) NULL) {
+      printf("Error:  Out of memory.\n");
+      exit(1);
+    }
+    base = newbase;
+    maxitems += expandsize;
+  }
+  // Do block move.
+  memmove(base + (pos + 1) * itembytes,   // dest
+          base + pos * itembytes,         // src
+          (items - pos) * itembytes);     // size in bytes
+  // Insert the item.
+  if (insitem != (void *) NULL) {
+    memcpy(base + pos * itembytes, insitem, itembytes);
+  }
+  items++;
+  return (void *) (base + pos * itembytes);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// del()    Delete an item at 'pos' (range from 0 to items - 1).             //
+//                                                                           //
+// The space at 'pos' will be overlapped by other items, that is, items lie  //
+// after pos will be moved one space upwords.                                //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::list::del(int pos)
+{
+  // If 'pos' is the last itemof the list, nothing need to do.
+  if (pos >= 0 && pos < items - 1) {
+    // Do block move.
+    memmove(base + pos * itembytes,       // dest
+            base + (pos + 1) * itembytes, // src
+            (items - pos - 1) * itembytes);
+  }
+  if (items > 0) {
+    items--;
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// hasitem()    Search in this list to find if 'checkitem' exists.           //
+//                                                                           //
+// This routine assumes that a linear order function has been set.  It loops //
+// through the entire list, compares each item to 'checkitem'. If it exists, //
+// return its position (between 0 to items - 1), otherwise, return -1.       //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::list::hasitem(void* checkitem)
+{
+  int i;
+
+  for (i = 0; i < items; i++) {
+    if (comp != (compfunc) NULL) {
+      if ((* comp)((void *)(base + i * itembytes), checkitem) == 0) {
+        return i;
+      }
+    }
+  }
+  return -1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// remove()    Remove an item (indicated by its pointer) from the list.      //
+//                                                                           //
+// If the list contains more than one copy of the pointer, only the first    //
+// copy is removed.  The returned value is the index of the removed item.    //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int  tetgenmesh::list::remove(void* remitem)
+{
+  int pos = hasitem(remitem);
+  if (pos != -1) {
+    del(pos);
+  }
+  return pos;
+} 
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// sort()    Sort the items with respect to a linear order function.         //
+//                                                                           //
+// Uses QuickSort routines (qsort) of the standard C/C++ library (stdlib.h). //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::list::sort()
+{
+  qsort((void *) base, (size_t) items, (size_t) itembytes, comp);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// memorypool()   The constructors of memorypool.                            //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+tetgenmesh::memorypool::memorypool()
+{
+  firstblock = nowblock = (void **) NULL;
+  nextitem = (void *) NULL;
+  deaditemstack = (void *) NULL;
+  pathblock = (void **) NULL;
+  pathitem = (void *) NULL;
+  itemwordtype = POINTER;
+  alignbytes = 0;
+  itembytes = itemwords = 0;
+  itemsperblock = 0;
+  items = maxitems = 0l;
+  unallocateditems = 0;
+  pathitemsleft = 0;
+}
+
+tetgenmesh::memorypool::
+memorypool(int bytecount, int itemcount, enum wordtype wtype, int alignment)
+{
+  poolinit(bytecount, itemcount, wtype, 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, enum wordtype wtype, int alignment)
+{
+  int wordsize;
+
+  // Initialize values in the pool.
+  itemwordtype = wtype;
+  wordsize = (itemwordtype == POINTER) ? sizeof(void *) : sizeof(REAL);
+  // 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 (sizeof(void *) > alignbytes) {
+    alignbytes = 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) {
+    printf("Error:  Out of memory.\n");
+    exit(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()
+{
+  unsigned long 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 = (unsigned long) (nowblock + 1);
+  // Align the item on an `alignbytes'-byte boundary.
+  nextitem = (void *)
+    (alignptr + (unsigned long) alignbytes -
+     (alignptr % (unsigned long) 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;
+  unsigned long 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) {
+          printf("Error:  Out of memory.\n");
+          exit(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 = (unsigned long) (nowblock + 1);
+      // Align the item on an `alignbytes'-byte boundary.
+      nextitem = (void *)
+        (alignptr + (unsigned long) alignbytes -
+         (alignptr % (unsigned long) 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.
+    if (itemwordtype == POINTER) {
+      nextitem = (void *) ((void **) nextitem + itemwords);
+    } else {
+      nextitem = (void *) ((REAL *) nextitem + itemwords);
+    }
+    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()
+{
+  unsigned long alignptr;
+
+  // Begin the traversal in the first block.
+  pathblock = firstblock;
+  // Find the first item in the block.  Increment by the size of (void *).
+  alignptr = (unsigned long) (pathblock + 1);
+  // Align with item on an `alignbytes'-byte boundary.
+  pathitem = (void *)
+    (alignptr + (unsigned long) alignbytes -
+     (alignptr % (unsigned long) 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;
+  unsigned long 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 = (unsigned long) (pathblock + 1);
+    // Align with item on an `alignbytes'-byte boundary.
+    pathitem = (void *)
+      (alignptr + (unsigned long) alignbytes -
+       (alignptr % (unsigned long) alignbytes));
+    // Set the number of items left in the current block.
+    pathitemsleft = itemsperblock;
+  }
+  newitem = pathitem;
+  // Find the next item in the block.
+  if (itemwordtype == POINTER) {
+    pathitem = (void *) ((void **) pathitem + itemwords);
+  } else {
+    pathitem = (void *) ((REAL *) pathitem + itemwords);
+  }
+  pathitemsleft--;
+  return newitem;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// linkinit()    Initialize a link for storing items.                        //
+//                                                                           //
+// The input parameters are the size of each item, a pointer of a linear     //
+// order function and the number of items allocating in one memory bulk.     //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::link::linkinit(int bytecount, compfunc pcomp, int itemcount)
+{
+  assert(bytecount > 0 && itemcount > 0);  
+
+  // Remember the real size of each item.
+  linkitembytes = bytecount;
+  // Set the linear order function for this link.
+  comp = pcomp;
+
+  // Call the constructor of 'memorypool' to initialize its variables.
+  //   like: itembytes, itemwords, items, ... Each node has size
+  //   bytecount + 2 * sizeof(void **), and total 'itemcount + 2' (because
+  //   link has additional two nodes 'head' and 'tail').
+  poolinit(bytecount + 2 * sizeof(void **), itemcount + 2, POINTER, 0);
+  
+  // Initial state of this link.
+  head = (void **) alloc();
+  tail = (void **) alloc();
+  *head = (void *) tail;
+  *(head + 1) = NULL;
+  *tail = NULL;
+  *(tail + 1) = (void *) head;
+  nextlinkitem = *head;
+  curpos = 1;
+  linkitems = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// clear()   Deallocate all nodes in this link.                              //
+//                                                                           //
+// The link 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::link::clear()
+{
+  // Reset the pool.
+  restart();
+
+  // Initial state of this link.
+  head = (void **) alloc();
+  tail = (void **) alloc();
+  *head = (void *) tail;
+  *(head + 1) = NULL;
+  *tail = NULL;
+  *(tail + 1) = (void *) head;
+  nextlinkitem = *head;
+  curpos = 1;
+  linkitems = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// move()    Causes 'nextlinkitem' to traverse the specified number of nodes,//
+//           updates 'curpos' to be the node to which 'nextlinkitem' points. //
+//                                                                           //
+// 'numberofnodes' is a number indicating how many nodes need be traversed   //
+// (not counter the current node) need be traversed. It may be positive(move //
+// forward) or negative (move backward).  Return TRUE if it is successful.   //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::link::move(int numberofnodes)
+{
+  void **nownode;
+  int i;
+
+  nownode = (void **) nextlinkitem;
+  if (numberofnodes > 0) {
+    // Move forward.
+    i = 0;
+    while ((i < numberofnodes) && *nownode) {
+      nownode = (void **) *nownode;
+      i++;
+    }
+    if (*nownode == NULL) return false;
+    nextlinkitem = (void *) nownode;
+    curpos += numberofnodes;
+  } else if (numberofnodes < 0) {
+    // Move backward.
+    i = 0;
+    numberofnodes = -numberofnodes;
+    while ((i < numberofnodes) && *(nownode + 1)) {
+      nownode = (void **) *(nownode + 1);
+      i++;
+    }
+    if (*(nownode + 1) == NULL) return false;
+    nextlinkitem = (void *) nownode;
+    curpos -= numberofnodes;
+  }
+  return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// locate()    Locates the node at the specified position.                   //
+//                                                                           //
+// The number 'pos' (between 1 and 'linkitems') indicates the location. This //
+// routine first decides the shortest path traversing from 'curpos' to 'pos',//
+// i.e., from head, tail or 'curpos'.   Routine 'move()' is called to really //
+// traverse the link. If success, 'nextlinkitem' points to the node, 'curpos'//
+// and 'pos' are equal. Otherwise, return FALSE.                             //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::link::locate(int pos)
+{
+  int headdist, taildist, curdist;
+  int abscurdist, mindist;
+
+  if (pos < 1 || pos > linkitems) return false;
+
+  headdist = pos - 1;
+  taildist = linkitems - pos;
+  curdist = pos - curpos;
+  abscurdist = curdist >= 0 ? curdist : -curdist;
+
+  if (headdist > taildist) {
+    if (taildist > abscurdist) {
+      mindist = curdist;
+    } else {
+      // taildist <= abs(curdist)
+      mindist = -taildist;
+      goend();
+    }
+  } else {
+    // headdist <= taildist
+    if (headdist > abscurdist) {
+      mindist = curdist;
+    } else {
+      // headdist <= abs(curdist)
+      mindist = headdist;
+      rewind();
+    }
+  }
+
+  return move(mindist);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// add()    Add a node at the end of this link.                              //
+//                                                                           //
+// A new node is appended to the end of the link.  If 'newitem' is not NULL, //
+// its conents will be copied to the data slot of the new node. Returns the  //
+// pointer to the newest added node.                                         //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void* tetgenmesh::link::add(void* newitem)
+{
+  void **newnode = tail;
+  if (newitem != (void *) NULL) {
+    memcpy((void *)(newnode + 2), newitem, linkitembytes);
+  }
+  tail = (void **) alloc();
+  *tail = NULL;
+  *newnode = (void*) tail;
+  *(tail + 1) = (void*) newnode;
+  linkitems++;
+  return (void *)(newnode + 2);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// insert()    Inserts a node before the specified position.                 //
+//                                                                           //
+// 'pos' (between 1 and 'linkitems') indicates the inserting position.  This //
+// routine inserts a new node before the node of 'pos'.  If 'newitem' is not //
+// NULL,  its conents will be copied into the data slot of the new node.  If //
+// 'pos' is larger than 'linkitems', it is equal as 'add()'.  A pointer to   //
+// the newest inserted item is returned.                                     //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void* tetgenmesh::link::insert(int pos, void* insitem)
+{
+  if (!locate(pos)) {
+    return add(insitem);
+  }
+
+  void **nownode = (void **) nextlinkitem;
+
+  // Insert a node before 'nownode'.
+  void **newnode = (void **) alloc();
+  if (insitem != (void *) NULL) {
+    memcpy((void *)(newnode + 2), insitem, linkitembytes);
+  }
+
+  *(void **)(*(nownode + 1)) = (void *) newnode;
+  *newnode = (void *) nownode;
+  *(newnode + 1) = *(nownode + 1);
+  *(nownode + 1) = (void *) newnode;
+
+  linkitems++;
+
+  nextlinkitem = (void *) newnode;
+  return (void *)(newnode + 2);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// del()    Delete a node containing the given pointer.                      //
+//                                                                           //
+// Returns a pointer of the deleted data. If you try to delete a non-existed //
+// node (e.g. link is empty or a wrong index is given) return NULL.          //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void* tetgenmesh::link::del(void* delitem)
+{
+  void **deadnode = (void **) ((void **) delitem - 2);
+  
+  // now delete the nownode
+  void **nextnode = (void **) *deadnode;
+  void **prevnode = (void **) *(deadnode + 1);
+  *prevnode = (void *) nextnode;
+  *(nextnode + 1) = (void *) prevnode;
+
+  dealloc((void *) deadnode);
+  linkitems--;
+
+  nextlinkitem = (void *) nextnode;
+  return (void *)(deadnode + 2);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// del()    Delete a node at the specified position.                         //
+//                                                                           //
+// 'pos' between 1 and 'linkitems'.  Returns a pointer of the deleted data.  //
+// If you try to delete a non-existed node (e.g. link is empty or a wrong    //
+// index is given) return NULL.                                              //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void* tetgenmesh::link::del(int pos)
+{
+  if (!locate(pos) || (linkitems == 0)) {
+    return (void *) NULL;
+  }
+  return del((void *) ((void **) nextlinkitem + 2));
+  /*
+  void **deadnode = (void **)nextlinkitem;
+
+  // now delete the nownode
+  void **nextnode = (void **) *deadnode;
+  void **prevnode = (void **) *(deadnode + 1);
+  *prevnode = (void *) nextnode;
+  *(nextnode + 1) = (void *) prevnode;
+
+  dealloc((void *) deadnode);
+  linkitems--;
+
+  nextlinkitem = (void *) nextnode;
+  return (void *)(deadnode + 2);
+  */
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// getitem()    The link traversal routine.                                  //
+//                                                                           //
+// Returns the node to which 'nextlinkitem' points. Returns a 'NULL' if the  //
+// end of the link is reaching.  Both 'nextlinkitem' and 'curpos' will be    //
+// updated after this operation.                                             //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void* tetgenmesh::link::getitem()
+{
+  if (nextlinkitem == (void *) tail) return NULL;
+  void **nownode = (void **) nextlinkitem;
+  nextlinkitem = *nownode;
+  curpos += 1;
+  return (void *)(nownode + 2);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// getnitem()    Returns the node at a specified position.                   //
+//                                                                           //
+// 'pos' between 1 and 'linkitems'. After this operation, 'nextlinkitem' and //
+// 'curpos' will be updated to indicate this node.                           //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void* tetgenmesh::link::getnitem(int pos)
+{
+  if (!locate(pos)) return NULL;
+  return (void *)((void **) nextlinkitem + 2);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// hasitem()    Search in this link to find if 'checkitem' exists.           //
+//                                                                           //
+// If 'checkitem' exists, return its position (between 1 to 'linkitems'),    //
+// otherwise, return -1. This routine requires the linear order function has //
+// been set.                                                                 //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int tetgenmesh::link::hasitem(void* checkitem)
+{
+  void *pathitem;
+  int count;
+
+  rewind();
+  pathitem = getitem();
+  count = 0;
+  while (pathitem) {
+    count ++;
+    if (comp) {
+      if ((* comp)(pathitem, checkitem) == 0) {
+        return count;
+      }
+    } 
+    pathitem = getitem();
+  }
+  return -1;
+}
+
+//
+// End of class 'list', 'memorypool' and 'link' implementation
+//
+
+//
+// Begin of mesh manipulation primitives
+//
+
+//
+// Begin of tables initialization.
+//
+
+// For enumerating three edges of a triangle.
+
+int tetgenmesh::plus1mod3[3] = {1, 2, 0};
+int tetgenmesh::minus1mod3[3] = {2, 0, 1};
+
+// Table 've' takes an edge version as input, returns the next edge version
+//   in the same edge ring.
+
+int tetgenmesh::ve[6] = { 2, 5, 4, 1, 0, 3 };
+
+// Tables 'vo', 'vd' and 'va' take an edge version, return the positions of
+//   the origin, destination and apex in the triangle.
+
+int tetgenmesh::vo[6] = { 0, 1, 1, 2, 2, 0 };
+int tetgenmesh::vd[6] = { 1, 0, 2, 1, 0, 2 };
+int tetgenmesh::va[6] = { 2, 2, 0, 0, 1, 1 };
+
+// The following tables are for tetrahedron primitives (operate on trifaces).
+
+// For 'org()', 'dest()' and 'apex()'.  Use 'loc' as the first index and
+//   'ver' as the second index.
+
+int tetgenmesh::locver2org[4][6]  = {
+  0, 1, 1, 2, 2, 0,
+  0, 3, 3, 1, 1, 0,
+  1, 3, 3, 2, 2, 1,
+  2, 3, 3, 0, 0, 2 
+};
+int tetgenmesh::locver2dest[4][6] = { 
+  1, 0, 2, 1, 0, 2,
+  3, 0, 1, 3, 0, 1,
+  3, 1, 2, 3, 1, 2,
+  3, 2, 0, 3, 2, 0
+};
+int tetgenmesh::locver2apex[4][6] = { 
+  2, 2, 0, 0, 1, 1,
+  1, 1, 0, 0, 3, 3,
+  2, 2, 1, 1, 3, 3,
+  0, 0, 2, 2, 3, 3
+};
+
+// For oppo() primitives, use 'loc' as the index.
+
+int tetgenmesh::loc2oppo[4] = { 3, 2, 0, 1 };
+
+// For fnext() primitive.  Use 'loc' as the first index and 'ver' as the
+//   second index. Returns a new 'loc' and new 'ver' in an array. (It is
+//   only valid for edge version equals one of {0, 2, 4}.)
+
+int tetgenmesh::locver2nextf[4][6][2] = {
+  { {1, 5}, {-1, -1}, {2, 5}, {-1, -1}, {3, 5}, {-1, -1} },
+  { {3, 3}, {-1, -1}, {2, 1}, {-1, -1}, {0, 1}, {-1, -1} },
+  { {1, 3}, {-1, -1}, {3, 1}, {-1, -1}, {0, 3}, {-1, -1} },
+  { {2, 3}, {-1, -1}, {1, 1}, {-1, -1}, {0, 5}, {-1, -1} }
+};
+
+//
+// End of tables initialization.
+//
+
+// Some macros for convenience
+
+#define Div2  >> 1
+#define Mod2  & 01
+
+// NOTE: These bit operators should only be used in macros below.
+
+// Get orient(Range from 0 to 2) from face version(Range from 0 to 5).
+
+#define Orient(V)   ((V) Div2)
+
+// Determine edge ring(0 or 1) from face version(Range from 0 to 5).
+
+#define EdgeRing(V) ((V) Mod2)
+
+//
+// Begin of primitives for tetrahedra
+// 
+
+// Each tetrahedron contains four pointers to its neighboring tetrahedra,
+//   with face indices.  To save memory, both information are kept in a
+//   single pointer. To make this possible, all tetrahedra are aligned to
+//   eight-byte boundaries, so that the last three bits of each pointer are
+//   zeros. A face index (in the range 0 to 3) is compressed into the last
+//   two bits of each pointer by the function 'encode()'.  The function
+//   'decode()' decodes a pointer, extracting a face index and a pointer to
+//   the beginning of a tetrahedron.
+
+inline void tetgenmesh::decode(tetrahedron ptr, triface& t) {
+  t.loc = (int) ((unsigned long) (ptr) & (unsigned long) 3l);
+  t.tet = (tetrahedron *) ((unsigned long) (ptr) & ~(unsigned long) 7l);
+}
+
+inline tetgenmesh::tetrahedron tetgenmesh::encode(triface& t) {
+  return (tetrahedron) ((unsigned long) t.tet | (unsigned long) t.loc);
+}
+
+// sym() finds the abutting tetrahedron on the same face.
+
+inline void tetgenmesh::sym(triface& t1, triface& t2) {
+  tetrahedron ptr = t1.tet[t1.loc];
+  decode(ptr, t2);
+}
+
+inline void tetgenmesh::symself(triface& t) {
+  tetrahedron ptr = t.tet[t.loc];
+  decode(ptr, t);
+}
+
+// Bond two tetrahedra together at their faces.
+
+inline void tetgenmesh::bond(triface& t1, triface& t2) {
+  t1.tet[t1.loc] = encode(t2);
+  t2.tet[t2.loc] = encode(t1);
+}
+
+// Dissolve a bond (from one side).  Note that the other tetrahedron will
+//   still think it is connected to this tetrahedron.  Usually, however,
+//   the other tetrahedron is being deleted entirely, or bonded to another
+//   tetrahedron, so it doesn't matter.
+
+inline void tetgenmesh::dissolve(triface& t) {
+  t.tet[t.loc] = (tetrahedron) dummytet;
+}
+
+// These primitives determine or set the origin, destination, apex or
+//   opposition of a tetrahedron with respect to 'loc' and 'ver'.
+
+inline tetgenmesh::point tetgenmesh::org(triface& t) {
+  return (point) t.tet[locver2org[t.loc][t.ver] + 4];
+}
+
+inline tetgenmesh::point tetgenmesh::dest(triface& t) {
+  return (point) t.tet[locver2dest[t.loc][t.ver] + 4];
+}
+
+inline tetgenmesh::point tetgenmesh::apex(triface& t) {
+  return (point) t.tet[locver2apex[t.loc][t.ver] + 4];
+}
+
+inline tetgenmesh::point tetgenmesh::oppo(triface& t) {
+  return (point) t.tet[loc2oppo[t.loc] + 4];
+}
+
+inline void tetgenmesh::setorg(triface& t, point pointptr) {
+  t.tet[locver2org[t.loc][t.ver] + 4] = (tetrahedron) pointptr;
+}
+
+inline void tetgenmesh::setdest(triface& t, point pointptr) {
+  t.tet[locver2dest[t.loc][t.ver] + 4] = (tetrahedron) pointptr;
+}
+
+inline void tetgenmesh::setapex(triface& t, point pointptr) {
+  t.tet[locver2apex[t.loc][t.ver] + 4] = (tetrahedron) pointptr;
+}
+
+inline void tetgenmesh::setoppo(triface& t, point pointptr) {
+  t.tet[loc2oppo[t.loc] + 4] = (tetrahedron) pointptr;
+}
+
+// These primitives were drived from Mucke's triangle-edge data structure
+//   to change face-edge relation in a tetrahedron (esym, enext and enext2)
+//   or between two tetrahedra (fnext).
+
+// If e0 = e(i, j), e1 = e(j, i), that is e0 and e1 are the two directions
+//   of the same undirected edge of a face. e0.sym() = e1 and vice versa.
+
+inline void tetgenmesh::esym(triface& t1, triface& t2) {
+  t2.tet = t1.tet;
+  t2.loc = t1.loc;
+  t2.ver = t1.ver + (EdgeRing(t1.ver) ? -1 : 1);
+}
+
+inline void tetgenmesh::esymself(triface& t) {
+  t.ver += (EdgeRing(t.ver) ? -1 : 1);
+}
+
+// If e0 and e1 are both in the same edge ring of a face, e1 = e0.enext().
+
+inline void tetgenmesh::enext(triface& t1, triface& t2) {
+  t2.tet = t1.tet;
+  t2.loc = t1.loc;
+  t2.ver = ve[t1.ver];
+}
+
+inline void tetgenmesh::enextself(triface& t) {
+  t.ver = ve[t.ver];
+}
+
+// enext2() is equal to e2 = e0.enext().enext()
+
+inline void tetgenmesh::enext2(triface& t1, triface& t2) {
+  t2.tet = t1.tet;
+  t2.loc = t1.loc;
+  t2.ver = ve[ve[t1.ver]];
+}
+
+inline void tetgenmesh::enext2self(triface& t) {
+  t.ver = ve[ve[t.ver]];
+}
+
+// If f0 and f1 are both in the same face ring of a face, f1 = f0.fnext().
+//   If f1 exists, return true. Otherwise, return false, i.e., f0 is a
+//   boundary or hull face.
+
+inline bool tetgenmesh::fnext(triface& t1, triface& t2) {
+  return getnextface(&t1, &t2);
+}
+
+inline bool tetgenmesh::fnextself(triface& t) {
+  return getnextface(&t, NULL);
+}
+
+// enextfnext() and enext2fnext() are combination primitives of enext(),
+//   enext2() and fnext().
+
+inline void tetgenmesh::enextfnext(triface& t1, triface& t2) {
+  enext(t1, t2);
+  fnextself(t2);
+}
+
+inline void tetgenmesh::enextfnextself(triface& t) {
+  enextself(t);
+  fnextself(t);
+}
+
+inline void tetgenmesh::enext2fnext(triface& t1, triface& t2) {
+  enext2(t1, t2);
+  fnextself(t2);
+}
+
+inline void tetgenmesh::enext2fnextself(triface& t) {
+  enext2self(t);
+  fnextself(t);
+}
+
+// Primitives to infect or cure a tetrahedron with the virus.   The last
+//   third bit of the pointer is marked for infection.  These rely on the
+//   assumption that all tetrahedron are aligned to eight-byte boundaries.
+
+inline void tetgenmesh::infect(triface& t) {
+  t.tet[0] = (tetrahedron) ((unsigned long) t.tet[0] | (unsigned long) 4l);
+}
+
+inline void tetgenmesh::uninfect(triface& t) {
+  t.tet[0] = (tetrahedron) ((unsigned long) t.tet[0] & ~ (unsigned long) 4l);
+}
+
+// Test a tetrahedron for viral infection.
+
+inline bool tetgenmesh::infected(triface& t) {
+  return (((unsigned long) t.tet[0] & (unsigned long) 4l) != 0);
+}
+
+// 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;
+}
+
+//
+// End of primitives for tetrahedra
+//
+
+//
+// Begin of primitives for subfaces/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) ((unsigned long) (sptr) & (unsigned long) 7l);
+  s.sh = (shellface *) ((unsigned long) (sptr) & ~ (unsigned long) 7l);
+}
+
+inline tetgenmesh::shellface tetgenmesh::sencode(face& s) {
+  return (shellface) ((unsigned long) s.sh | (unsigned long) s.shver);
+}
+
+// spivot() finds the other subface (from this subface) that shares the
+//   same edge.
+
+inline void tetgenmesh::spivot(face& s1, face& s2) {
+  shellface sptr = s1.sh[Orient(s1.shver)];
+  sdecode(sptr, s2);
+}
+
+inline void tetgenmesh::spivotself(face& s) {
+  shellface sptr = s.sh[Orient(s.shver)];
+  sdecode(sptr, s);
+}
+
+// sbond() bonds two subfaces together, i.e., after bonding, both faces
+//   are pointing to each other.
+
+inline void tetgenmesh::sbond(face& s1, face& s2) {
+  s1.sh[Orient(s1.shver)] = sencode(s2);
+  s2.sh[Orient(s2.shver)] = sencode(s1);
+}
+
+// sbond1() only bonds s2 to s1, i.e., after bonding, s1 is pointing to s2,
+//   but s2 is not pointing to s1.
+
+inline void tetgenmesh::sbond1(face& s1, face& s2) {
+  s1.sh[Orient(s1.shver)] = 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[Orient(s.shver)] = (shellface) dummysh;
+}
+
+// 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[3 + vo[s.shver]];
+}
+
+inline tetgenmesh::point tetgenmesh::sdest(face& s) {
+  return (point) s.sh[3 + vd[s.shver]];
+}
+
+inline tetgenmesh::point tetgenmesh::sapex(face& s) {
+  return (point) s.sh[3 + va[s.shver]];
+}
+
+inline void tetgenmesh::setsorg(face& s, point pointptr) {
+  s.sh[3 + vo[s.shver]] = (shellface) pointptr;
+}
+
+inline void tetgenmesh::setsdest(face& s, point pointptr) {
+  s.sh[3 + vd[s.shver]] = (shellface) pointptr;
+}
+
+inline void tetgenmesh::setsapex(face& s, point pointptr) {
+  s.sh[3 + va[s.shver]] = (shellface) pointptr;
+}
+
+// These primitives were drived from Mucke[2]'s triangle-edge data structure
+//   to change face-edge relation in a subface (sesym, senext and senext2).
+
+inline void tetgenmesh::sesym(face& s1, face& s2) {
+  s2.sh = s1.sh;
+  s2.shver = s1.shver + (EdgeRing(s1.shver) ? -1 : 1);
+}
+
+inline void tetgenmesh::sesymself(face& s) {
+  s.shver += (EdgeRing(s.shver) ? -1 : 1);
+}
+
+inline void tetgenmesh::senext(face& s1, face& s2) {
+  s2.sh = s1.sh;
+  s2.shver = ve[s1.shver];
+}
+
+inline void tetgenmesh::senextself(face& s) { 
+  s.shver = ve[s.shver]; 
+}
+
+inline void tetgenmesh::senext2(face& s1, face& s2) {
+  s2.sh = s1.sh;
+  s2.shver = ve[ve[s1.shver]];
+}
+
+inline void tetgenmesh::senext2self(face& s) {
+  s.shver = ve[ve[s.shver]];
+}
+
+// If f0 and f1 are both in the same face ring, then f1 = f0.fnext(),
+
+inline void tetgenmesh::sfnext(face& s1, face& s2) {
+  getnextsface(&s1, &s2);
+}
+
+inline void tetgenmesh::sfnextself(face& s) {
+  getnextsface(&s, NULL);
+}
+
+// These primitives read or set a pointer of the badface structure.  The
+//   pointer is stored sh[11].
+
+inline tetgenmesh::badface* tetgenmesh::shell2badface(face& s) {
+  return (badface*) s.sh[11];
+}
+
+inline void tetgenmesh::setshell2badface(face& s, badface* value) {
+  s.sh[11] = (shellface) value;
+}
+
+// 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 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;
+}
+
+// These primitives set or read the type of the subface or subsegment.
+
+inline enum tetgenmesh::shestype tetgenmesh::shelltype(face& s) {
+  return (enum shestype) ((int *) (s.sh))[shmarkindex + 1];
+}
+
+inline void tetgenmesh::setshelltype(face& s, enum shestype value) {
+  ((int *) (s.sh))[shmarkindex + 1] = (int) value;
+} 
+
+// Primitives to infect or cure a subface with the virus. These rely on the
+//   assumption that all tetrahedra are aligned to eight-byte boundaries.
+
+inline void tetgenmesh::sinfect(face& s) {
+  s.sh[6] = (shellface) ((unsigned long) s.sh[6] | (unsigned long) 4l);
+}
+
+inline void tetgenmesh::suninfect(face& s) {
+  s.sh[6] = (shellface)((unsigned long) s.sh[6] & ~(unsigned long) 4l);
+}
+
+// Test a subface for viral infection.
+
+inline bool tetgenmesh::sinfected(face& s) {
+  return (((unsigned long) s.sh[6] & (unsigned long) 4l) != 0);
+}
+
+//
+// End of primitives for subfaces/subsegments
+//
+
+//
+// Begin of primitives for interacting between tetrahedra and subfaces
+//
+
+// tspivot() finds a subface abutting on this tetrahdera.
+
+inline void tetgenmesh::tspivot(triface& t, face& s) {
+  shellface sptr = (shellface) t.tet[8 + t.loc];
+  sdecode(sptr, s);
+}
+
+// stpivot() finds a tetrahedron abutting a subface.
+
+inline void tetgenmesh::stpivot(face& s, triface& t) {
+  tetrahedron ptr = (tetrahedron) s.sh[6 + EdgeRing(s.shver)];
+  decode(ptr, t);
+}
+
+// tsbond() bond a tetrahedron to a subface.
+
+inline void tetgenmesh::tsbond(triface& t, face& s) {
+  t.tet[8 + t.loc] = (tetrahedron) sencode(s);
+  s.sh[6 + EdgeRing(s.shver)] = (shellface) encode(t);
+}
+
+// tsdissolve() dissolve a bond (from the tetrahedron side).
+
+inline void tetgenmesh::tsdissolve(triface& t) {
+  t.tet[8 + t.loc] = (tetrahedron) dummysh;
+}
+
+// stdissolve() dissolve a bond (from the subface side).
+
+inline void tetgenmesh::stdissolve(face& s) {
+  s.sh[6 + EdgeRing(s.shver)] = (shellface) dummytet;
+}
+
+//
+// End of primitives for interacting between tetrahedra and subfaces
+//
+
+//
+// Begin of primitives for interacting between subfaces and subsegs
+//
+
+// sspivot() finds a subsegment abutting a subface.
+
+inline void tetgenmesh::sspivot(face& s, face& edge) {
+  shellface sptr = (shellface) s.sh[8 + Orient(s.shver)];
+  sdecode(sptr, edge);
+}
+
+// ssbond() bond a subface to a subsegment.
+
+inline void tetgenmesh::ssbond(face& s, face& edge) {
+  s.sh[8 + Orient(s.shver)] = sencode(edge);
+  edge.sh[0] = sencode(s);
+}
+
+// ssdisolve() dissolve a bond (from the subface side)
+
+inline void tetgenmesh::ssdissolve(face& s) {
+  s.sh[8 + Orient(s.shver)] = (shellface) dummysh;
+}
+
+//
+// End of primitives for interacting between subfaces and subsegs
+//
+
+//
+// Begin of 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];
+}
+
+inline void tetgenmesh::setpointtype(point pt, enum verttype value) {
+  ((int *) (pt))[pointmarkindex + 1] = (int) value;
+}
+
+// These two primitives set and read a pointer to a tetrahedron.
+
+inline tetgenmesh::tetrahedron tetgenmesh::point2tet(point pt) {
+  return ((tetrahedron *) (pt))[point2simindex];
+}
+
+inline void tetgenmesh::setpoint2tet(point pt, tetrahedron value) {
+  ((tetrahedron *) (pt))[point2simindex] = value;
+}
+
+// These two primitives set and read a pointer to a subface/subsegment.
+//   Note: they use the same field as the above. Don't use them together.
+
+inline tetgenmesh::shellface tetgenmesh::point2sh(point pt) {
+  return (shellface) ((tetrahedron *) (pt))[point2simindex];
+}
+
+inline void tetgenmesh::setpoint2sh(point pt, shellface value) {
+  ((tetrahedron *) (pt))[point2simindex] = (tetrahedron) value;
+}
+
+// These two primitives set and read a pointer to a point. 
+//   Note: they use the same field as the above. Don't use them together.
+
+inline tetgenmesh::point tetgenmesh::point2pt(point pt) {
+  return (point) ((tetrahedron *) (pt))[point2simindex];
+}
+
+inline void tetgenmesh::setpoint2pt(point pt, point value) {
+  ((tetrahedron *) (pt))[point2simindex] = (tetrahedron) value;
+}
+
+// These primitives set and read a pointer to its parent point. They're used
+//   only in qulaity conforming Delaunay mesh algorithm.
+
+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;
+}
+
+// Get the pre-calculated lifting point of a facet (specified by its mark).  
+
+inline tetgenmesh::point tetgenmesh::getliftpoint(int facetmark) {
+  return (point) &liftpointarray[(facetmark - 1) * 3];
+}
+
+//
+// End of primitives for points
+//
+
+//
+// Begin of advanced primitives
+//
+
+// adjustedgering() adjusts the edge version so that it belongs to the
+//   indicated edge ring.  The 'direction' only can be 0(CCW) or 1(CW).
+//   If the edge is not in the wanted edge ring, reverse it.
+
+inline void tetgenmesh::adjustedgering(triface& t, int direction) {
+  if (EdgeRing(t.ver) != direction) {
+    esymself(t);
+  }
+}
+
+inline void tetgenmesh::adjustedgering(face& s, int direction) {
+  if (EdgeRing(s.shver) != direction) {
+    sesymself(s);
+  }
+}
+
+// isdead() returns TRUE if the tetrahedron or subface has been dealloced.
+
+inline bool tetgenmesh::isdead(triface* t) {
+  if (t->tet == (tetrahedron *) NULL) return true;
+  else return t->tet[4] == (tetrahedron) NULL;
+}
+
+inline bool tetgenmesh::isdead(face* s) {
+  if (s->sh == (shellface *) NULL) return true;
+  else return s->sh[3] == (shellface) NULL;
+}
+
+// isfacehaspoint() returns TRUE if the 'testpoint' is one of the vertices
+//   of the subface 's'.
+
+inline bool tetgenmesh::isfacehaspoint(face* s, point testpoint) {
+  return (s->sh[3] == (shellface) testpoint) || 
+         (s->sh[4] == (shellface) testpoint) ||
+         (s->sh[5] == (shellface) testpoint);
+}
+
+// isfacehasedge() returns TRUE if the edge (given by its two endpoints) is
+//   one of the three edges of the subface 's'.
+
+inline bool tetgenmesh::isfacehasedge(face* s, point tend1, point tend2) {
+  return (isfacehaspoint(s, tend1) && isfacehaspoint(s, tend2));
+}
+
+// issymexist() returns TRUE if the adjoining tetrahedron is not 'duumytet'.
+
+inline bool tetgenmesh::issymexist(triface* t) {
+  tetrahedron *ptr = (tetrahedron *) 
+    ((unsigned long)(t->tet[t->loc]) & ~(unsigned long)7l);
+  return ptr != dummytet;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// getnextface()    Get the successor of 'tface1' in the face ring.          //
+//                                                                           //
+// If 'tface1' is not a boundary (or hull) face, then its successor in the   //
+// face ring exists. The successor is returned in 'tface2' if it is not a    //
+// NULL, or the 'tface1' itself is used to return this face. On finish, the  //
+// function returns TRUE.                                                    //
+//                                                                           //
+// If 'tface1' is a boundary (or hull) face, its successor does not exist.   //
+// This case, return FALSE and 'tface1' remains unchanged.                   //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::getnextface(triface* tface1, triface* tface2)
+{
+  point torg, tdest;
+  int tloc, tver;
+
+  // Where the next face locates, in 'tface1' or in its neigbhour? It can be
+  //   quickly determined by checking the edge ring of 'tface1'.
+  if (EdgeRing(tface1->ver) == CW) {
+    // The next face is in the neigbhour of 'tface1'.
+    if (!issymexist(tface1)) {
+      // Hit outer space - The next face does not exist.
+      return false;
+    }
+    torg = org(*tface1);
+    tdest = dest(*tface1);
+    if (tface2) {
+      sym(*tface1, *tface2);
+      findedge(tface2, torg, tdest);
+    } else {
+      symself(*tface1);
+      findedge(tface1, torg, tdest);
+    }
+  } else {
+    // The next face is in 'tface1'.
+    if (tface2) {
+      *tface2 = *tface1;
+    }
+  }
+
+  if (tface2) {
+    tloc = tface2->loc;
+    tver = tface2->ver;
+    tface2->loc = locver2nextf[tloc][tver][0];
+    tface2->ver = locver2nextf[tloc][tver][1];
+  } else {
+    tloc = tface1->loc;
+    tver = tface1->ver;
+    tface1->loc = locver2nextf[tloc][tver][0];
+    tface1->ver = locver2nextf[tloc][tver][1];
+  }
+  return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// getnextsface()    Finds the next subface in the face ring.                //
+//                                                                           //
+// For saving space in the data structure of subface, there only exists one  //
+// face ring around a segment (see programming manual).  This routine imple- //
+// ments the double face ring as desired in Muecke's data structure.         //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::getnextsface(face* s1, face* s2)
+{
+  face neighsh, spinsh;
+  face testseg;
+
+  sspivot(*s1, testseg);
+  if (testseg.sh != dummysh) {
+    testseg.shver = 0;
+    if (sorg(testseg) == sorg(*s1)) {
+      spivot(*s1, neighsh);
+    } else {
+      spinsh = *s1;
+      do {
+        neighsh = spinsh;
+        spivotself(spinsh);
+      } while (spinsh.sh != s1->sh);
+    }
+  } else {
+    spivot(*s1, neighsh);
+  }
+  if (sorg(neighsh) != sorg(*s1)) {
+    sesymself(neighsh);
+  }
+  if (s2 != (face *) NULL) {
+    *s2 = neighsh;
+  } else {
+    *s1 = neighsh;
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// tsspivot()    Finds a subsegment abutting on a tetrahderon's edge.        //
+//                                                                           //
+// The edge is represented in the primary edge of 'checkedge'. If there is a //
+// subsegment bonded at this edge, it is returned in handle 'checkseg', the  //
+// edge direction of 'checkseg' is conformed to 'checkedge'. If there isn't, //
+// set 'checkseg.sh = dummysh' to indicate it is not a subsegment.           //
+//                                                                           //
+// To find whether an edge of a tetrahedron is a subsegment or not. First we //
+// need find a subface around this edge to see if it contains a subsegment.  //
+// The reason is there is no direct connection between a tetrahedron and its //
+// adjoining subsegments.                                                    //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::tsspivot(triface* checkedge, face* checkseg)
+{
+  triface spintet;
+  face parentsh;
+  point tapex;
+  int hitbdry;
+
+  spintet = *checkedge;
+  tapex = apex(*checkedge);
+  hitbdry = 0;
+  do {
+    tspivot(spintet, parentsh);
+    if (parentsh.sh != dummysh) {
+      // Find a subface!
+      findedge(&parentsh, org(*checkedge), dest(*checkedge));
+      sspivot(parentsh, *checkseg);
+      if (checkseg->sh != dummysh) {
+        // Find a subsegment! Correct its edge direction before return.
+        if (sorg(*checkseg) != sorg(parentsh)) {
+          sesymself(*checkseg);
+        }
+      }
+      return;
+    }
+    if (!fnextself(spintet)) {
+      hitbdry++;
+      if (hitbdry < 2) {
+        esym(*checkedge, spintet);
+        if (!fnextself(spintet)) {
+          hitbdry++;
+        }
+      }
+    }
+  } while ((apex(spintet) != tapex) && (hitbdry < 2));
+  // Not find.
+  checkseg->sh = dummysh;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// sstpivot()    Finds a tetrahedron abutting a subsegment.                  //
+//                                                                           //
+// This is the inverse operation of 'tsspivot()'.  One subsegment shared by  //
+// arbitrary number of tetrahedron, the returned tetrahedron is not unique.  //
+// The edge direction of the returned tetrahedron is conformed to the given  //
+// subsegment.                                                               //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::sstpivot(face* checkseg, triface* retedge)
+{
+  face parentsh;
+
+  // Get the subface which holds the subsegment.
+  sdecode(checkseg->sh[0], parentsh);
+  assert(parentsh.sh != dummysh);
+  // Get a tetraheron to which the subface attches.
+  stpivot(parentsh, *retedge);
+  if (retedge->tet == dummytet) {
+    sesymself(parentsh);
+    stpivot(parentsh, *retedge);
+    assert(retedge->tet != dummytet);
+  }
+  // Correct the edge direction before return.
+  findedge(retedge, sorg(*checkseg), sdest(*checkseg));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// findorg()    Finds a point in the given handle (tetrahedron or subface).  //
+//                                                                           //
+// If 'dorg' is a one of vertices of the given handle,  set the origin of    //
+// this handle be that point and return TRUE.  Otherwise, return FALSE and   //
+// 'tface' remains unchanged.                                                //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::findorg(triface* tface, point dorg)
+{
+  if (org(*tface) == dorg) {
+    return true;
+  } else { 
+    if (dest(*tface) == dorg) {
+      enextself(*tface);
+      return true;
+    } else {
+      if (apex(*tface) == dorg) {
+        enext2self(*tface);
+        return true;
+      } else {
+        if (oppo(*tface) == dorg) {
+          // Keep 'tface' referring to the same tet after fnext().
+          adjustedgering(*tface, CCW);
+          fnextself(*tface);
+          enext2self(*tface);
+          return true;
+        } 
+      }
+    }
+  }
+  return false;
+}
+
+bool tetgenmesh::findorg(face* sface, point dorg)
+{
+  if (sorg(*sface) == dorg) {
+    return true;
+  } else {
+    if (sdest(*sface) == dorg) {
+      senextself(*sface);
+      return true;
+    } else {
+      if (sapex(*sface) == dorg) {
+        senext2self(*sface);
+        return true;
+      } 
+    }
+  }
+  return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// findedge()    Find an edge in the given handle (tetrahedron or subface).  //
+//                                                                           //
+// The edge is given in two points 'eorg' and 'edest'.  It is assumed that   //
+// the edge must exist in the given handle (tetrahedron or subface).  This   //
+// routine sets the right edge version for the input handle.                 //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::findedge(triface* tface, point eorg, point edest)
+{
+  int i;
+
+  for (i = 0; i < 3; i++) {
+    if (org(*tface) == eorg) {
+      if (dest(*tface) == edest) {
+        // Edge is found, return.
+        return;
+      } 
+    } else {
+      if (org(*tface) == edest) {
+        if (dest(*tface) == eorg) {
+          // Edge is found, but need to inverse the direction.
+          esymself(*tface);
+          return;
+        }
+      }
+    }
+    enextself(*tface);
+  }
+  // It should not be here.
+  assert(i < 3);
+}
+
+void tetgenmesh::findedge(face* sface, point eorg, point edest)
+{
+  int i;
+
+  for (i = 0; i < 3; i++) {
+    if (sorg(*sface) == eorg) {
+      if (sdest(*sface) == edest) {
+        // Edge is found, return.
+        return;
+      } 
+    } else {
+      if (sorg(*sface) == edest) {
+        if (sdest(*sface) == eorg) {
+          // Edge is found, but need to inverse the direction.
+          sesymself(*sface);
+          return;
+        }
+      }
+    }
+    senextself(*sface);
+  }
+  // It should not be here.
+  assert(i < 3);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// findface()    Find the face has the given origin, destination and apex.   //
+//                                                                           //
+// On input, 'fface' is a handle which may contain the three corners or may  //
+// not or may be dead.  On return, it represents exactly the face with the   //
+// given origin, destination and apex.                                       //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::findface(triface *fface, point forg, point fdest, point fapex)
+{
+  triface spintet;
+  enum finddirectionresult collinear;
+  int hitbdry;
+
+  if (!isdead(fface)) {
+    // First check the easiest case, that 'fface' is just the right one.
+    if (org(*fface) == forg && dest(*fface) == fdest && 
+        apex(*fface) == fapex) return;
+  } else {
+    // The input handle is dead, use the 'recenttet' if it is alive.
+    if (!isdead(&recenttet)) *fface = recenttet;
+  }
+
+  if (!isdead(fface)) {
+    if (!findorg(fface, forg)) {
+      // 'forg' is not a corner of 'fface', locate it.
+      preciselocate(forg, fface);
+    }
+    // It is possible that forg is not found in a non-convex mesh.
+    if (org(*fface) == forg) {
+      collinear = finddirection(fface, fdest);
+      if (collinear == RIGHTCOLLINEAR) {
+        // fdest is just the destination.
+      } else if (collinear == LEFTCOLLINEAR) {
+        enext2self(*fface);
+        esymself(*fface);
+      } else if (collinear == TOPCOLLINEAR) {
+        fnextself(*fface);
+        enext2self(*fface);
+        esymself(*fface);
+      }
+    }
+    // It is possible taht fdest is not found in a non-convex mesh.
+    if ((org(*fface) == forg) && (dest(*fface) == fdest)) {
+      // Find the apex of 'fapex'.
+      spintet = *fface;
+      hitbdry = 0;
+      do {
+        if (apex(spintet) == fapex) {
+          // We have done. Be careful the edge direction of 'spintet',
+          //   it may reversed because of hitting boundary once.
+          if (org(spintet) != org(*fface)) {
+            esymself(spintet);
+          }
+          *fface = spintet;
+          return;
+        }
+        if (!fnextself(spintet)) {
+          hitbdry ++;
+          if (hitbdry < 2) {
+            esym(*fface, spintet);
+            if (!fnextself(spintet)) {
+              hitbdry ++;
+            }
+          }
+        }
+      } while (hitbdry < 2 && apex(spintet) != apex(*fface));
+      // It is possible that fapex is not found in a non-convex mesh.
+    }
+  }
+
+  if (isdead(fface) || (org(*fface) != forg) || (dest(*fface) != fdest) ||
+      (apex(*fface) != fapex)) {
+    // Too bad, the input handle is useless. We have to find a handle
+    //   for 'fface' contains the 'forg' and 'fdest'. Here a brute force
+    //   search is performed.
+    if (b->verbose > 1) {
+      printf("Warning in findface():  Perform a brute-force searching.\n");
+    }
+    enum verttype forgty, fdestty, fapexty;
+    int share, i;
+    forgty = pointtype(forg);
+    fdestty = pointtype(fdest);
+    fapexty = pointtype(fapex);
+    setpointtype(forg, DEADVERTEX);
+    setpointtype(fdest, DEADVERTEX);
+    setpointtype(fapex, DEADVERTEX);
+    tetrahedrons->traversalinit();
+    fface->tet = tetrahedrontraverse();
+    while (fface->tet != (tetrahedron *) NULL) {
+      share = 0;
+      for (i = 0; i < 4; i++) {
+        if (pointtype((point) fface->tet[4 + i]) == DEADVERTEX) share ++;
+      }
+      if (share == 3) {
+        // Found! Set the correct face and desired corners.
+        if (pointtype((point) fface->tet[4]) != DEADVERTEX) {
+          fface->loc = 2;
+	} else if (pointtype((point) fface->tet[5]) != DEADVERTEX) {
+          fface->loc = 3;
+        } else if (pointtype((point) fface->tet[6]) != DEADVERTEX) {
+          fface->loc = 1;
+        } else { // pointtype((point) fface->tet[7]) != DEADVERTEX
+          fface->loc = 0;
+        }
+        findedge(fface, forg, fdest);
+        break;
+      }
+      fface->tet = tetrahedrontraverse();
+    }
+    setpointtype(forg, forgty);
+    setpointtype(fdest, fdestty);
+    setpointtype(fapex, fapexty);
+    if (fface->tet == (tetrahedron *) NULL) {
+      // It is impossible to reach here.
+      printf("Internal error:  Fail to find the indicated face.\n");
+      internalerror();
+    }
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// getonextseg()    Get the next SEGMENT counterclockwise with the same org. //
+//                                                                           //
+// 's' is a subface. This routine reteuns the segment which is counterclock- //
+// wise with the origin of s.                                                //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::getonextseg(face* s, face* lseg)
+{
+  face checksh, checkseg;
+  point forg;
+
+  forg = sorg(*s);
+  checksh = *s;
+  do {
+    // Go to the edge at forg's left side.
+    senext2self(checksh);
+    // Check if there is a segment attaching this edge.
+    sspivot(checksh, checkseg);
+    if (checkseg.sh != dummysh) break;
+    // No segment! Go to the neighbor of this subface.
+    spivotself(checksh);
+    // It should always meet a segment before come back.
+    assert(checksh.sh != s->sh);
+    if (sorg(checksh) != forg) {
+      sesymself(checksh);
+      assert(sorg(checksh) == forg);
+    }
+  } while (true);
+  assert(checkseg.sh != dummysh);
+  if (sorg(checkseg) != forg) sesymself(checkseg);
+  *lseg = checkseg;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// getseghasorg()    Get the segment containing the given point.             //
+//                                                                           //
+// On input we know 'dorg' is an endpoint of the segment containing 'sseg'.  //
+// This routine search along 'sseg' for the vertex 'dorg'. On return, 'sseg' //
+// contains 'dorg' as its origin.                                            //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::getseghasorg(face* sseg, point dorg)
+{
+  face nextseg;
+  point checkpt;
+
+  nextseg = *sseg;
+  checkpt = sorg(nextseg);
+  while ((checkpt != dorg) && (pointtype(checkpt) == FREESEGVERTEX)) {
+    // Search dorg along the original direction of sseg.
+    senext2self(nextseg);
+    spivotself(nextseg);
+    nextseg.shver = 0;
+    if (sdest(nextseg) != checkpt) sesymself(nextseg);
+    checkpt = sorg(nextseg);
+  }
+  if (checkpt == dorg) {
+    *sseg = nextseg;
+    return;
+  }
+  nextseg = *sseg;
+  checkpt = sdest(nextseg);
+  while ((checkpt != dorg) && (pointtype(checkpt) == FREESEGVERTEX)) {
+    // Search dorg along the destinational direction of sseg.
+    senextself(nextseg);
+    spivotself(nextseg);
+    nextseg.shver = 0;
+    if (sorg(nextseg) != checkpt) sesymself(nextseg);
+    checkpt = sdest(nextseg);
+  }
+  if (checkpt == dorg) {
+    sesym(nextseg, *sseg);
+    return;
+  }
+  // Should not be here.
+  assert(0);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// getsubsegfarorg()    Get the origin of the parent segment of a subseg.    //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+tetgenmesh::point tetgenmesh::getsubsegfarorg(face* sseg)
+{
+  face prevseg;
+  point checkpt;
+
+  checkpt = sorg(*sseg);
+  senext2(*sseg, prevseg);
+  spivotself(prevseg);
+  // Search dorg along the original direction of sseg.
+  while (prevseg.sh != dummysh) {
+    prevseg.shver = 0;
+    if (sdest(prevseg) != checkpt) sesymself(prevseg);
+    checkpt = sorg(prevseg);
+    senext2self(prevseg);
+    spivotself(prevseg);
+  }
+  return checkpt;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// getsubsegfardest()    Get the dest. of the parent segment of a subseg.    //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+tetgenmesh::point tetgenmesh::getsubsegfardest(face* sseg)
+{
+  face nextseg;
+  point checkpt;
+
+  checkpt = sdest(*sseg);
+  senext(*sseg, nextseg);
+  spivotself(nextseg);
+  // Search dorg along the destinational direction of sseg.
+  while (nextseg.sh != dummysh) {
+    nextseg.shver = 0;
+    if (sorg(nextseg) != checkpt) sesymself(nextseg);
+    checkpt = sdest(nextseg);
+    senextself(nextseg);
+    spivotself(nextseg);
+  }
+  return checkpt;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// printtet()    Print out the details of a tetrahedron on screen.           //
+//                                                                           //
+// It's also used when the highest level of verbosity (`-VVV') is specified. //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::printtet(triface* tface)
+{
+  triface tmpface, prtface;
+  point tmppt;
+  face tmpsh;
+  int facecount;
+
+  printf("Tetra x%lx with loc(%i) and ver(%i):",
+         (unsigned long)(tface->tet), tface->loc, tface->ver);
+  if (infected(*tface)) {
+    printf(" (infected)");
+  }
+  printf("\n");
+
+  tmpface = *tface;
+  facecount = 0;
+  while(facecount < 4) {
+    tmpface.loc = facecount;
+    sym(tmpface, prtface);
+    if(prtface.tet == dummytet) {
+      printf("      [%i] Outer space.\n", facecount);
+    } else {
+      printf("      [%i] x%lx  loc(%i).", facecount,
+             (unsigned long)(prtface.tet), prtface.loc);
+      if (infected(prtface)) {
+        printf(" (infected)");
+      }
+      printf("\n");
+    }
+    facecount ++;
+  }
+
+  tmppt = org(*tface);
+  if(tmppt == (point) NULL) {
+    printf("      Org [%i] NULL\n", locver2org[tface->loc][tface->ver]);
+  } else {
+    printf("      Org [%i] x%lx (%.12g,%.12g,%.12g) %d\n",
+           locver2org[tface->loc][tface->ver], (unsigned long)(tmppt),
+           tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt));
+  }
+  tmppt = dest(*tface);
+  if(tmppt == (point) NULL) {
+    printf("      Dest[%i] NULL\n", locver2dest[tface->loc][tface->ver]);
+  } else {
+    printf("      Dest[%i] x%lx (%.12g,%.12g,%.12g) %d\n",
+           locver2dest[tface->loc][tface->ver], (unsigned long)(tmppt),
+           tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt));
+  }
+  tmppt = apex(*tface);
+  if(tmppt == (point) NULL) {
+    printf("      Apex[%i] NULL\n", locver2apex[tface->loc][tface->ver]);
+  } else {
+    printf("      Apex[%i] x%lx (%.12g,%.12g,%.12g) %d\n",
+           locver2apex[tface->loc][tface->ver], (unsigned long)(tmppt),
+           tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt));
+  }
+  tmppt = oppo(*tface);
+  if(tmppt == (point) NULL) {
+    printf("      Oppo[%i] NULL\n", loc2oppo[tface->loc]);
+  } else {
+    printf("      Oppo[%i] x%lx (%.12g,%.12g,%.12g) %d\n",
+           loc2oppo[tface->loc], (unsigned long)(tmppt),
+           tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt));
+  }
+
+  if (b->useshelles) {
+    tmpface = *tface;
+    facecount = 0;
+    while(facecount < 4) {
+      tmpface.loc = facecount;
+      tspivot(tmpface, tmpsh);
+      if(tmpsh.sh != dummysh) {
+        printf("      [%i] x%lx  ID(%i).\n", facecount,
+               (unsigned long)(tmpsh.sh), shellmark(tmpsh));
+      }
+      facecount ++;
+    }
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// printsh()    Print out the details of a subface or subsegment on screen.  //
+//                                                                           //
+// It's also used when the highest level of verbosity (`-VVV') is specified. //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::printsh(face* sface)
+{
+  face prtsh;
+  triface prttet;
+  point printpoint;
+
+  if (sapex(*sface) != NULL) {
+    printf("subface x%lx, ver %d, mark %d:",
+           (unsigned long)(sface->sh), sface->shver, shellmark(*sface));
+  } else {
+    printf("Subsegment x%lx, ver %d, mark %d:",
+           (unsigned long)(sface->sh), sface->shver, shellmark(*sface));
+  }
+  if (sinfected(*sface)) {
+    printf(" (infected)");
+  }
+  if (shell2badface(*sface)) {
+    printf(" (queued)");
+  }
+  if (sapex(*sface) != NULL) {
+    if (shelltype(*sface) == PROTCYLSUBFACE) {
+      printf(" (cyls)");
+    } else if (shelltype(*sface) == PROTSPHSUBFACE) {
+      printf(" (sphs)");
+    }
+  } else {
+    if (shelltype(*sface) == SHARPSEGMENT) {
+      printf(" (sharp)");
+    } else if (shelltype(*sface) == PROTCYLSEGMENT) {
+      printf(" (cyls)");
+    } else if (shelltype(*sface) == PROTSPHSEGMENT) {
+      printf(" (sphs)");
+    }
+  }
+  printf("\n");
+
+  sdecode(sface->sh[0], prtsh);
+  if (prtsh.sh == dummysh) {
+    printf("      [0] = No shell\n");
+  } else {
+    printf("      [0] = x%lx  %d\n", (unsigned long)(prtsh.sh), prtsh.shver);
+  }
+  sdecode(sface->sh[1], prtsh);
+  if (prtsh.sh == dummysh) {
+    printf("      [1] = No shell\n");
+  } else {
+    printf("      [1] = x%lx  %d\n", (unsigned long)(prtsh.sh), prtsh.shver);
+  }
+  sdecode(sface->sh[2], prtsh);
+  if (prtsh.sh == dummysh) {
+    printf("      [2] = No shell\n");
+  } else {
+    printf("      [2] = x%lx  %d\n", (unsigned long)(prtsh.sh), prtsh.shver);
+  }
+
+  printpoint = sorg(*sface);
+  if (printpoint == (point) NULL)
+    printf("      Org [%d] = NULL\n", vo[sface->shver]);
+  else
+    printf("      Org [%d] = x%lx  (%.12g,%.12g,%.12g) %d\n",
+           vo[sface->shver], (unsigned long)(printpoint), printpoint[0],
+           printpoint[1], printpoint[2], pointmark(printpoint));
+  printpoint = sdest(*sface);
+  if (printpoint == (point) NULL)
+    printf("      Dest[%d] = NULL\n", vd[sface->shver]);
+  else
+    printf("      Dest[%d] = x%lx  (%.12g,%.12g,%.12g) %d\n",
+            vd[sface->shver], (unsigned long)(printpoint), printpoint[0],
+            printpoint[1], printpoint[2], pointmark(printpoint));
+
+  if (sapex(*sface) != NULL) {
+    printpoint = sapex(*sface);
+    if (printpoint == (point) NULL)
+      printf("      Apex[%d] = NULL\n", va[sface->shver]);
+    else
+      printf("      Apex[%d] = x%lx  (%.12g,%.12g,%.12g) %d\n",
+             va[sface->shver], (unsigned long)(printpoint), printpoint[0],
+             printpoint[1], printpoint[2], pointmark(printpoint));
+
+    decode(sface->sh[6], prttet);
+    if (prttet.tet == dummytet) {
+      printf("      [6] = Outer space\n");
+    } else {
+      printf("      [6] = x%lx  %d\n",
+             (unsigned long)(prttet.tet), prttet.loc);
+    }
+    decode(sface->sh[7], prttet);
+    if (prttet.tet == dummytet) {
+      printf("      [7] = Outer space\n");
+    } else {
+      printf("      [7] = x%lx  %d\n",
+             (unsigned long)(prttet.tet), prttet.loc);
+    }
+
+    sdecode(sface->sh[8], prtsh);
+    if (prtsh.sh == dummysh) {
+      printf("      [8] = No subsegment\n");
+    } else {
+      printf("      [8] = x%lx  %d\n",
+             (unsigned long)(prtsh.sh), prtsh.shver);
+    }
+    sdecode(sface->sh[9], prtsh);
+    if (prtsh.sh == dummysh) {
+      printf("      [9] = No subsegment\n");
+    } else {
+      printf("      [9] = x%lx  %d\n",
+             (unsigned long)(prtsh.sh), prtsh.shver);
+    }
+    sdecode(sface->sh[10], prtsh);
+    if (prtsh.sh == dummysh) {
+      printf("      [10]= No subsegment\n");
+    } else {
+      printf("      [10]= x%lx  %d\n",
+             (unsigned long)(prtsh.sh), prtsh.shver);
+    }
+  } 
+}
+
+//
+// End of advanced primitives
+//
+
+//
+// End of mesh manipulation primitives
+//
+
+//
+// Begin of mesh items searching routines
+//
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// makepoint2tetmap()    Construct a mapping from points to tetrahedra.      //
+//                                                                           //
+// Traverses all the tetrahedra,  provides each corner of each tetrahedron   //
+// with a pointer to that tetrahedera.  Some pointers will be overwritten by //
+// other pointers because each point may be a corner of several tetrahedra,  //
+// but in the end every point will point to a tetrahedron that contains it.  //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::makepoint2tetmap()
+{
+  triface tetloop;
+  point pointptr;
+
+  if (b->verbose) {
+    printf("  Constructing mapping from points to tetrahedra.\n");
+  }
+
+  tetrahedrons->traversalinit();
+  tetloop.tet = tetrahedrontraverse();
+  while (tetloop.tet != (tetrahedron *) NULL) {
+    // Check all four points of the tetrahedron.
+    pointptr = org(tetloop);
+    setpoint2tet(pointptr, encode(tetloop));
+    pointptr = dest(tetloop);
+    setpoint2tet(pointptr, encode(tetloop));
+    pointptr = apex(tetloop);
+    setpoint2tet(pointptr, encode(tetloop));
+    pointptr = oppo(tetloop);
+    setpoint2tet(pointptr, encode(tetloop));
+    // Get the next tetrahedron in the list.
+    tetloop.tet = tetrahedrontraverse();
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// 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[0]'.  Don't forget to minus 'in->firstnumber' when  //
+// to get the vertex form its index.                                         //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::makeindex2pointmap(point*& idx2verlist)
+{
+  point pointloop;
+  int idx;
+
+  if (b->verbose) {
+    printf("  Constructing mapping from indices to points.\n");
+  }
+
+  idx2verlist = new point[points->items];
+
+  points->traversalinit();
+  pointloop = pointtraverse();
+  idx = 0;
+  while (pointloop != (point) NULL) {
+    idx2verlist[idx] = pointloop;
+    idx++;
+    pointloop = pointtraverse();
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// makesegmentmap()    Create a map from vertices (their indices) to         //
+//                     segments incident at the same vertices.               //
+//                                                                           //
+// Two arrays 'idx2seglist' and 'segsperverlist' together return the map.    //
+// They form a sparse matrix structure with size (n + 1) x (n + 1), n is the //
+// number of segments.  idx2seglist contains row information and             //
+// segsperverlist contains all (non-zero) elements.  The i-th entry of       //
+// idx2seglist is the starting position of i-th row's (non-zero) elements in //
+// segsperverlist.  The number of elements of i-th row is calculated by the  //
+// (i+1)-th entry minus i-th entry of idx2seglist.                           //
+//                                                                           //
+// NOTE: These two arrays will be created inside this routine, don't forget  //
+// to free them after using.                                                 //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::
+makesegmentmap(int*& idx2seglist, shellface**& segsperverlist)
+{
+  shellface *shloop;
+  int i, j, k;
+
+  if (b->verbose) {
+    printf("  Constructing mapping from points to segments.\n");
+  }
+
+  // Create and initialize 'idx2seglist'.
+  idx2seglist = new int[points->items + 1];
+  for (i = 0; i < points->items + 1; i++) {
+    idx2seglist[i] = 0;
+  }
+
+  // Loop the set of segments once, counter the number of segments sharing
+  //   each vertex.
+  subsegs->traversalinit();
+  shloop = shellfacetraverse(subsegs);
+  while (shloop != (shellface *) NULL) {
+    // Increment the number of sharing segments for each endpoint.
+    for (i = 0; i < 2; i++) {
+      j = pointmark((point) shloop[3 + i]) - in->firstnumber;
+      idx2seglist[j]++;
+    }
+    shloop = shellfacetraverse(subsegs);
+  }
+
+  // Calculate the total length of array 'facesperverlist'.
+  j = idx2seglist[0];
+  idx2seglist[0] = 0;  // Array starts from 0 element.
+  for (i = 0; i < points->items; i++) {
+    k = idx2seglist[i + 1];
+    idx2seglist[i + 1] = idx2seglist[i] + j;
+    j = k;
+  }
+  // The total length is in the last unit of idx2seglist.
+  segsperverlist = new shellface*[idx2seglist[i]];
+  // Loop the set of segments again, set the info. of segments per vertex.
+  subsegs->traversalinit();
+  shloop = shellfacetraverse(subsegs);
+  while (shloop != (shellface *) NULL) {
+    for (i = 0; i < 2; i++) {
+      j = pointmark((point) shloop[3 + i]) - in->firstnumber;
+      segsperverlist[idx2seglist[j]] = shloop;
+      idx2seglist[j]++;
+    }
+    shloop = shellfacetraverse(subsegs);
+  }
+  // Contents in 'idx2seglist' are shifted, now shift them back.
+  for (i = points->items - 1; i >= 0; i--) {
+    idx2seglist[i + 1] = idx2seglist[i];
+  }
+  idx2seglist[0] = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// makesubfacemap()    Create a map from vertices (their indices) to         //
+//                     subfaces incident at the same vertices.               //
+//                                                                           //
+// Two arrays 'idx2facelist' and 'facesperverlist' together return the map.  //
+// They form a sparse matrix structure with size (n + 1) x (n + 1), n is the //
+// number of subfaces.  idx2facelist contains row information and            //
+// facesperverlist contains all (non-zero) elements.  The i-th entry of      //
+// idx2facelist is the starting position of i-th row's(non-zero) elements in //
+// facesperverlist.  The number of elements of i-th row is calculated by the //
+// (i+1)-th entry minus i-th entry of idx2facelist.                          //
+//                                                                           //
+// NOTE: These two arrays will be created inside this routine, don't forget  //
+// to free them after using.                                                 //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::
+makesubfacemap(int*& idx2facelist, shellface**& facesperverlist)
+{
+  shellface *shloop;
+  int i, j, k;
+
+  if (b->verbose) {
+    printf("  Constructing mapping from points to subfaces.\n");
+  }
+
+  // Create and initialize 'idx2facelist'.
+  idx2facelist = new int[points->items + 1];
+  for (i = 0; i < points->items + 1; i++) {
+    idx2facelist[i] = 0;
+  }
+
+  // Loop the set of subfaces once, counter the number of subfaces sharing
+  //   each vertex.
+  subfaces->traversalinit();
+  shloop = shellfacetraverse(subfaces);
+  while (shloop != (shellface *) NULL) {
+    // Increment the number of sharing segments for each endpoint.
+    for (i = 0; i < 3; i++) {
+      j = pointmark((point) shloop[3 + i]) - in->firstnumber;
+      idx2facelist[j]++;
+    }
+    shloop = shellfacetraverse(subfaces);
+  }
+
+  // Calculate the total length of array 'facesperverlist'.
+  j = idx2facelist[0];
+  idx2facelist[0] = 0;  // Array starts from 0 element.
+  for (i = 0; i < points->items; i++) {
+    k = idx2facelist[i + 1];
+    idx2facelist[i + 1] = idx2facelist[i] + j;
+    j = k;
+  }
+  // The total length is in the last unit of idx2facelist.
+  facesperverlist = new shellface*[idx2facelist[i]];
+  // Loop the set of segments again, set the info. of segments per vertex.
+  subfaces->traversalinit();
+  shloop = shellfacetraverse(subfaces);
+  while (shloop != (shellface *) NULL) {
+    for (i = 0; i < 3; i++) {
+      j = pointmark((point) shloop[3 + i]) - in->firstnumber;
+      facesperverlist[idx2facelist[j]] = shloop;
+      idx2facelist[j]++;
+    }
+    shloop = shellfacetraverse(subfaces);
+  }
+  // Contents in 'idx2facelist' are shifted, now shift them back.
+  for (i = points->items - 1; i >= 0; i--) {
+    idx2facelist[i + 1] = idx2facelist[i];
+  }
+  idx2facelist[0] = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// maketetrahedronmap()    Create a map from vertices (their indices) to     //
+//                         tetrahedra incident at the same vertices.         //
+//                                                                           //
+// Two arrays 'idx2tetlist' and 'tetsperverlist' together return the map.    //
+// They form a sparse matrix structure with size (n + 1) x (n + 1), n is the //
+// number of tetrahedra.  idx2tetlist contains row information and           //
+// tetsperverlist contains all (non-zero) elements.  The i-th entry of       //
+// idx2tetlist is the starting position of i-th row's (non-zero) elements in //
+// tetsperverlist.  The number of elements of i-th row is calculated by the  //
+// (i+1)-th entry minus i-th entry of idx2tetlist.                           //
+//                                                                           //
+// NOTE: These two arrays will be created inside this routine, don't forget  //
+// to free them after using.                                                 //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::
+maketetrahedronmap(int*& idx2tetlist, tetrahedron**& tetsperverlist)
+{
+  tetrahedron *tetloop;
+  int i, j, k;
+
+  if (b->verbose) {
+    printf("  Constructing mapping from points to tetrahedra.\n");
+  }
+
+  // Create and initialize 'idx2tetlist'.
+  idx2tetlist = new int[points->items + 1];
+  for (i = 0; i < points->items + 1; i++) {
+    idx2tetlist[i] = 0;
+  }
+
+  // Loop the set of tetrahedra once, counter the number of tetrahedra
+  //   sharing each vertex.
+  tetrahedrons->traversalinit();
+  tetloop = tetrahedrontraverse();
+  while (tetloop != (tetrahedron *) NULL) {
+    // Increment the number of sharing tetrahedra for each endpoint.
+    for (i = 0; i < 4; i++) {
+      j = pointmark((point) tetloop[4 + i]) - in->firstnumber;
+      idx2tetlist[j]++;
+    }
+    tetloop = tetrahedrontraverse();
+  }
+
+  // Calculate the total length of array 'tetsperverlist'.
+  j = idx2tetlist[0];
+  idx2tetlist[0] = 0;  // Array starts from 0 element.
+  for (i = 0; i < points->items; i++) {
+    k = idx2tetlist[i + 1];
+    idx2tetlist[i + 1] = idx2tetlist[i] + j;
+    j = k;
+  }
+  // The total length is in the last unit of idx2tetlist.
+  tetsperverlist = new tetrahedron*[idx2tetlist[i]];
+  // Loop the set of tetrahedra again, set the info. of tet. per vertex.
+  tetrahedrons->traversalinit();
+  tetloop = tetrahedrontraverse();
+  while (tetloop != (tetrahedron *) NULL) {
+    for (i = 0; i < 4; i++) {
+      j = pointmark((point) tetloop[4 + i]) - in->firstnumber;
+      tetsperverlist[idx2tetlist[j]] = tetloop;
+      idx2tetlist[j]++;
+    }
+    tetloop = tetrahedrontraverse();
+  }
+  // Contents in 'idx2tetlist' are shifted, now shift them back.
+  for (i = points->items - 1; i >= 0; i--) {
+    idx2tetlist[i + 1] = idx2tetlist[i];
+  }
+  idx2tetlist[0] = 0;
+}
+
+//
+// End of mesh items searching routines
+//
+
+//
+// Begin of linear algebra functions
+//
+
+// 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];
+}
+
+// initm44() initializes a 4x4 matrix.
+void tetgenmesh::initm44(REAL a00, REAL a01, REAL a02, REAL a03,
+                         REAL a10, REAL a11, REAL a12, REAL a13,
+                         REAL a20, REAL a21, REAL a22, REAL a23,
+                         REAL a30, REAL a31, REAL a32, REAL a33,
+                         REAL M[4][4])
+{
+  M[0][0] = a00; M[0][1] = a01; M[0][2] = a02; M[0][3] = a03;
+  M[1][0] = a10; M[1][1] = a11; M[1][2] = a12; M[1][3] = a13;
+  M[2][0] = a20; M[2][1] = a21; M[2][2] = a22; M[2][3] = a23;
+  M[3][0] = a30; M[3][1] = a31; M[3][2] = a32; M[3][3] = a33;
+}
+
+// m4xm4() multiplies 2 4x4 matrics:  m1 = m1 * m2.
+void tetgenmesh::m4xm4(REAL m1[4][4], REAL m2[4][4])
+{
+  REAL tmp[4];
+  int i, j;
+
+  for (i = 0; i < 4; i++) {   // i-th row
+    for (j = 0; j < 4; j++) { // j-th col
+      tmp[j] = m1[i][0] * m2[0][j] + m1[i][1] * m2[1][j] 
+             + m1[i][2] * m2[2][j] + m1[i][3] * m2[3][j];
+    }
+    for (j = 0; j < 4; j++) 
+      m1[i][j] = tmp[j];
+  }
+}
+
+// m4xv4() multiplies a 4x4 matrix and 4x1 vector: v2 = m * v1
+void tetgenmesh::m4xv4(REAL v2[4], REAL m[4][4], REAL v1[4])
+{
+  v2[0] = m[0][0]*v1[0] + m[0][1]*v1[1] + m[0][2]*v1[2] + m[0][3]*v1[3];
+  v2[1] = m[1][0]*v1[0] + m[1][1]*v1[1] + m[1][2]*v1[2] + m[1][3]*v1[3];
+  v2[2] = m[2][0]*v1[0] + m[2][1]*v1[1] + m[2][2]*v1[2] + m[2][3]*v1[3];
+  v2[3] = m[3][0]*v1[0] + m[3][1]*v1[1] + m[3][2]*v1[2] + m[3][3]*v1[3];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// 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[3][3], int n, int* ps, REAL* d, int N)
+{
+  REAL scales[3];
+  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[3][3], int n, int* ps, REAL* b, int N)
+{
+  int i, j;
+  REAL X[3], 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];
+}
+
+//
+// End of linear algebra functions
+//
+
+//
+// Begin of geometric tests
+//
+
+// All the following routines require the input objects are not degenerate.
+//   i.e., a triangle must has three non-collinear corners; an edge must
+//   has two identical endpoints.  Degenerate cases should have to detect
+//   first and then handled as special cases.
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// edge_vertex_collinear_inter()    Test whether an edge (ab) and a vertex   //
+//                                  (p) are intersecting or not.             //
+//                                                                           //
+// p and ab are collinear.  Possible cases are p is coincident to a (p = a), //
+// or coincident to b (p = b), or inside ab (a < p < b), or outside ab (p <  //
+// a or p > b).  These cases can be quickly determined by comparing the      //
+// homogeneous coordinates of a, b, and p (which are not all equal).         //
+//                                                                           //
+// The return value indicates one of the three cases: DISJOINT, SHAREVERTEX  //
+// (p = a or p = b), and INTERSECT (a < p < b).                              //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+enum tetgenmesh::intersectresult tetgenmesh::
+edge_vertex_collinear_inter(REAL* A, REAL* B, REAL* P)
+{
+  int i = 0;
+  do {
+    if (A[i] < B[i]) {
+      if (P[i] < A[i]) {
+        return DISJOINT;
+      } else if (P[i] > A[i]) {
+        if (P[i] < B[i]) {
+          return INTERSECT;
+        } else if (P[i] > B[i]) {
+          return DISJOINT;
+        } else {
+          // assert(P[i] == B[i]);
+          return SHAREVERTEX;
+        }
+      } else {
+        // assert(P[i] == A[i]);
+        return SHAREVERTEX;
+      }
+    } else if (A[i] > B[i]) {
+      if (P[i] < B[i]) {
+        return DISJOINT;
+      } else if (P[i] > B[i]) {
+        if (P[i] < A[i]) {
+          return INTERSECT;
+        } else if (P[i] > A[i]) {
+          return DISJOINT;
+        } else {
+          // assert(P[i] == A[i]);
+          return SHAREVERTEX;
+        }
+      } else {
+        // assert(P[i] == B[i]);
+        return SHAREVERTEX;
+      }
+    }
+    // i-th coordinates are equal, try i+1-th;
+    i++;
+  } while (i < 3);
+  // Should never be here.
+  assert(i >= 3);
+  return DISJOINT;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// edge_edge_coplanar_inter()    Test whether two edges (ab) and (pq) are    //
+//                               intersecting or not.                        //
+//                                                                           //
+// ab and pq are coplanar.  Possible cases are ab and pq are disjointed, or  //
+// proper intersecting (intersect at a point other than their vertices), or  //
+// collinear and intersecting, or sharing at a vertex, or ab and pq are co-  //
+// incident (i.e., the same edge).                                           //
+//                                                                           //
+// A reference point R is required, which is exactly not coplanar with these //
+// two edges.  Since the caller know these two edges are coplanar, it must   //
+// be able to provide (or calculate) such a point.                           //
+//                                                                           //
+// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX,  //
+// SHAREEDGE, and INTERSECT.                                                 //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+enum tetgenmesh::intersectresult tetgenmesh:: 
+edge_edge_coplanar_inter(REAL* A, REAL* B, REAL* P, REAL* Q, REAL* R)
+{
+  REAL s1, s2, s3, s4;
+
+  assert(R != NULL);
+  
+  s1 = orient3d(A, B, R, P);
+  s2 = orient3d(A, B, R, Q);
+  if (s1 * s2 > 0.0) {
+    // Both p and q are at the same side of ab.
+    return DISJOINT;
+  }
+  s3 = orient3d(P, Q, R, A);
+  s4 = orient3d(P, Q, R, B);
+  if (s3 * s4 > 0.0) {
+    // Both a and b are at the same side of pq.
+    return DISJOINT;
+  }
+
+  // Possible degenerate cases are:
+  //   (1) Only one of p and q is collinear with ab;
+  //   (2) Both p and q are collinear with ab;
+  //   (3) Only one of a and b is collinear with pq. 
+  enum intersectresult abp, abq;
+  enum intersectresult pqa, pqb;
+
+  if (s1 == 0.0) {
+    // p is collinear with ab.
+    abp = edge_vertex_collinear_inter(A, B, P);
+    if (abp == INTERSECT) {
+      // p is inside ab.
+      return INTERSECT;
+    }
+    if (s2 == 0.0) {
+      // q is collinear with ab. Case (2).
+      abq = edge_vertex_collinear_inter(A, B, Q);
+      if (abq == INTERSECT) {
+        // q is inside ab.
+        return INTERSECT;
+      }
+      if (abp == SHAREVERTEX && abq == SHAREVERTEX) {
+        // ab and pq are identical.
+        return SHAREEDGE;
+      }
+      pqa = edge_vertex_collinear_inter(P, Q, A);
+      if (pqa == INTERSECT) {
+        // a is inside pq.
+        return INTERSECT;
+      }
+      pqb = edge_vertex_collinear_inter(P, Q, B);
+      if (pqb == INTERSECT) {
+        // b is inside pq.
+        return INTERSECT;
+      }
+      if (abp == SHAREVERTEX || abq == SHAREVERTEX) {
+        // either p or q is coincident with a or b.
+        // ONLY one case is possible, otherwise, shoule be SHAREEDGE.
+        assert(abp ^ abq);
+        return SHAREVERTEX;
+      }
+      // The last case. They are disjointed.
+      assert((abp == DISJOINT) && (abp == abq && abq == pqa && pqa == pqb));
+      return DISJOINT;
+    } else {
+      // p is collinear with ab. Case (1).
+      assert(abp == SHAREVERTEX || abp == DISJOINT);
+      return abp;
+    }
+  }
+  // p is NOT collinear with ab.
+  if (s2 == 0.0) {
+    // q is collinear with ab. Case (1).
+    abq = edge_vertex_collinear_inter(A, B, Q);
+    assert(abq == SHAREVERTEX || abq == DISJOINT || abq == INTERSECT);
+    return abq;
+  }
+
+  // We have found p and q are not collinear with ab. However, it is still
+  //   possible that a or b is collinear with pq (ONLY one of a and b).
+  if (s3 == 0.0) {
+    // a is collinear with pq. Case (3).
+    assert(s4 != 0.0);
+    pqa = edge_vertex_collinear_inter(P, Q, A);
+    // This case should have been detected in above.
+    assert(pqa != SHAREVERTEX);
+    assert(pqa == INTERSECT || pqa == DISJOINT);
+    return pqa;
+  }
+  if (s4 == 0.0) {
+    // b is collinear with pq. Case (3).
+    assert(s3 != 0.0);
+    pqb = edge_vertex_collinear_inter(P, Q, B);
+    // This case should have been detected in above.
+    assert(pqb != SHAREVERTEX);
+    assert(pqb == INTERSECT || pqb == DISJOINT);
+    return pqb;
+  }
+
+  // ab and pq are intersecting properly.
+  return INTERSECT;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// Notations                                                                 //
+//                                                                           //
+// Let ABC be the plane passes through a, b, and c;  ABC+ be the halfspace   //
+// including the set of all points x, such that orient3d(a, b, c, x) > 0;    //
+// ABC- be the other halfspace, such that for each point x in ABC-,          //
+// orient3d(a, b, c, x) < 0.  For the set of x which are on ABC, orient3d(a, //
+// b, c, x) = 0.                                                             //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// triangle_vertex_coplanar_inter()    Test whether a triangle (abc) and a   //
+//                                     point (p) are intersecting or not.    //
+//                                                                           //
+// abc and p are coplanar. Possible cases are p is inside abc, or on an edge //
+// of, or coincident with a vertex of, or outside abc.                       //
+//                                                                           //
+// A reference point R is required, which is exactly not coplanar with the   //
+// triangle and the vertex. Since the caller know they are coplanar, it must //
+// be able to provide (or calculate) such a point.                           //
+//                                                                           //
+// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX,  //
+// and INTERSECT.                                                            //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+enum tetgenmesh::intersectresult tetgenmesh::
+triangle_vertex_coplanar_inter(REAL* A, REAL* B, REAL* C, REAL* P, REAL* R)
+{
+  REAL s1, s2, s3;
+  int sign;
+
+  assert(R != (REAL *) NULL);
+
+  // Adjust the orientation of a, b, c and r, so that we can assume that
+  //   r is strictly in ABC- (i.e., r is above ABC wrt. right-hand rule).
+  s1 = orient3d(A, B, C, R);
+  assert(s1 != 0.0);
+  sign = s1 < 0.0 ? 1 : -1;
+
+  // Test starts from here.
+  s1 = orient3d(A, B, R, P) * sign;
+  if (s1 < 0.0) {
+    // p is in ABR-.
+    return DISJOINT;
+  }
+  s2 = orient3d(B, C, R, P) * sign;
+  if (s2 < 0.0) {
+    // p is in BCR-.
+    return DISJOINT;
+  }
+  s3 = orient3d(C, A, R, P) * sign;
+  if (s3 < 0.0) {
+    // p is in CAR-.
+    return DISJOINT;
+  }
+  if (s1 == 0.0) {
+    // p is on ABR.
+    if (s2 == 0.0) {
+      // p is on BCR.
+      assert(s3 > 0.0);
+      // p is coincident with b.
+      return SHAREVERTEX;
+    }
+    if (s3 == 0.0) {
+      // p is on CAR.
+      // p is coincident with a.
+      return SHAREVERTEX;
+    }
+    // p is on edge ab.
+    return INTERSECT;
+  }
+  // p is in ABR+.
+  if (s2 == 0.0) {
+    // p is on BCR.
+    if (s3 == 0.0) {
+      // p is on CAR.
+      // p is coincident with c.
+      return SHAREVERTEX;
+    }
+    // p is on edge bc.
+    return INTERSECT;
+  }
+  if (s3 == 0.0) {
+    // p is on CAR.
+    // p is on edge ca.
+    return INTERSECT;
+  }
+
+  // p is strictly inside abc.
+  return INTERSECT;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// triangle_edge_coplanar_inter()    Test whether a triangle (abc) and an    //
+//                                   edge (pq) are intersecting or not.      //
+//                                                                           //
+// A reference point R is required, which is exactly not coplanar with the   //
+// triangle and the edge. Since the caller know they are coplanar, it must   //
+// be able to provide (or calculate) such a point.                           //
+//                                                                           //
+// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX,  //
+// SHAREEDGE, and INTERSECT.                                                 //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+enum tetgenmesh::intersectresult tetgenmesh::
+triangle_edge_coplanar_inter(REAL* A, REAL* B, REAL* C, REAL* P, REAL* Q,
+                             REAL* R)
+{
+  enum intersectresult abpq, bcpq, capq;
+  enum intersectresult abcp, abcq;
+
+  // Test if pq is intersecting one of edges of abc.
+  abpq = edge_edge_coplanar_inter(A, B, P, Q, R);
+  if (abpq == INTERSECT || abpq == SHAREEDGE) {
+    return abpq;
+  }
+  bcpq = edge_edge_coplanar_inter(B, C, P, Q, R);
+  if (bcpq == INTERSECT || bcpq == SHAREEDGE) {
+    return bcpq;
+  }
+  capq = edge_edge_coplanar_inter(C, A, P, Q, R);
+  if (capq == INTERSECT || capq == SHAREEDGE) {
+    return capq;
+  }
+  
+  // Test if p and q is inside abc.
+  abcp = triangle_vertex_coplanar_inter(A, B, C, P, R);
+  if (abcp == INTERSECT) {
+    return INTERSECT;
+  }
+  abcq = triangle_vertex_coplanar_inter(A, B, C, Q, R);
+  if (abcq == INTERSECT) {
+    return INTERSECT;
+  }
+
+  // Combine the test results of edge intersectings and triangle insides
+  //   to detect whether abc and pq are sharing vertex or disjointed.
+  if (abpq == SHAREVERTEX) {
+    // p or q is coincident with a or b.
+    assert(abcp ^ abcq);
+    return SHAREVERTEX;
+  }
+  if (bcpq == SHAREVERTEX) {
+    // p or q is coincident with b or c.
+    assert(abcp ^ abcq);
+    return SHAREVERTEX;
+  }
+  if (capq == SHAREVERTEX) {
+    // p or q is coincident with c or a.
+    assert(abcp ^ abcq);
+    return SHAREVERTEX;
+  }
+
+  // They are disjointed.
+  return DISJOINT;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// triangle_edge_inter_tail()    Test whether a triangle (abc) and an edge   //
+//                               (pq) are intersecting or not.               //
+//                                                                           //
+// s1 and s2 are results of pre-performed orientation tests. s1 = orient3d(  //
+// a, b, c, p); s2 = orient3d(a, b, c, q).                                   //
+//                                                                           //
+// To separate this routine from triangle_edge_inter() can save two          //
+// orientation tests in triangle_triangle_inter().                           //
+//                                                                           //
+// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX,  //
+// SHAREEDGE, and INTERSECT.                                                 //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+enum tetgenmesh::intersectresult tetgenmesh::
+triangle_edge_inter_tail(REAL* A, REAL* B, REAL* C, REAL* P, REAL* Q, REAL s1,
+                         REAL s2)
+{
+  REAL s3, s4, s5;
+  int sign;
+
+  if (s1 * s2 > 0.0) {
+    // p, q are at the same halfspace of ABC, no intersection.
+    return DISJOINT;
+  }
+
+  if (s1 * s2 < 0.0) {
+    // p, q are both not on ABC (and not sharing vertices, edges of abc).
+    // Adjust the orientation of a, b, c and p, so that we can assume that
+    //   p is strictly in ABC-, and q is strictly in ABC+.
+    sign = s1 < 0.0 ? 1 : -1;
+    s3 = orient3d(A, B, P, Q) * sign;
+    if (s3 < 0.0) {
+      // q is at ABP-.
+      return DISJOINT;
+    }
+    s4 = orient3d(B, C, P, Q) * sign;
+    if (s4 < 0.0) {
+      // q is at BCP-.
+      return DISJOINT;
+    }
+    s5 = orient3d(C, A, P, Q) * sign;
+    if (s5 < 0.0) {
+      // q is at CAP-.
+      return DISJOINT;
+    }
+    if (s3 == 0.0) {
+      // q is on ABP.
+      if (s4 == 0.0) {
+        // q is on BCP (and q must in CAP+).
+        assert(s5 > 0.0); 
+        // pq intersects abc at vertex b.
+        return SHAREVERTEX;
+      }
+      if (s5 == 0.0) {
+        // q is on CAP (and q must in BCP+).
+        // pq intersects abc at vertex a.
+        return SHAREVERTEX;
+      }
+      // q in both BCP+ and CAP+.
+      // pq crosses ab properly.
+      return INTERSECT;
+    }
+    // q is in ABP+;
+    if (s4 == 0.0) {
+      // q is on BCP.
+      if (s5 == 0.0) {
+        // q is on CAP.
+        // pq intersects abc at vertex c.
+        return SHAREVERTEX;
+      }
+      // pq crosses bc properly.
+      return INTERSECT;
+    }
+    // q is in BCP+;
+    if (s5 == 0.0) {
+      // q is on CAP.
+      // pq crosses ca properly.
+      return INTERSECT;
+    }
+    // q is in CAP+;
+    // pq crosses abc properly.
+    return INTERSECT;
+  }
+
+  if (s1 != 0.0 || s2 != 0.0) {
+    // Either p or q is coplanar with abc. ONLY one of them is possible.
+    if (s1 == 0.0) {
+      // p is coplanar with abc, q can be used as reference point.
+      assert(s2 != 0.0);
+      return triangle_vertex_coplanar_inter(A, B, C, P, Q);
+    } else {
+      // q is coplanar with abc, p can be used as reference point.
+      assert(s2 == 0.0);
+      return triangle_vertex_coplanar_inter(A, B, C, Q, P);
+    }
+  }
+
+  // pq is coplanar with abc.  Calculate a point which is exactly
+  //   non-coplanar with a, b, and c.
+  REAL R[3], N[3];
+  REAL ax, ay, az, bx, by, bz;
+  
+  ax = A[0] - B[0];
+  ay = A[1] - B[1];
+  az = A[2] - B[2];
+  bx = A[0] - C[0];
+  by = A[1] - C[1];
+  bz = A[2] - C[2];
+  N[0] = ay * bz - by * az;
+  N[1] = az * bx - bz * ax;
+  N[2] = ax * by - bx * ay;
+  // The normal should not be a zero vector. 
+  assert((fabs(N[0]) + fabs(N[1]) + fabs(N[2])) > 0.0);
+  // The reference point R is lifted from A to the normal direction with
+  //   a non-zero distance.
+  R[0] = N[0] + A[0];
+  R[1] = N[1] + A[1];
+  R[2] = N[2] + A[2];
+  // Becareful the case: if the non-zero component(s) in N is smaller than
+  //   the machine epsilon (i.e., 2^(-16) for double), R will exactly equal
+  //   to A due to the round-off error.  Do check if it is.
+  if (R[0] == A[0] && R[1] == A[1] && R[2] == A[2]) {
+    int i, j;
+    for (i = 0; i < 3; i++) {
+      assert (R[i] == A[i]);
+      j = 2;
+      do {
+        if (N[i] > 0.0) {
+          N[i] += (j * macheps);
+        } else {
+          N[i] -= (j * macheps);
+        }
+        R[i] = N[i] + A[i];
+        j *= 2;
+      } while (R[i] == A[i]);
+    }
+  }
+
+  return triangle_edge_coplanar_inter(A, B, C, P, Q, R);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// triangle_edge_inter()    Test whether a triangle (abc) and an edge (pq)   //
+//                          are intersecting or not.                         //
+//                                                                           //
+// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX,  //
+// SHAREEDGE, and INTERSECT.                                                 //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+enum tetgenmesh::intersectresult tetgenmesh::
+triangle_edge_inter(REAL* A, REAL* B, REAL* C, REAL* P, REAL* Q)
+{
+  REAL s1, s2;
+
+  // Test the locations of p and q with respect to ABC.
+  s1 = orient3d(A, B, C, P);
+  s2 = orient3d(A, B, C, Q);
+
+  return triangle_edge_inter_tail(A, B, C, P, Q, s1, s2);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// triangle_triangle_inter()    Test whether two triangle (abc) and (opq)    //
+//                              are intersecting or not.                     //
+//                                                                           //
+// The return value indicates one of the five cases: DISJOINT, SHAREVERTEX,  //
+// SHAREEDGE, SHAREFACE, and INTERSECT.                                      //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+enum tetgenmesh::intersectresult tetgenmesh::
+triangle_triangle_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 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 DISJOINT;
+  }
+
+  enum intersectresult abcop, abcpq, abcqo;
+  int shareedge = 0;
+
+  abcop = triangle_edge_inter_tail(A, B, C, O, P, s_o, s_p);
+  if (abcop == INTERSECT) {
+    return INTERSECT;
+  } else if (abcop == SHAREEDGE) {
+    shareedge++;
+  }
+  abcpq = triangle_edge_inter_tail(A, B, C, P, Q, s_p, s_q);
+  if (abcpq == INTERSECT) {
+    return INTERSECT;
+  } else if (abcpq == SHAREEDGE) {
+    shareedge++;
+  }
+  abcqo = triangle_edge_inter_tail(A, B, C, Q, O, s_q, s_o);
+  if (abcqo == INTERSECT) {
+    return INTERSECT;
+  } else if (abcqo == SHAREEDGE) {
+    shareedge++;
+  }
+  if (shareedge == 3) {
+    // opq are coincident with abc.
+    return SHAREFACE;
+  }
+  // It is only possible either no share edge or one.
+  assert(shareedge == 0 || shareedge == 1);
+
+  // Continue to detect whether opq and abc are intersecting or not.
+  enum intersectresult opqab, opqbc, opqca;
+
+  opqab = triangle_edge_inter_tail(O, P, Q, A, B, s_a, s_b);
+  if (opqab == INTERSECT) {
+    return INTERSECT;
+  }
+  opqbc = triangle_edge_inter_tail(O, P, Q, B, C, s_b, s_c);
+  if (opqbc == INTERSECT) {
+    return INTERSECT;
+  }
+  opqca = triangle_edge_inter_tail(O, P, Q, C, A, s_c, s_a);
+  if (opqca == INTERSECT) {
+    return 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 == SHAREEDGE) {
+    assert(abcpq == SHAREVERTEX && abcqo == SHAREVERTEX);
+    // op is coincident with an edge of abc.
+    return SHAREEDGE;
+  }
+  if (abcpq == SHAREEDGE) {
+    assert(abcop == SHAREVERTEX && abcqo == SHAREVERTEX);
+    // pq is coincident with an edge of abc.
+    return SHAREEDGE;
+  }
+  if (abcqo == SHAREEDGE) {
+    assert(abcop == SHAREVERTEX && abcpq == SHAREVERTEX);
+    // qo is coincident with an edge of abc.
+    return SHAREEDGE;
+  }
+
+  // They may share a vertex or disjoint.
+  if (abcop == SHAREVERTEX) {
+    // o or p is coincident with a vertex of abc.
+    if (abcpq == SHAREVERTEX) {
+      // p is the coincident vertex.
+      assert(abcqo != SHAREVERTEX);
+    } else {
+      // o is the coincident vertex.
+      assert(abcqo == SHAREVERTEX);
+    }
+    return SHAREVERTEX;
+  }
+  if (abcpq == SHAREVERTEX) {
+    // q is the coincident vertex.
+    assert(abcqo == SHAREVERTEX);
+    return SHAREVERTEX;
+  }
+
+  // They are disjoint.
+  return DISJOINT;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// iscollinear()    Check if three points are collinear with respect to a    //
+//                  given relative tolerance.                                //
+//                                                                           //
+// 'epspp' is the relative tolerance provided by caller. The collinearity is //
+// determined by evaluating the angle q between the two vectors formed from  //
+// thes three points. If q <= epspp, then they are assumed be collinear.     //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::iscollinear(REAL* A, REAL* B, REAL* C, REAL epspp)
+{
+  REAL V[3], W[3];
+  REAL Lv, Lw, d, q;
+
+  V[0] = A[0] - B[0];
+  V[1] = A[1] - B[1];
+  V[2] = A[2] - B[2];
+  W[0] = A[0] - C[0];
+  W[1] = A[1] - C[1];
+  W[2] = A[2] - C[2];
+  Lv = sqrt(V[0] * V[0] + V[1] * V[1] + V[2] * V[2]);
+  Lw = sqrt(W[0] * W[0] + W[1] * W[1] + W[2] * W[2]);
+  
+  d = (V[0] * W[0] + V[1] * W[1] + V[2] * W[2]) / (Lv * Lw);
+  if (d > 1.0) {
+    q = 0.0;
+  } else if (d < -1.0) {
+    q = 0.0; // q = PI;
+  } else {
+    q = acos(fabs(d));
+  }
+  
+  return q <= epspp;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// iscoplanar()    Check if four points are coplanar with respect to a given //
+//                 relative tolerance.                                       //
+//                                                                           //
+// 'vol6' is the six times of the signed volume of the tetrahedron formed by //
+// the four points. 'epspp' is the relative tolerance provided by the caller.//
+// This coplanarity is determined by evaluating the value:                   //
+//                                                                           //
+//                 q = fabs(vol6) / L^3                                      //
+//                                                                           //
+// where L is the average edge length of the tetrahedron.  If q <= epspp,    //
+// then these four points are assumed be coplanar.                           //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::
+iscoplanar(REAL* k, REAL* l, REAL* m, REAL* n, REAL vol6, REAL epspp)
+{
+  REAL L, q;
+  REAL x, y, z;  
+
+  x = k[0] - l[0];
+  y = k[1] - l[1];
+  z = k[2] - l[2];
+  L = sqrt(x * x + y * y + z * z);
+  x = l[0] - m[0];
+  y = l[1] - m[1];
+  z = l[2] - m[2];
+  L += sqrt(x * x + y * y + z * z);
+  x = m[0] - k[0];
+  y = m[1] - k[1];
+  z = m[2] - k[2];
+  L += sqrt(x * x + y * y + z * z);
+  x = k[0] - n[0];
+  y = k[1] - n[1];
+  z = k[2] - n[2];
+  L += sqrt(x * x + y * y + z * z);
+  x = l[0] - n[0];
+  y = l[1] - n[1];
+  z = l[2] - n[2];
+  L += sqrt(x * x + y * y + z * z);
+  x = m[0] - n[0];
+  y = m[1] - n[1];
+  z = m[2] - n[2];
+  L += sqrt(x * x + y * y + z * z);
+  assert(L > 0.0);
+  L /= 6.0;
+  q = fabs(vol6) / (L * L * L);
+  
+  return q <= epspp;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// iscospheric()    Check if five points are coplanar with respect to a      //
+//                  given relative tolerance.                                //
+//                                                                           //
+// The cosphere is determined by comparing the distance between the radius R //
+// of the circumsphere S of the first four points and the distance from the  //
+// circumcenter C of S to the fifth points P, i.e., to calculate the value:  //
+//                                                                           //
+//                  q = fabs(P - C) / R                                      //
+//                                                                           //
+// If q <= epspp, then these five points are assumed to be cospherical.      //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::
+iscospheric(REAL* k, REAL* l, REAL* m, REAL* n, REAL* o, REAL epspp)
+{
+  REAL ori, *p5; 
+  REAL cent[3], R, D, q;
+
+  ori = orient3d(k, l, m, n);
+  if (iscoplanar(k, l, m, n, ori, epspp)) {
+    ori = orient3d(k, l, m, o);
+    assert(!iscoplanar(k, l, m, o, ori, epspp));
+    circumsphere(k, l, m, o, cent, &R);
+    p5 = n;
+  } else {
+    circumsphere(k, l, m, n, cent, &R);
+    p5 = o;
+  }
+  D = distance(p5, cent);
+  q = fabs(D - R) / R;
+  
+  return  q <= epspp;
+}
+
+//
+// End of geometric tests
+//
+
+//
+// Begin of Geometric quantities calculators
+//
+
+// distance() computs 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]));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// 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));
+  assert(len != 0.0);
+  v1[0] /= len;
+  v1[1] /= len;
+  v1[2] /= len;
+  l_p = dot(v1, v2);
+
+  return sqrt(dot(v2, v2) - l_p * l_p);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// 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;
+  assert(lenlen != 0.0);
+  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));
+  assert(len != 0.0);
+  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, &len);
+  assert(len > 0.0);
+  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);
+  assert(fabs(dist) >= b->epsilon);
+  
+  // 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];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// facenormal()    Calculate the normal of a face given by three points.     //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::facenormal(REAL* pa, REAL* pb, REAL* pc, REAL* n, REAL* nlen)
+{
+  REAL v1[3], v2[3];
+
+  v1[0] = pb[0] - pa[0];
+  v1[1] = pb[1] - pa[1];
+  v1[2] = pb[2] - pa[2];
+  v2[0] = pc[0] - pa[0];
+  v2[1] = pc[1] - pa[1];
+  v2[2] = pc[2] - pa[2];
+
+  cross(v1, v2, n);
+  if (nlen != (REAL *) NULL) {
+    *nlen = sqrt(dot(n, n));
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// edgeorthonormal()    Return the unit normal of an edge in a given plane.  //
+//                                                                           //
+// The edge is from e1 to e2,  the plane is defined by given an additional   //
+// point op, which is non-collinear with the edge.  In addition, the side of //
+// the edge in which op lies defines the positive position of the normal.    //
+//                                                                           //
+// Let v1 be the unit vector from e1 to e2, v2 be the unit edge vector from  //
+// e1 to op, fn be the unit face normal calculated by fn = v1 x v2. Then the //
+// unit edge normal of e1e2 pointing to op is n = fn x v1.  Note, we should  //
+// not change the position of fn and v1, otherwise, we get the edge normal   //
+// pointing to the other side of op.                                         //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::edgeorthonormal(REAL* e1, REAL* e2, REAL* op, REAL* n)
+{
+  REAL v1[3], v2[3], fn[3];
+  REAL len;
+
+  // Get the edge vector v1.
+  v1[0] = e2[0] - e1[0];
+  v1[1] = e2[1] - e1[1];
+  v1[2] = e2[2] - e1[2];
+  // Get the edge vector v2.
+  v2[0] = op[0] - e1[0];
+  v2[1] = op[1] - e1[1];
+  v2[2] = op[2] - e1[2];
+  // Get the face normal fn = v1 x v2.
+  cross(v1, v2, fn);
+  // Get the edge normal n pointing to op. n = fn x v1.
+  cross(fn, v1, n);
+  // Normalize the vector.
+  len = sqrt(dot(n, n));
+  n[0] /= len;
+  n[1] /= len;
+  n[2] /= len;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// facedihedral()    Return the dihedral angle (in radian) between two       //
+//                   adjoining faces.                                        //
+//                                                                           //
+// 'pa', 'pb' are the shared edge of these two faces, 'pc1', and 'pc2' are   //
+// apexes of these two faces.  Return the angle (between 0 to 2*pi) between  //
+// the normal of face (pa, pb, pc1) and normal of face (pa, pb, pc2).        //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+REAL tetgenmesh::facedihedral(REAL* pa, REAL* pb, REAL* pc1, REAL* pc2)
+{
+  REAL n1[3], n2[3];
+  REAL n1len, n2len;
+  REAL costheta, ori;
+  REAL theta;
+
+  facenormal(pa, pb, pc1, n1, &n1len);
+  facenormal(pa, pb, pc2, n2, &n2len);
+  costheta = dot(n1, n2) / (n1len * n2len);
+  theta = acos(costheta);
+  ori = orient3d(pa, pb, pc1, pc2);
+  if (ori > 0.0) {
+    theta = 2 * PI - theta;
+  }
+
+  return theta;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// tetalldihedral()    Get all(six) dihedral angles in tetrahedron formed by //
+//                     vertices a, b, c and d. Return by array adDihed[6].   //
+//                                                                           //
+// The order in which the dihedrals are assigned matters for computation of  //
+// solid angles. The way they're currently set up, combining them as (0,1,2),//
+// (0,3,4), (1,3,5), (2,4,5) gives (in order) solid angles at vertices a, b, //
+// c and d.                                                                  //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::
+tetalldihedral(point pa, point pb, point pc, point pd, REAL dihed[6])
+{
+  REAL n0[3], n1[3], n2[3], n3[3];
+  REAL n0len, n1len, n2len, n3len;
+  REAL dotp;
+  
+  facenormal(pc, pb, pd, n0, &n0len);
+  facenormal(pa, pc, pd, n1, &n1len);
+  facenormal(pb, pa, pd, n2, &n2len);
+  facenormal(pa, pb, pc, n3, &n3len);
+  
+  n0[0] /= n0len; n0[1] /= n0len; n0[2] /= n0len;
+  n1[0] /= n1len; n1[1] /= n1len; n1[2] /= n1len;
+  n2[0] /= n2len; n2[1] /= n2len; n2[2] /= n2len;
+  n3[0] /= n3len; n3[1] /= n3len; n3[2] /= n3len;
+
+  dotp = -dot(n0, n1);
+  if (dotp > 1.) dotp = 1.;
+  else if (dotp < -1.) dotp = -1.;
+  dihed[5] = acos(dotp); // Edge CD
+
+  dotp = -dot(n0, n2);
+  if (dotp > 1.) dotp = 1.;
+  else if (dotp < -1.) dotp = -1.;
+  dihed[4] = acos(dotp); // Edge BD
+
+  dotp = -dot(n0, n3);
+  if (dotp > 1.) dotp = 1.;
+  else if (dotp < -1.) dotp = -1.;
+  dihed[3] = acos(dotp); // Edge BC
+
+  dotp = -dot(n1, n2);
+  if (dotp > 1.) dotp = 1.;
+  else if (dotp < -1.) dotp = -1.;
+  dihed[2] = acos(dotp); // Edge AD
+
+  dotp = -dot(n1, n3);
+  if (dotp > 1.) dotp = 1.;
+  else if (dotp < -1.) dotp = -1.;
+  dihed[1] = acos(dotp); // Edge AC
+
+  dotp = -dot(n2, n3);
+  if (dotp > 1.) dotp = 1.;
+  else if (dotp < -1.) dotp = -1.;
+  dihed[0] = acos(dotp); // Edge AB
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// 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 indicated the points are degenrate.//
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::
+circumsphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* cent, REAL* radius)
+{
+  REAL A[3][3], rhs[3], D;
+  int indx[3];
+
+  // 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;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// inscribedsphere()    Compute the radius and center of the biggest         //
+//                      inscribed sphere of a given tetrahedron.             //
+//                                                                           //
+// The tetrahedron is given by its four points, it must not be degenerate.   //
+// The center and radius are returned in 'cent' and 'radius' respectively if //
+// they are not NULLs.                                                       //
+//                                                                           //
+// Geometrical fact. For any simplex in d dimension,                         //
+//   r/h1 + r/h2 + ... r/hn = 1 (n <= d + 1);                                //
+// where r is the radius of inscribed ball, and h is the height of each side //
+// of the simplex. The value of 'r/h' is just the barycenter coordinates of  //
+// each vertex of the simplex. Therefore, we can compute the radius and      //
+// center of the smallest inscribed ball as following equations:             //
+//   r = 1.0 / (1/h1 + 1/h2 + ... + 1/hn);          (1)                      //
+//   C = r/h1 * P1 + r/h2 * P2 + ... + r/hn * Pn;   (2)                      //
+// where C is the vector of center, P1, P2, .. Pn are vectors of vertices.   //
+// Here (2) contains n linear equations with n variables.  (h, P) must be a  //
+// pair, h is the height from P to its opposite face.                        //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::
+inscribedsphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* cent, 
+                REAL* radius)
+{
+  REAL A[3][3], rhs[3], D;
+  REAL N[3][4], H[4];  // Normals (colume vectors) and heights of each face.
+  REAL rd;
+  int indx[3], i, j;  
+
+  // Compute the normals of 4 faces.
+  A[0][0] = pa[0] - pd[0];
+  A[0][1] = pa[1] - pd[1];
+  A[0][2] = pa[2] - pd[2];
+  A[1][0] = pb[0] - pd[0];
+  A[1][1] = pb[1] - pd[1];
+  A[1][2] = pb[2] - pd[2];
+  A[2][0] = pc[0] - pd[0];
+  A[2][1] = pc[1] - pd[1];
+  A[2][2] = pc[2] - pd[2];
+  // Compute inverse of matrix A, to get the 3 normals of 4 faces.
+  lu_decmp(A, 3, indx, &D, 0);     // Decompose the matrix just once.
+  for (j = 0; j < 3; j++) {
+    for (i = 0; i < 3; i++) rhs[i] = 0.0;
+    rhs[j] = -1.0;
+    lu_solve(A, 3, indx, rhs, 0);
+    for (i = 0; i < 3; i++) N[i][j] = rhs[i];
+  }
+  // Compute the last normal by summing 3 computed vectors, because sum over 
+  //   a closed sufrace is 0.
+  N[0][3] = - N[0][0] - N[0][1] - N[0][2];
+  N[1][3] = - N[1][0] - N[1][1] - N[1][2];
+  N[2][3] = - N[2][0] - N[2][1] - N[2][2];
+  // Compute the length of  normals.
+  for (i = 0; i < 4; i++) {
+    // H[i] is the inverse of height of its corresponding face.
+    H[i] = sqrt(N[0][i] * N[0][i] + N[1][i] * N[1][i] + N[2][i] * N[2][i]);
+  }
+  // Compute the radius use eq. (1).
+  rd = 1.0 / (H[0] + H[1] + H[2] + H[3]);
+  if (radius != (REAL*) NULL) *radius = rd;
+  if (cent != (REAL*) NULL) {
+    // Compute the center use eq. (2).
+    cent[0] = rd * (H[0] * pa[0] + H[1] * pb[0] + H[2] * pc[0] + H[3] * pd[0]);
+    cent[1] = rd * (H[0] * pa[1] + H[1] * pb[1] + H[2] * pc[1] + H[3] * pd[1]);
+    cent[2] = rd * (H[0] * pa[2] + H[1] * pb[2] + H[2] * pc[2] + H[3] * pd[2]);
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// rotatepoint()    Create a point by rotating an existing point.            //
+//                                                                           //
+// Create a 3D point by rotating point 'p' with an angle 'rotangle' (in arc  //
+// degree) around a rotating axis given by a vector from point 'p1' to 'p2'. //
+// The rotation is according with right-hand rule, i.e., use your right-hand //
+// to grab the axis with your thumber pointing to its positive direction,    //
+// your fingers indicate the rotating direction.                             //
+//                                                                           //
+// The rotating steps are the following:                                     //
+//   1. Translate vector 'p1->p2' to origin, M1;                             //
+//   2. Rotate vector around the Y-axis until it lies in the YZ plane, M2;   //
+//   3. Rotate vector around the X-axis until it lies on the Z axis, M3;     //
+//   4. Perform the rotation of 'p' around the z-axis, M4;                   //
+//   5. Undo Step 3, M5;                                                     //
+//   6. Undo Step 2, M6;                                                     //
+//   7. Undo Step 1, M7;                                                     //
+// Use matrix multiplication to combine the above sequences, we get:         //
+//   p0' = T * p0, where T = M7 * M6 * M5 * M4 * M3 * M2 * M1                //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::rotatepoint(REAL* p, REAL rotangle, REAL* p1, REAL* p2)
+{
+  REAL T[4][4], pp0[4], p0t[4], p2t[4];
+  REAL roty, rotx, alphaR, projlen;
+  REAL dx, dy, dz;
+
+  initm44(1, 0, 0, -p1[0],
+          0, 1, 0, -p1[1],
+          0, 0, 1, -p1[2],
+          0, 0, 0, 1, T);
+  pp0[0] = p[0]; pp0[1] = p[1]; pp0[2] = p[2]; pp0[3] = 1.0;
+  m4xv4(p0t, T, pp0); // Step 1
+  pp0[0] = p2[0]; pp0[1] = p2[1]; pp0[2] = p2[2]; pp0[3] = 1.0;
+  m4xv4(p2t, T, pp0); // Step 1
+
+  // Get the rotation angle around y-axis;
+  dx = p2t[0];
+  dz = p2t[2];
+  projlen = sqrt(dx * dx + dz * dz);
+  if (projlen <= (b->epsilon * 1e-2) * longest) {
+    roty = 0;
+  } else {
+    roty = acos(dz / projlen);
+    if (dx < 0) {
+      roty = -roty;
+    }
+  }
+
+  initm44(cos(-roty), 0, sin(-roty), 0,
+          0, 1, 0, 0,
+          -sin(-roty), 0, cos(-roty), 0,
+          0, 0, 0, 1, T);
+  pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0;
+  m4xv4(p0t, T, pp0); // Step 2
+  pp0[0] = p2t[0]; pp0[1] = p2t[1]; pp0[2] = p2t[2]; pp0[3] = 1.0;
+  m4xv4(p2t, T, pp0); // Step 2
+
+  // Get the rotation angle around x-axis
+  dy = p2t[1];
+  dz = p2t[2];
+  projlen = sqrt(dy * dy + dz * dz);
+  if (projlen <= (b->epsilon * 1e-2) * longest) {
+    rotx = 0;
+  } else {
+    rotx = acos(dz / projlen);
+    if (dy < 0) {
+      rotx = -rotx;
+    }
+  }
+    
+  initm44(1, 0, 0, 0,
+          0, cos(rotx), -sin(rotx), 0,
+          0, sin(rotx), cos(rotx), 0,
+          0, 0, 0, 1, T);
+  pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0;
+  m4xv4(p0t, T, pp0); // Step 3
+  // pp0[0] = p2t[0]; pp0[1] = p2t[1]; pp0[2] = p2t[2]; pp0[3] = 1.0;
+  // m4xv4(p2t, T, pp0); // Step 3
+
+  alphaR = rotangle;
+  initm44(cos(alphaR), -sin(alphaR), 0, 0,
+          sin(alphaR), cos(alphaR), 0, 0,
+          0, 0, 1, 0,
+          0, 0, 0, 1, T);
+  pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0;
+  m4xv4(p0t, T, pp0); // Step 4
+
+  initm44(1, 0, 0, 0,
+          0, cos(-rotx), -sin(-rotx), 0,
+          0, sin(-rotx), cos(-rotx), 0,
+          0, 0, 0, 1, T);
+  pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0;
+  m4xv4(p0t, T, pp0); // Step 5
+
+  initm44(cos(roty), 0, sin(roty), 0,
+          0, 1, 0, 0,
+          -sin(roty), 0, cos(roty), 0,
+          0, 0, 0, 1, T);
+  pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0;
+  m4xv4(p0t, T, pp0); // Step 6
+
+  initm44(1, 0, 0, p1[0],
+          0, 1, 0, p1[1],
+          0, 0, 1, p1[2],
+          0, 0, 0, 1, T);
+  pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0;
+  m4xv4(p0t, T, pp0); // Step 7  
+
+  p[0] = p0t[0];
+  p[1] = p0t[1];
+  p[2] = p0t[2];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// spherelineint()    3D line sphere (or circle) intersection.               //
+//                                                                           //
+// The line is given by two points p1, and p2, the sphere is centered at c   //
+// with radius r.  This function returns a pointer array p which first index //
+// indicates the number of intersection point, followed by coordinate pairs. //
+//                                                                           //
+// The following code are adapted from: http://astronomy.swin.edu.au/pbourke //
+// /geometry/sphereline. Paul Bourke pbourke@swin.edu.au                     //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::spherelineint(REAL* p1, REAL* p2, REAL* C, REAL R, REAL p[7])
+{
+  REAL x1, y1, z1; //  P1 coordinates (point of line)
+  REAL x2, y2, z2; //  P2 coordinates (point of line)
+  REAL x3, y3, z3, r; //  P3 coordinates and radius (sphere)
+  REAL a, b, c, mu, i ;
+
+  x1 = p1[0]; y1 = p1[1]; z1 = p1[2];
+  x2 = p2[0]; y2 = p2[1]; z2 = p2[2];
+  x3 = C[0];  y3 = C[1];  z3 = C[2];
+  r = R;
+  
+  a =   (x2 - x1) * (x2 - x1) 
+      + (y2 - y1) * (y2 - y1) 
+      + (z2 - z1) * (z2 - z1);
+  b = 2 * ( (x2 - x1) * (x1 - x3)
+          + (y2 - y1) * (y1 - y3)
+          + (z2 - z1) * (z1 - z3) ) ;
+  c =   (x3 * x3) + (y3 * y3) + (z3 * z3)
+      + (x1 * x1) + (y1 * y1) + (z1 * z1)
+      - 2 * (x3 * x1 + y3 * y1 + z3 * z1) - (r * r) ;
+  i = b * b - 4 * a * c ;
+
+  if (i < 0.0) {
+    // no intersection
+    p[0] = 0.0;
+  } else if (i == 0.0) {
+    // one intersection
+    p[0] = 1.0;
+    mu = -b / (2 * a) ;
+    p[1] = x1 + mu * (x2 - x1);
+    p[2] = y1 + mu * (y2 - y1);
+    p[3] = z1 + mu * (z2 - z1);
+  } else {
+    assert(i > 0.0);
+    // two intersections
+    p[0] = 2.0;
+    // first intersection
+    mu = (-b + sqrt((b * b) - 4 * a * c)) / (2 * a);
+    p[1] = x1 + mu * (x2 - x1);
+    p[2] = y1 + mu * (y2 - y1);
+    p[3] = z1 + mu * (z2 - z1);
+    // second intersection
+    mu = (-b - sqrt((b * b) - 4 * a * c)) / (2 * a);
+    p[4] = x1 + mu * (x2 - x1);
+    p[5] = y1 + mu * (y2 - y1);
+    p[6] = z1 + mu * (z2 - z1);
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// linelineint()    Calculate The shortest line between two lines in 3D.     //
+//                                                                           //
+// Two 3D lines generally don't intersect at a point, they may be parallel ( //
+// no intersections), or they may be coincident (infinite intersections) but //
+// most often only their projection onto a plane intersect.  When they don't //
+// exactly intersect at a point they can be connected by a line segment, the //
+// shortest line segment is unique and is often considered to be their inter-//
+// section in 3D.                                                            //
+//                                                                           //
+// The following code are adapted from: http://astronomy.swin.edu.au/pbourke //
+// /geometry/lineline3d. Paul Bourke pbourke@swin.edu.au                     //
+//                                                                           //
+// Calculate the line segment PaPb that is the shortest route between two    //
+// lines P1P2 and P3P4. This function returns a pointer array p which first  //
+// index indicates there exists solution or not, 0 means no solution, 1 meas //
+// has solution followed by two coordinate pairs.                            //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::linelineint(REAL *p1,REAL *p2, REAL *p3, REAL *p4, REAL p[7])
+{
+  REAL p13[3], p43[3], p21[3];
+  REAL d1343, d4321, d1321, d4343, d2121;
+  REAL numer, denom;
+  REAL mua, mub;
+
+  p13[0] = p1[0] - p3[0];
+  p13[1] = p1[1] - p3[1];
+  p13[2] = p1[2] - p3[2];
+  p43[0] = p4[0] - p3[0];
+  p43[1] = p4[1] - p3[1];
+  p43[2] = p4[2] - p3[2];
+  if (p43[0] == 0.0 && p43[1] == 0.0 && p43[2] == 0.0) {
+    p[0] = 0.0;
+    return;
+  }
+
+  p21[0] = p2[0] - p1[0];
+  p21[1] = p2[1] - p1[1];
+  p21[2] = p2[2] - p1[2];
+  if (p21[0] == 0.0 && p21[1] == 0.0 && p21[2] == 0.0) {
+    p[0] = 0.0;
+    return;
+  }
+
+  d1343 = p13[0] * p43[0] + p13[1] * p43[1] + p13[2] * p43[2];
+  d4321 = p43[0] * p21[0] + p43[1] * p21[1] + p43[2] * p21[2];
+  d1321 = p13[0] * p21[0] + p13[1] * p21[1] + p13[2] * p21[2];
+  d4343 = p43[0] * p43[0] + p43[1] * p43[1] + p43[2] * p43[2];
+  d2121 = p21[0] * p21[0] + p21[1] * p21[1] + p21[2] * p21[2];
+
+  denom = d2121 * d4343 - d4321 * d4321;
+  if (denom == 0.0) {
+    p[0] = 0.0;
+    return;
+  }
+  numer = d1343 * d4321 - d1321 * d4343;
+  mua = numer / denom;
+  mub = (d1343 + d4321 * mua) / d4343;
+
+  p[0] = 1.0;
+  p[1] = p1[0] + mua * p21[0];
+  p[2] = p1[1] + mua * p21[1];
+  p[3] = p1[2] + mua * p21[2];
+  p[4] = p3[0] + mub * p43[0];
+  p[5] = p3[1] + mub * p43[1];
+  p[6] = p3[2] + mub * p43[2];
+}
+
+//
+// End of Geometric quantities calculators
+//
+
+//
+// Begin of memory management routines
+//
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// dummyinit()    Initialize the tetrahedron that fills "outer space" and    //
+//                the omnipresent subface.                                   //
+//                                                                           //
+// The tetrahedron that fills "outer space" called 'dummytet', is pointed to //
+// by every tetrahedron and subface on a boundary (be it outer or inner) of  //
+// the tetrahedralization. Also, 'dummytet' points to one of the tetrahedron //
+// on the convex hull(until the holes and concavities are carved), making it //
+// possible to find a starting tetrahedron for point location.               //
+//                                                                           //
+// The omnipresent subface,'dummysh', is pointed to by every tetrahedron or  //
+// subface that doesn't have a full complement of real subface to point to.  //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::dummyinit(int tetwords, int shwords)
+{
+  unsigned long alignptr;
+
+  // Set up 'dummytet', the 'tetrahedron' that occupies "outer space".
+  dummytetbase = (tetrahedron *) new char[tetwords * sizeof(tetrahedron)
+                                          + tetrahedrons->alignbytes];
+  // Align 'dummytet' on a 'tetrahedrons->alignbytes'-byte boundary.
+  alignptr = (unsigned long) dummytetbase;
+  dummytet = (tetrahedron *)
+    (alignptr + (unsigned long) tetrahedrons->alignbytes
+     - (alignptr % (unsigned long) tetrahedrons->alignbytes));
+  // Initialize the four adjoining tetrahedra to be "outer space". These
+  //   will eventually be changed by various bonding operations, but their
+  //   values don't really matter, as long as they can legally be
+  //   dereferenced.
+  dummytet[0] = (tetrahedron) dummytet;
+  dummytet[1] = (tetrahedron) dummytet;
+  dummytet[2] = (tetrahedron) dummytet;
+  dummytet[3] = (tetrahedron) dummytet;
+  // Four null vertex points.
+  dummytet[4] = (tetrahedron) NULL;
+  dummytet[5] = (tetrahedron) NULL;
+  dummytet[6] = (tetrahedron) NULL;
+  dummytet[7] = (tetrahedron) NULL;
+
+  if (b->useshelles) {
+    // Set up 'dummysh', the omnipresent "subface" pointed to by any
+    //   tetrahedron side or subface end that isn't attached to a real
+    //   subface.
+    dummyshbase = (shellface *) new char[shwords * sizeof(shellface)
+                                         + subfaces->alignbytes];
+    // Align 'dummysh' on a 'subfaces->alignbytes'-byte boundary.
+    alignptr = (unsigned long) dummyshbase;
+    dummysh = (shellface *)
+      (alignptr + (unsigned long) subfaces->alignbytes
+       - (alignptr % (unsigned long) subfaces->alignbytes));
+    // Initialize the three adjoining subfaces to be the omnipresent
+    //   subface. These will eventually be changed by various bonding
+    //   operations, but their values don't really matter, as long as they
+    //   can legally be dereferenced.
+    dummysh[0] = (shellface) dummysh;
+    dummysh[1] = (shellface) dummysh;
+    dummysh[2] = (shellface) dummysh;
+    // Three null vertex points.
+    dummysh[3] = (shellface) NULL;
+    dummysh[4] = (shellface) NULL;
+    dummysh[5] = (shellface) NULL;
+    // Initialize the two adjoining tetrahedra to be "outer space".
+    dummysh[6] = (shellface) dummytet;
+    dummysh[7] = (shellface) dummytet;
+    // Initialize the three adjoining subsegments to be "out boundary".
+    dummysh[8]  = (shellface) dummysh;
+    dummysh[9]  = (shellface) dummysh;
+    dummysh[10] = (shellface) dummysh;
+    // Initialize the pointer to badface structure.
+    dummysh[11] = (shellface) NULL;
+    // Initialize the four adjoining subfaces of 'dummytet' to be the
+    //   omnipresent subface.
+    dummytet[8 ] = (tetrahedron) dummysh;
+    dummytet[9 ] = (tetrahedron) dummysh;
+    dummytet[10] = (tetrahedron) dummysh;
+    dummytet[11] = (tetrahedron) dummysh;
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// initializepointpool()    Calculate the size of the point data structure   //
+//                          and initialize its memory pool.                  //
+//                                                                           //
+// This routine also computes the 'pointmarkindex' and 'point2simindex'      //
+// indices used to find values within each point.                            //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::initializepointpool()
+{
+  enum wordtype wtype;
+  int pointsize;
+
+  // The index within each point at which a element pointer is found. Ensure
+  //   the index is aligned to a sizeof(tetrahedron)-byte address.
+  point2simindex = ((3 + in->numberofpointattributes) * sizeof(REAL) +
+                    sizeof(tetrahedron) - 1) / sizeof(tetrahedron);
+  if (b->plc || b->refine) {
+    // Increase the point size by two pointers.  One points to a simplex:
+    //    - a tetrahedron containing it, read by point2tet();
+    //    - a subface containing it, read by point2sh();
+    //    - a (sharp) subsegment it relates, read by point2sh();
+    //    - a (duplicated) point of it, read by point2pt();
+    //    and one points to another point (its parent, used in conforming
+    //    Delaunay meshing algorithm), read by point2ppt().
+    pointsize = (point2simindex + 2) * sizeof(tetrahedron);
+  } else {
+    pointsize = point2simindex * 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 REALs (inidcated by vertexmarkindex) plus:
+  //   - an integer for boundary marker;
+  //   - an integer for vertex type;
+  pointsize = (pointmarkindex + 2) * sizeof(int);
+  // Decide the wordtype used in vertex pool.
+  wtype = (sizeof(REAL) >= sizeof(tetrahedron)) ? FLOATINGPOINT : POINTER;
+  // Initialize the pool of vertices.
+  points = new memorypool(pointsize, VERPERBLOCK, wtype, 0);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// initializetetshpools()    Calculate the sizes of the tetrahedron and      //
+//                           subface data structures and initialize their    //
+//                           memory pools.                                   //
+//                                                                           //
+// This routine also computes the 'highorderindex', 'elemattribindex', and   //
+// 'volumeboundindex' indices used to find values within each tetrahedron.   //
+//                                                                           //
+// There are two types of boundary elements, whihc are subfaces and subsegs, //
+// they are stored in seperate pools. However, the data structures of them   //
+// are the same.  A subsegment can be regarded as a degenerate subface, i.e.,//
+// one of its three corners is not used. We set the apex of it be 'NULL' to  //
+// distinguish it's a subsegment.                                            //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::initializetetshpools()
+{
+  int elesize, shsize;
+  
+  // The number of bytes occupied by a tetrahedron.  There are four pointers
+  //   to other tetrahedra, four pointers to corners, and possibly four
+  //   pointers to subfaces.
+  elesize = (8 + b->useshelles * 4) * sizeof(tetrahedron);
+  // 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 voulme bound is
+  //   found, where the index is measured in REALs.  Note that if the
+  //   `b->regionattrib' flag is set, an additional attribute will be added.
+  volumeboundindex = elemattribindex + in->numberoftetrahedronattributes
+                   + b->regionattrib;
+  // 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 (in->numberoftetrahedronattributes + b->regionattrib > 0) {
+    elesize = volumeboundindex * sizeof(REAL);
+  }
+  // If the high order elements are required (-o2 switch is used), an
+  //   additional pointer pointed to the list of extra nodes is allocated
+  //   for each element.
+  if (b->order == 2) {
+    highorderindex = (elesize + sizeof(int) - 1) / sizeof(int);
+    elesize = (highorderindex + 1) * sizeof(int);
+  }
+  // If element neighbor graph is requested, make sure there's room to
+  //   store an integer index in each element.  This integer index can
+  //   occupy the same space as the subface pointers.
+  if (b->neighout && (elesize <= 8 * sizeof(tetrahedron))) {
+    elesize = 8 * sizeof(tetrahedron) + sizeof(int);
+  }
+  // Having determined the memory size of an element, initialize the pool.
+  tetrahedrons = new memorypool(elesize, ELEPERBLOCK, POINTER, 8);
+
+  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, and one to a badface.
+    shsize = 12 * 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 (b->quality) {
+      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 integers, one for facet marker,
+    //   and one for shellface type.
+    shsize = (shmarkindex + 2) * sizeof(int);
+    // 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, SUBPERBLOCK, POINTER, 8);
+    // Initialize the pool of subsegments. The subsegment's record is same
+    //   with subface.
+    subsegs = new memorypool(shsize, SUBPERBLOCK, POINTER, 8);
+    // Initialize the "outer space" tetrahedron and omnipresent subface.
+    dummyinit(tetrahedrons->itemwords, subfaces->itemwords);
+  } else {
+    // Initialize the "outer space" tetrahedron.
+    dummyinit(tetrahedrons->itemwords, 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;
+  dyingtetrahedron[5] = (tetrahedron) NULL;
+  dyingtetrahedron[6] = (tetrahedron) NULL;
+  dyingtetrahedron[7] = (tetrahedron) NULL;
+  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[7] == (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;
+  dyingsh[4] = (shellface) NULL;
+  dyingsh[5] = (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;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// badfacedealloc()    Deallocate space for a badface, marking it dead.      //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::badfacedealloc(memorypool *pool, badface *dying)
+{
+  // Set badface's forg to NULL. This makes it possible to detect dead
+  //   ones when traversing the list of all items.
+  dying->forg = (point) NULL;
+  pool->dealloc((void *) dying);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// badfacetraverse()    Traverse the pools, skipping dead ones.              //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+tetgenmesh::badface* tetgenmesh::badfacetraverse(memorypool *pool)
+{
+  badface *newsh;
+
+  do {
+    newsh = (badface *) pool->traverse();
+    if (newsh == (badface *) NULL) {
+      return (badface *) NULL;
+    }
+  } while (newsh->forg == (point) NULL);               // Skip dead ones.
+  return newsh;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// 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] = (tetrahedron) dummytet;
+  newtet->tet[1] = (tetrahedron) dummytet;
+  newtet->tet[2] = (tetrahedron) dummytet;
+  newtet->tet[3] = (tetrahedron) dummytet;
+  // Four NULL vertices.
+  newtet->tet[4] = (tetrahedron) NULL;
+  newtet->tet[5] = (tetrahedron) NULL;
+  newtet->tet[6] = (tetrahedron) NULL;
+  newtet->tet[7] = (tetrahedron) NULL;
+  // Initialize the four adjoining subfaces to be the omnipresent subface.
+  if (b->useshelles) {
+    newtet->tet[8 ] = (tetrahedron) dummysh;
+    newtet->tet[9 ] = (tetrahedron) dummysh;
+    newtet->tet[10] = (tetrahedron) dummysh;
+    newtet->tet[11] = (tetrahedron) dummysh;
+  }
+  for (int i = 0; i < in->numberoftetrahedronattributes; i++) {
+    setelemattribute(newtet->tet, i, 0.0);
+  }
+  if (b->varvolume) {
+    setvolumebound(newtet->tet, -1.0);
+  }
+  // Initialize the location and version to be Zero.
+  newtet->loc = 0;
+  newtet->ver = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// makeshellface()    Create a new shellface with version zero. Used for     //
+//                    both subfaces and seusegments.                         //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::makeshellface(memorypool *pool, face *newface)
+{
+  newface->sh = (shellface *) pool->alloc();
+  //Initialize the three adjoining subfaces to be the omnipresent subface.
+  newface->sh[0] = (shellface) dummysh;
+  newface->sh[1] = (shellface) dummysh;
+  newface->sh[2] = (shellface) dummysh;
+  // Three NULL vertices.
+  newface->sh[3] = (shellface) NULL;
+  newface->sh[4] = (shellface) NULL;
+  newface->sh[5] = (shellface) NULL;
+  // Initialize the two adjoining tetrahedra to be "outer space".
+  newface->sh[6] = (shellface) dummytet;
+  newface->sh[7] = (shellface) dummytet;
+  // Initialize the three adjoining subsegments to be the omnipresent
+  //   subsegments.
+  newface->sh [8] = (shellface) dummysh;
+  newface->sh [9] = (shellface) dummysh;
+  newface->sh[10] = (shellface) dummysh;
+  // Initialize the pointer to badface structure.
+  newface->sh[11] = (shellface) NULL;
+  if (b->quality) {
+    // Initialize the maximum area bound.
+    setareabound(*newface, 0.0);
+  }
+  // Set the boundary marker to zero.
+  setshellmark(*newface, 0);
+  // Set the type be NONPROTSUBFACE.
+  setshelltype(*newface, NONPROTSUBFACE);
+  // Initialize the version to be Zero.
+  newface->shver = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// makepoint()    Create a new point.                                        //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::makepoint(point* pnewpoint)
+{
+  int ptmark, i;
+
+  *pnewpoint = (point) points->alloc();
+  // Initialize three coordinates.
+  (*pnewpoint)[0] = 0.0;
+  (*pnewpoint)[1] = 0.0;
+  (*pnewpoint)[2] = 0.0;
+  // Initialize the list of user-defined attributes.
+  for (i = 0; i < in->numberofpointattributes; i++) {
+    (*pnewpoint)[3 + i] = 0.0;
+  }
+  if (b->plc || b->refine) {
+    // Initialize the point-to-tetrahedron filed.
+    setpoint2tet(*pnewpoint, NULL);
+    // Initialize the other pointer to its parent point.
+    setpoint2ppt(*pnewpoint, NULL);
+  }
+  // Initialize the point marker (starting from in->firstnumber).
+  ptmark = (int) points->items - (in->firstnumber == 1 ? 0 : 1);
+  setpointmark(*pnewpoint, ptmark);
+  // Initialize the point type be UNUSEDVERTEX.
+  setpointtype(*pnewpoint, UNUSEDVERTEX);
+}
+
+//
+// End of memory management routines
+//
+
+//
+// Begin of point location routines
+//
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// randomnation()    Generate a random number between 0 and 'choices' - 1.   //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+unsigned long tetgenmesh::randomnation(unsigned int choices)
+{
+  randomseed = (randomseed * 1366l + 150889l) % 714025l;
+  return randomseed / (714025l / choices + 1);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// distance2()    Returns the square "distance" of a tetrahedron to point p. //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+REAL tetgenmesh::distance2(tetrahedron* tetptr, point p)
+{
+  point p1, p2, p3, p4;
+  REAL dx, dy, dz;
+
+  p1 = (point) tetptr[4];
+  p2 = (point) tetptr[5];
+  p3 = (point) tetptr[6];
+  p4 = (point) tetptr[7];
+
+  dx = p[0] - 0.25 * (p1[0] + p2[0] + p3[0] + p4[0]);
+  dy = p[1] - 0.25 * (p1[1] + p2[1] + p3[1] + p4[1]);
+  dz = p[2] - 0.25 * (p1[2] + p2[2] + p3[2] + p4[2]);
+
+  return dx * dx + dy * dy + dz * dz;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// preciselocate()    Find a simplex containing a given point.               //
+//                                                                           //
+// This routine implements the simple Walk-through point location algorithm. //
+// Begins its search from 'searchtet', assume there is a line segment L from //
+// a vertex of 'searchtet' to the query point 'searchpoint', and simply walk //
+// towards 'searchpoint' by traversing all faces intersected by L.           //
+//                                                                           //
+// On completion, 'searchtet' is a tetrahedron that contains 'searchpoint'.  //
+// The returned value indicates one of the following cases:                  //
+//   - Returns ONVERTEX if the point lies on an existing vertex. 'searchtet' //
+//     is a handle whose origin is the existing vertex.                      //
+//   - Returns ONEDGE if the point lies on a mesh edge.  'searchtet' is a    //
+//     handle whose primary edge is the edge on which the point lies.        //
+//   - Returns ONFACE if the point lies strictly within a face. 'searchtet'  //
+//     is a handle whose primary face is the face on which the point lies.   //
+//   - Returns INTETRAHEDRON if the point lies strictly in a tetrahededron.  //
+//     'searchtet' is a handle on the tetrahedron that contains the point.   //
+//   - Returns OUTSIDE if the point lies outside the mesh. 'searchtet' is a  //
+//     handle whose location is the face the point is to 'above' of.         //
+//                                                                           //
+// 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::
+preciselocate(point searchpoint, triface* searchtet)
+{
+  triface backtracetet;
+  triface walkthroface;
+  point forg, fdest, fapex, toppo;
+  REAL ori1, ori2, ori3, ori4;
+  long tetnumber;
+  int side;
+
+  // 'searchtet' should be a valid tetrahedron.
+  if (searchtet->tet == dummytet) {
+    symself(*searchtet);
+    assert(searchtet->tet != dummytet);
+  }
+  assert(!isdead(searchtet));
+
+  searchtet->ver = 0; // Keep in CCW edge ring.
+  // Find a face of 'searchtet' such that the 'searchpoint' lies strictly
+  //   above it.  Such face should always exist.
+  for (searchtet->loc = 0; searchtet->loc < 4; searchtet->loc++) {
+    forg = org(*searchtet);
+    fdest = dest(*searchtet);
+    fapex = apex(*searchtet);
+    ori1 = orient3d(forg, fdest, fapex, searchpoint);
+    if (ori1 < 0.0) break;
+  }
+  assert(searchtet->loc < 4);
+
+  // Define 'tetnumber' for exit the loop when it's running endless.
+  tetnumber = 0l;
+  while (tetnumber <= tetrahedrons->items) {
+    // Check if we are reaching the boundary of the triangulation.
+    if (searchtet->tet == dummytet) {
+      *searchtet = backtracetet;
+      return OUTSIDE;
+    }
+    // Initialize the face for returning the walk-through face.
+    walkthroface.tet = (tetrahedron *) NULL;
+    // Adjust the edge ring, so that 'ori1 < 0.0' holds.
+    searchtet->ver = 0;
+    // 'toppo' remains unchange for the following orientation tests.
+    toppo = oppo(*searchtet);
+    // Check the three sides of 'searchtet' to find the face through which
+    //   we can walk next.
+    for (side = 0; side < 3; side++) {
+      forg = org(*searchtet);
+      fdest = dest(*searchtet);
+      ori2 = orient3d(forg, fdest, toppo, searchpoint);
+      if (ori2 == 0.0) {
+        // They are coplanar, check if 'searchpoint' lies inside, or on an
+        //   edge, or coindice with a vertex of face (forg, fdest, toppo). 
+        fapex = apex(*searchtet);
+        ori3 = orient3d(fdest, fapex, toppo, searchpoint);
+        if (ori3 < 0.0) {
+          // Outside the face (fdest, fapex, toppo), walk through it.
+          enextself(*searchtet);
+          fnext(*searchtet, walkthroface);
+          break;
+        }
+        ori4 = orient3d(fapex, forg, toppo, searchpoint);
+        if (ori4 < 0.0) {
+          // Outside the face (fapex, forg, toppo), walk through it.
+          enext2self(*searchtet);
+          fnext(*searchtet, walkthroface);
+          break;
+        }
+        // Remember, ori1 < 0.0, which means 'searchpoint' will not
+        //   on edge (forg, fdest) or on vertex forg or fdest.
+        assert(ori1 < 0.0);
+        // The rest possible cases are: 
+        //   (1) 'searchpoint' lies on edge (fdest, toppo);
+        //   (2) 'searchpoint' lies on edge (toppo, forg);
+        //   (3) 'searchpoint' coincident with toppo;
+        //   (4) 'searchpoint' lies inside face (forg, fdest, toppo).
+        fnextself(*searchtet);
+        if (ori3 == 0.0) {
+          if (ori4 == 0.0) {
+            // Case (4).
+            enext2self(*searchtet);
+            return ONVERTEX;
+          } else {
+            // Case (1).
+            enextself(*searchtet);
+            return ONEDGE;
+          }
+        }
+        if (ori4 == 0.0) {
+          // Case (2).
+          enext2self(*searchtet);
+          return ONEDGE;
+        }
+        // Case (4).
+        return ONFACE;
+      } else if (ori2 < 0.0) {
+        // Outside the face (forg, fdest, toppo), walk through it.
+        fnext(*searchtet, walkthroface);
+        break;
+      }
+      // Go to check next side.
+      enextself(*searchtet);
+    }
+    if (side >= 3) {
+      // Found! Inside tetrahedron.
+      return INTETRAHEDRON;
+    }
+    // We walk through the face 'walkthroface' and continue the searching.
+    assert(walkthroface.tet != (tetrahedron *) NULL);
+    // Store the face handle in 'backtracetet' before we take the real walk.
+    //   So we are able to restore the handle to 'searchtet' if we are
+    //   reaching the outer boundary.
+    backtracetet = walkthroface;
+    sym(walkthroface, *searchtet);    
+    tetnumber++;
+  }
+
+  // Should never be here.
+  printf("Internal error in preciselocate(): Point location failed.\n");
+  internalerror();
+  return OUTSIDE;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// locate()    Find a simplex containing a given point.                      //
+//                                                                           //
+// This routine implements Muecke's Jump-and-walk point location algorithm.  //
+// It improves the simple walk-through by "jumping" to a good starting point //
+// via random sampling.  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 barycenter is closest to the point we are searcing for.  Having chosen //
+// the starting tetrahedron, the simple Walk-through algorithm is used to do //
+// the real walking.                                                         //
+//                                                                           //
+// On completion, 'searchtet' is a tetrahedron that contains 'searchpoint'.  //
+// The returned value indicates one of the following cases:                  //
+//   - Returns ONVERTEX if the point lies on an existing vertex. 'searchtet' //
+//     is a handle whose origin is the existing vertex.                      //
+//   - Returns ONEDGE if the point lies on a mesh edge.  'searchtet' is a    //
+//     handle whose primary edge is the edge on which the point lies.        //
+//   - Returns ONFACE if the point lies strictly within a face. 'searchtet'  //
+//     is a handle whose primary face is the face on which the point lies.   //
+//   - Returns INTETRAHEDRON if the point lies strictly in a tetrahededron.  //
+//     'searchtet' is a handle on the tetrahedron that contains the point.   //
+//   - Returns OUTSIDE if the point lies outside the mesh. 'searchtet' is a  //
+//     handle whose location is the face the point is to 'above' of.         //
+//                                                                           //
+// 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 searchpoint, triface *searchtet)
+{
+  tetrahedron *firsttet, *tetptr;
+  void **sampleblock;
+  long sampleblocks, samplesperblock, samplenum;
+  long tetblocks, i, j;
+  unsigned long alignptr;
+  REAL searchdist, dist;
+
+  // 'searchtet' should be a valid tetrahedron.
+  if (isdead(searchtet)) {
+    searchtet->tet = dummytet;
+  }
+  if (searchtet->tet == dummytet) {
+    // This is an 'Outer Space' handle, get a hull tetrahedron.
+    searchtet->loc = 0;
+    symself(*searchtet);
+  }
+  assert(!isdead(searchtet));
+  
+  // Record the distance from the suggested starting tetrahedron to the
+  //   point we seek.
+  searchdist = distance2(searchtet->tet, searchpoint);
+
+  // If a recently encountered tetrahedron has been recorded and has not
+  //   been deallocated, test it as a good starting point.
+  if (!isdead(&recenttet) && (recenttet.tet != searchtet->tet)) {
+    dist = distance2(recenttet.tet, searchpoint);
+    if (dist < searchdist) {
+      *searchtet = recenttet;
+      searchdist = dist;
+    }
+  }
+
+  // Select "good" candidate using k random samples, taking the closest one.
+  //   The number of random samples taken is proportional to the cube root
+  //   of the number of tetrahedra in the mesh. The next bit of code assumes
+  //   that the number of tetrahedra increases monotonically.
+  while (SAMPLEFACTOR * samples * samples * samples < tetrahedrons->items) {
+    samples++;
+  }
+  // Find how much blocks in current tet pool.
+  tetblocks = (tetrahedrons->maxitems + ELEPERBLOCK - 1) / ELEPERBLOCK;
+  // Find the average samles 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 = (unsigned long) (sampleblock + 1);
+    firsttet = (tetrahedron *)
+               (alignptr + (unsigned long) tetrahedrons->alignbytes
+               - (alignptr % (unsigned long) tetrahedrons->alignbytes));
+    for (j = 0; j < samplesperblock; j++) {
+      if (i == tetblocks - 1) {
+        // This is the last block.
+        samplenum = randomnation((int)
+                      (tetrahedrons->maxitems - (i * ELEPERBLOCK)));
+      } else {
+        samplenum = randomnation(ELEPERBLOCK);
+      }
+      tetptr = (tetrahedron *)
+               (firsttet + (samplenum * tetrahedrons->itemwords));
+      if (tetptr[4] != (tetrahedron) NULL) {
+        dist = distance2(tetptr, searchpoint);
+        if (dist < searchdist) {
+          searchtet->tet = tetptr;
+          searchdist = dist;
+        }
+      }
+    }
+    sampleblock = (void **) *sampleblock;
+  }
+  
+  // Call simple walk-through to locate the point.
+  return preciselocate(searchpoint, searchtet); 
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// adjustlocate()    Adjust the precise location of a vertex with respect to //
+//                   a given tetrahedron using a given relative tolerance.   //
+//                                                                           //
+// 'precise' is the precise location (returned from preciselocate()) of the  //
+// point 'searchpoint' with respect to the tetrahedron 'searchtet'.  'epspp' //
+// is the given relative tolerance.                                          //
+//                                                                           //
+// This routine reevaluates the orientations of searchpoint with respect to  //
+// the four faces of searchtet. Detects the coplanarities by additinal tests //
+// which are based on the given tolerance. If 'precise' is ONFACE or ONEDGE, //
+// we can save one or two orientation tests.                                 //
+//                                                                           //
+// On completion, 'searchtet' is a tetrahedron that contains 'searchpoint'.  //
+// The returned value indicates one of the following cases:                  //
+//   - Returns ONVERTEX if the point lies on an existing vertex. 'searchtet' //
+//     is a handle whose origin is the existing vertex.                      //
+//   - Returns ONEDGE if the point lies on a mesh edge.  'searchtet' is a    //
+//     handle whose primary edge is the edge on which the point lies.        //
+//   - Returns ONFACE if the point lies strictly within a face. 'searchtet'  //
+//     is a handle whose primary face is the face on which the point lies.   //
+//   - Returns INTETRAHEDRON if the point lies strictly in a tetrahededron.  //
+//     'searchtet' is a handle on the tetrahedron that contains the point.   //
+//   - Returns OUTSIDE if the point lies outside the mesh. 'searchtet' is a  //
+//     handle whose location is the face the point is to 'above' of.         //
+//                                                                           //
+// WARNING:  This routine detect degenerate case using relative tolerance.   //
+// It is better used after locate() or preciselocate().  For general inputs, //
+// it may not able to tell the correct location.                             //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+enum tetgenmesh::locateresult tetgenmesh::
+adjustlocate(point searchpoint, triface* searchtet, enum locateresult precise,
+             REAL epspp)
+{
+  point torg, tdest, tapex, toppo;
+  REAL s1, s2, s3, s4;
+
+  // For the given 'searchtet', the orientations tests are:
+  //  s1: (tdest, torg, tapex, searchpoint);
+  //  s2: (torg, tdest, toppo, searchpoint);
+  //  s3: (tdest, tapex, toppo, searchpoint);
+  //  s4: (tapex, torg, toppo, searchpoint);
+  adjustedgering(*searchtet, CCW);
+  torg = org(*searchtet);
+  tdest = dest(*searchtet);
+  tapex = apex(*searchtet);
+  toppo = oppo(*searchtet);
+
+  switch (precise) {
+  case ONVERTEX:
+    // This case we don't need do any further test.
+    return ONVERTEX;
+  case ONEDGE:
+    // (torg, tdest);
+    s1 = 0.0;
+    s2 = 0.0;
+    break;
+  case ONFACE:
+    // (tdest, torg, tapex);
+    s1 = 0.0;
+    s2 = orient3d(torg, tdest, toppo, searchpoint);
+    break;
+  default: // INTETRAHEDRON or OUTSIDE
+    s1 = orient3d(tdest, torg, tapex, searchpoint);
+    s2 = orient3d(torg, tdest, toppo, searchpoint);
+  }
+  
+  if (s1 != 0.0) {
+    if (iscoplanar(tdest, torg, tapex, searchpoint, s1, epspp)) {
+      s1 = 0.0;
+    }
+  }
+  if (s1 < 0.0) {
+    return OUTSIDE;
+  }
+
+  if (s2 != 0.0) {
+    if (iscoplanar(torg, tdest, toppo, searchpoint, s2, epspp)) {
+      s2 = 0.0;
+    }
+  }
+  if (s2 < 0.0) {
+    fnextself(*searchtet);
+    return OUTSIDE;
+  }
+
+  s3 = orient3d(tdest, tapex, toppo, searchpoint);
+  if (s3 != 0.0) {
+    if (iscoplanar(tdest, tapex, toppo, searchpoint, s3, epspp)) {
+      s3 = 0.0;
+    }
+  }
+  if (s3 < 0.0) {
+    enextfnextself(*searchtet);
+    return OUTSIDE;
+  }
+
+  s4 = orient3d(tapex, torg, toppo, searchpoint);
+  if (s4 != 0.0) {
+    if (iscoplanar(tapex, torg, toppo, searchpoint, s4, epspp)) {
+      s4 = 0.0;
+    }
+  }
+  if (s4 < 0.0) {
+    enext2fnextself(*searchtet);
+    return OUTSIDE;
+  }
+
+  // Determine degenerate cases.
+  if (s1 == 0.0) {
+    if (s2 == 0.0) {
+      if (s3 == 0.0) {
+        // On tdest.
+        enextself(*searchtet);
+        return ONVERTEX;
+      }
+      if (s4 == 0.0) {
+        // On torg.
+        return ONVERTEX;
+      }
+      // On edge (torg, tdest).
+      return ONEDGE;
+    }
+    if (s3 == 0.0) {
+      if (s4 == 0.0) {
+        // On tapex.
+        enext2self(*searchtet);
+        return ONVERTEX;
+      }
+      // On edge (tdest, tapex).
+      enextself(*searchtet);
+      return ONEDGE;
+    }
+    if (s4 == 0.0) {
+      // On edge (tapex, torg).
+      enext2self(*searchtet);
+      return ONEDGE;
+    }
+    // On face (torg, tdest, tapex).
+    return ONFACE;
+  }
+  if (s2 == 0.0) {
+    fnextself(*searchtet);
+    if (s3 == 0.0) {
+      if (s4 == 0.0) {
+        // On toppo.
+        enext2self(*searchtet);
+        return ONVERTEX;
+      }
+      // On edge (tdest, toppo).
+      enextself(*searchtet);
+      return ONEDGE;
+    }
+    if (s4 == 0.0) {
+      // On edge (toppo, torg).
+      enext2self(*searchtet);
+      return ONEDGE;
+    }
+    // On face (torg, tdest, toppo).
+    return ONFACE;
+  }
+  if (s3 == 0.0) {
+    enextfnextself(*searchtet);
+    if (s4 == 0.0) {
+      // On edge (tapex, toppo).
+      enextself(*searchtet);
+      return ONEDGE;
+    }
+    // On face (tdest, tapex, toppo).
+    return ONFACE;
+  }
+  if (s4 == 0.0) {
+    enext2fnextself(*searchtet);
+    // On face (tapex, torg, toppo).
+    return ONFACE;
+  }
+
+  // Inside tetrahedron.
+  return INTETRAHEDRON;
+}
+
+//
+// End of point location routines
+//
+
+//
+// Begin of mesh transformation routines
+//
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// categorizeface()    Determine the flip type of a given face.              //
+//                                                                           //
+// On input, 'horiz' represents the face we want to flip (you can imagine it //
+// is parallel to the horizon).  Let the tetrahedron above it be abcd, where //
+// abc is 'horiz'.                                                           //
+//                                                                           //
+// If abc is a hull face, it is unflipable, and is locally Delaunay.  In the //
+// following, we assume abc is an interior face, and the other tetrahedron   //
+// adjoining at abc is bace.                                                 //
+//                                                                           //
+// If the convex hull CH of the set {a, b, c, d, e} only has four vertices,  //
+// i.e., one vertex lies inside CH, then abc is unflipable, and is locally   //
+// Delaunay. If CH is the vertex set itself, we have the following cases to  //
+// determine whether abc is flipable or not.                                 //
+//                                                                           //
+// If no four points of {a, b, c, d, e} are coplanar, a 2-to-3 flip can be   //
+// applied to abc if the edge de crosses the triangle abc; a 3-to-2 flip can //
+// be applied to abc if ab crosses cde, and abde exists, otherwise, face abc //
+// is unflipable, i.e., the tetrahedron abde is not present.                 //
+//                                                                           //
+// If four points of {a, b, c, d, e} are coplanar (two faces are coplanar).  //
+// Assume faces abd and abe are coplanar (it is impossible be abc). If a, b, //
+// d, e form a non-convex quadrilateral, then abc is unflipable, furthermore,//
+// it is locally Delaunay.  Assume they are convex quadrilateral, if abd and //
+// abe are hull faces, a 2-to-2 flip can be applied to abc;  if abd and abe  //
+// are interior faces,  assume two tetrahedra adjoining abd and abe at the   //
+// opposite sides are abdg and abef, respectively.  If g = f, a 4-to-4 flip  //
+// can be applied to abc, otherwise, abc is unflipable.                      //
+//                                                                           //
+// There are other cases which can cause abc unflipable. If abc is a subface,//
+// a 2-to-3 flip is forbidden;  if ab is a subsegment, flips 3-to-2, 2-to-2, //
+// and 4-to-4 are forbidden.                                                 //
+//                                                                           //
+// This routine determines the suitable type of flip operation for 'horiz'.  //
+//   - Returns T23 if a 2-to-3 flip is applicable. 'horiz' is same as input. //
+//   - Returns T32 if a 3-to-2 flip is applicable. 'horiz' is adjusted so    //
+//     that the primary edge of 'horiz' is the flipable edge.                //
+//   - Returns T22 if a 2-to-2 or 4-to-4 flip is applicable.  'horiz' is     //
+//     adjusted so that the primary edge of 'horiz' is the flipable edge.    //
+//   - Returns FORBIDDENFACE indicates although a 2-to-3 flip is applicable, //
+//     but it is a subface and should not be flipped away.                   //
+//   - Returns FORBIDDENEDGE indicates although a 3-to-2, or 2-to-2, or      //
+//     4-to-4 flip is applicable, but the flipable edge is a subsegment and  //
+//     should not be flipped away.  'horiz' is adjusted so that the primary  //
+//     edge of 'horiz' is the flipable edge.                                 //
+//   - Returns UNFLIPABLE indicates it is unflipable due to the absence of   //
+//     a tetrahedron. 'horiz' is adjusted so that the primary edge of 'horiz'//
+//     is the unflipable edge. Possibly, It is a subsegment.                 //
+//   - Returns NONCONVEX indicates it is unflipable and is locally Delaunay. //
+//                                                                           //
+// Given a face abc, with two adjoining tetrahedra abcd and bace.  If abc is //
+// flipable, i.e., T23, T32, T22 or T44, its flip type can be determined by  //
+// doing five orientation tests: two tests for determining that d, e lie on  //
+// the different sides of abc, three tests for determining if the edge de    //
+// intersects the face abc.  However, if we use the neighbor information of  //
+// the mesh data structure, we can reduce the five orientation tests to at   //
+// most three tests, that is, the two tests for determining whether d and e  //
+// lie on the different sides of abc can be saved.                           //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+enum tetgenmesh::fliptype tetgenmesh::categorizeface(triface& horiz)
+{
+  triface symhoriz, casing;
+  face checksh, checkseg;
+  face cassh1, cassh2;
+  point pa, pb, pc, pd, pe, pf, pg;
+  point abdoppo, bcdoppo, cadoppo;
+  REAL ori1, ori2, ori3;
+  int adjtet;
+
+  sym(horiz, symhoriz);
+  if (symhoriz.tet == dummytet) {
+    // A hull face is unflipable and locally Delaunay.
+    return NONCONVEX;
+  }
+  
+  adjustedgering(horiz, CCW);
+  findedge(&symhoriz, dest(horiz), org(horiz));
+  pa = org(horiz);
+  pb = dest(horiz);
+  pc = apex(horiz);
+  pd = oppo(horiz);
+  pe = oppo(symhoriz);
+
+  // Find the number of adjacent tetrahedra of abc, which have d, e, and one
+  //   of corners of abc as their corners. This number can be 0, 1 and 2.
+  abdoppo = bcdoppo = cadoppo = (point) NULL;
+  adjtet = 0;
+  fnext(horiz, casing); // at edge 'ab'.
+  symself(casing);
+  if (casing.tet != dummytet) {
+    abdoppo = oppo(casing);
+    if (abdoppo == pe) adjtet++;
+  }
+  enextfnext(horiz, casing); // at edge 'bc'.
+  symself(casing);
+  if (casing.tet != dummytet) {
+    bcdoppo = oppo(casing);
+    if (bcdoppo == pe) adjtet++;
+  }
+  enext2fnext(horiz, casing); // at edge 'ca'.
+  symself(casing);
+  if (casing.tet != dummytet) {
+    cadoppo = oppo(casing);
+    if (cadoppo == pe) adjtet++;
+  }
+  
+  if (adjtet == 0) {
+    // No adjacent tetrahedron. Types T23, T22 and T44 are possible. 
+    ori1 = orient3d(pa, pb, pd, pe);
+    if (checksubfaces && ori1 != 0.0) {
+      // Are abd and abe subfaces and belong to the same facet?
+      fnext(horiz, casing);
+      tspivot(casing, cassh1);
+      fnext(symhoriz, casing);
+      tspivot(casing, cassh2);
+      if (cassh1.sh != dummysh && cassh2.sh != dummysh) {
+        // Two adjoining boundary faces. If the common edge of them is not
+        //   a subsegment, they belong to the same facet.
+        findedge(&cassh1, pa, pb);
+        sspivot(cassh1, checkseg);
+        if (checkseg.sh == dummysh) {
+          // The four points are forced to be coplanar.
+          ori1 = 0.0;
+        }
+      }
+      if (ori1 != 0.0) {
+        // Check if abd and bae are approximately coplanar.
+        if (iscoplanar(pa, pb, pd, pe, ori1, b->epsilon)) ori1 = 0.0;
+      }
+    }
+    if (ori1 < 0.0) {
+      // e lies above abd, unflipable, tet abde is not present.
+#ifdef SELF_CHECK
+      if (!nonconvex) {
+        // abd and abe should not be hull faces, check it.
+        fnext(horiz, casing);
+        symself(casing);
+        assert(casing.tet != dummytet);
+        fnext(symhoriz, casing);
+        symself(casing);
+        assert(casing.tet != dummytet);
+      }
+#endif
+      if (checksubfaces) {
+        // The nonconvexbility may be casued by existing an subsegment.
+        tsspivot(&horiz, &checkseg);
+        if (checkseg.sh != dummysh) {
+          return FORBIDDENEDGE;
+        }
+      }
+      return UNFLIPABLE;
+    }
+    ori2 = orient3d(pb, pc, pd, pe);
+    if (checksubfaces && ori2 != 0.0) {
+      // Are bcd and cbe subfaces and belong to the same facet?
+      enextfnext(horiz, casing);
+      tspivot(casing, cassh1);
+      enext2fnext(symhoriz, casing);
+      tspivot(casing, cassh2);
+      if (cassh1.sh != dummysh && cassh2.sh != dummysh) {
+        // Two adjoining boundary faces. If the common edge of them is not
+        //   a subsegment, they belong to the same facet.
+        findedge(&cassh1, pb, pc);
+        sspivot(cassh1, checkseg);
+        if (checkseg.sh == dummysh) {
+          // The four points are forced to be coplanar.
+          ori2 = 0.0;
+        } 
+      }
+      if (ori2 != 0.0) {
+        // Check if bcd and cbe are approximately coplanar.
+        if (iscoplanar(pb, pc, pd, pe, ori2, b->epsilon)) ori2 = 0.0;
+      }
+    }
+    if (ori2 < 0.0) {
+      // e lies above bcd, unflipable, tet bcde is not present.
+#ifdef SELF_CHECK
+      if (!nonconvex) {
+        // bcd and cbe should not be hull faces, check it.
+        enextfnext(horiz, casing);
+        symself(casing);
+        assert(casing.tet != dummytet);
+        enext2fnext(symhoriz, casing);
+        symself(casing);
+        assert(casing.tet != dummytet);
+      }
+#endif
+      enextself(horiz);
+      if (checksubfaces) {
+        // The nonconvexbility may be casued by existing an subsegment.
+        tsspivot(&horiz, &checkseg);
+        if (checkseg.sh != dummysh) {
+          return FORBIDDENEDGE;
+        }
+      }
+      return UNFLIPABLE;
+    } 
+    ori3 = orient3d(pc, pa, pd, pe);
+    if (checksubfaces && ori3 != 0.0) {
+      // Are cad and ace subfaces and belong to the same facet?
+      enext2fnext(horiz, casing);
+      tspivot(casing, cassh1);
+      enextfnext(symhoriz, casing);
+      tspivot(casing, cassh2);
+      if (cassh1.sh != dummysh && cassh2.sh != dummysh) {
+        // Two adjoining boundary faces. If the common edge of them is not
+        //   a subsegment, they belong to the same facet.
+        findedge(&cassh1, pc, pa);
+        sspivot(cassh1, checkseg);
+        if (checkseg.sh == dummysh) {
+          // The four points are forced to be coplanar.
+          ori3 = 0.0;
+        }
+      }
+      if (ori3 != 0.0) {
+        // Check if cad and ace are approximately coplanar.
+        if (iscoplanar(pc, pa, pd, pe, ori3, b->epsilon)) ori3 = 0.0;
+      }
+    }
+    if (ori3 < 0.0) {
+      // e lies above cad, unflipable, tet cade is not present.
+#ifdef SELF_CHECK
+      if (!nonconvex) {
+        // cad and ace should not be hull faces, check it.
+        enext2fnext(horiz, casing);
+        symself(casing);
+        assert(casing.tet != dummytet);
+        enextfnext(symhoriz, casing);
+        symself(casing);
+        assert(casing.tet != dummytet);
+      }
+#endif
+      enext2self(horiz);
+      if (checksubfaces) {
+        // The nonconvexbility may be casued by existing an subsegment.
+        tsspivot(&horiz, &checkseg);
+        if (checkseg.sh != dummysh) {
+          return FORBIDDENEDGE;
+        }
+      }
+      return UNFLIPABLE;
+    }
+    if (ori1 == 0.0) {
+      // e is coplanar with abd.
+      if (ori2 * ori3 == 0.0) {
+        // only one zero is possible.
+        // assert(!(ori2 == 0.0 && ori3 == 0.0));
+        // Three points (d, e, and a or b) are collinear, abc is unflipable
+        //   and locally Delaunay.
+        return NONCONVEX;
+      }
+    } else if (ori2 == 0.0) {
+      // e is coplanar with bcd.
+      if (ori1 * ori3 == 0.0) {
+        // only one zero is possible.
+        // assert(!(ori1 == 0.0 && ori3 == 0.0));
+        // Three points (d, e, and b or c) are collinear, abc is unflipable
+        //   and locally Delaunay.
+        return NONCONVEX;
+      }
+      // Adjust 'horiz' and 'symhoriz' be the edge bc.
+      enextself(horiz);
+      enext2self(symhoriz);
+    } else if (ori3 == 0.0) {
+      // e is coplanar with cad.
+      if (ori1 * ori2 == 0.0) {
+        // only one zero is possible.
+        // assert(!(ori1 == 0.0 && ori2 == 0.0));
+        // Three points (d, e, and c or a) are collinear, abc is unflipable
+        //   and locally Delaunay.
+        return NONCONVEX;
+      }
+      // Adjust 'horiz' and 'symhoriz' be the edge ca.
+      enext2self(horiz);
+      enextself(symhoriz);
+    } else {
+      // e lies below all three faces, flipable.
+      if (checksubfaces) {
+        tspivot(horiz, checksh);
+        if (checksh.sh != dummysh) {
+          // To flip a subface is forbidden.
+          return FORBIDDENFACE;
+        }
+      }
+      return T23;
+    }
+    // Four points are coplanar, T22 or T44 is possible.
+    if (checksubfaces) {
+      tsspivot(&horiz, &checkseg);
+      if (checkseg.sh != dummysh) {
+        // To flip a subsegment is forbidden.
+        return FORBIDDENEDGE;
+      }
+      tspivot(horiz, checksh);
+      if (checksh.sh != dummysh) {
+        // To flip a subface is forbidden.
+        return FORBIDDENFACE;
+      }
+    }
+    // Assume the four coplanar points are a, b, d, e, abd and abe are two
+    //   coplanar faces. If both abd and abe are hull faces, flipable(T22).
+    //   If they are interior faces, get the opposite tetrahedra abdf and
+    //   abeg, if f = g, flipable (T44). Otherwise, unflipable.
+    pf = pg = (point) NULL;
+    fnext(horiz, casing);
+    symself(casing);
+    if (casing.tet != dummytet) {
+      pf = oppo(casing);
+    }
+    fnext(symhoriz, casing);
+    symself(casing);
+    if (casing.tet != dummytet) {
+      pg = oppo(casing);
+    }
+    if (pf == (point) NULL && pg == (point) NULL) {
+      // abd and abe are hull faces, flipable.
+      return T22;
+    } else if (pf == pg) {
+      // abd and abe are interior faces, flipable.
+      return T44;
+    } else {
+      // ab has more than four faces around it, unflipable.
+      return UNFLIPABLE;
+    }
+  } else if (adjtet == 1) {
+    // One of its three edges is locally non-convex. Type T32 is possible.
+    // Adjust current configuration so that edge ab is non-convex.
+    if (bcdoppo == pe) {
+      // Edge bc is non-convex. Adjust 'horiz' and 'symhoriz' be edge bc.
+      enextself(horiz);
+      enext2self(symhoriz);
+      pa = org(horiz);
+      pb = dest(horiz);
+      pc = apex(horiz);
+    } else if (cadoppo == pe) {
+      // Edge ca is non-convex. Adjust 'horiz' and 'symhoriz' be edge ca.
+      enext2self(horiz);
+      enextself(symhoriz);
+      pa = org(horiz);
+      pb = dest(horiz);
+      pc = apex(horiz);
+    } else {
+      // Edge ab is non-convex.
+      assert(abdoppo == pe);
+    } // Now ab is the non-convex edge.
+    // In order to be flipable, ab should cross face cde. Check it.
+    ori1 = orient3d(pc, pd, pe, pa);
+    if (checksubfaces && ori1 != 0.0) {
+      // Are cad and ace subfaces and belong to the same facet?
+      enext2fnext(horiz, casing);
+      tspivot(casing, cassh1);
+      enextfnext(symhoriz, casing);
+      tspivot(casing, cassh2);
+      if (cassh1.sh != dummysh && cassh2.sh != dummysh) {
+        // Two adjoining boundary faces. If the common edge of them is not
+        //   a subsegment, they belong to the same facet.
+        findedge(&cassh1, pc, pa);
+        sspivot(cassh1, checkseg);
+        if (checkseg.sh == dummysh) {
+          // The four points are forced to be coplanar.
+          ori1 = 0.0;
+        }
+      }
+    }
+    if (ori1 <= 0.0) {
+      // a lies above cde, unflipable, and abc is locally Delaunay.
+      return NONCONVEX;
+    }
+    ori2 = orient3d(pd, pc, pe, pb);
+    if (checksubfaces && ori2 != 0.0) {
+      // Are bcd and cbe subfaces and belong to the same facet?
+      enextfnext(horiz, casing);
+      tspivot(casing, cassh1);
+      enext2fnext(symhoriz, casing);
+      tspivot(casing, cassh2);
+      if (cassh1.sh != dummysh && cassh2.sh != dummysh) {
+        // Two adjoining boundary faces. If the common edge of them is not
+        //   a subsegment, they belong to the same facet.
+        findedge(&cassh1, pb, pc);
+        sspivot(cassh1, checkseg);
+        if (checkseg.sh == dummysh) {
+          // The four points are forced to be coplanar.
+          ori2 = 0.0;
+        }
+      }
+    }
+    if (ori2 <= 0.0) {
+      // b lies above dce, unflipable, and abc is locally Delaunay.
+      return NONCONVEX;
+    }
+    // Edge ab crosses face cde properly.
+    if (checksubfaces) {
+      // If abc is subface, then ab must be a subsegment (because abde is
+      //   a tetrahedron and ab crosses cde properly). 
+      tsspivot(&horiz, &checkseg);
+      if (checkseg.sh != dummysh) {
+        // To flip a subsegment is forbidden.
+        return FORBIDDENEDGE;
+      }
+      // Both abd and bae should not be subfaces (because they're not
+      //   coplanar and ab is not a subsegment). However, they may be
+      //   subfaces and belong to a facet (created during facet recovery),
+      //   that is, abde is an invalid tetrahedron. Find this case out.
+      fnext(horiz, casing);
+      tspivot(casing, cassh1);
+      fnext(symhoriz, casing);
+      tspivot(casing, cassh2); 
+      if (cassh1.sh != dummysh || cassh2.sh != dummysh) {
+        // Unfortunately, they're subfaces. Corrections need be done here.
+        printf("Warning:  A tetrahedron spans two subfaces of a facet.\n");
+        // Temporarily, let it be there.
+        return NONCONVEX;
+      }
+    }
+    return T32;
+  } else {
+    assert(adjtet == 2);
+    // The convex hull of {a, b, c, d, e} has only four vertices, abc is
+    //   unflipable, furthermore, it is locally Delaunay.
+    return NONCONVEX;
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// enqueueflipface(), enqueueflipedge()    Add a face or an edge to the end  //
+//                                         of a queue.                       //
+//                                                                           //
+// This face or edge may be non-Delaunay and will be checked.  Corresponding //
+// flip operation will be applied on it if it is non-Delaunay.  The vertices //
+// of the face or edge are stored seperatly used to ensure the face or edge  //
+// is still the same one when we save it.  Sometimes, other flipping will    //
+// cause this face or edge be changed or dead.                               //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::enqueueflipface(triface& checkface, queue* flipqueue)
+{
+  badface *queface;
+
+  queface = (badface *) flipqueue->push((void *) NULL);
+  queface->tt = checkface;
+  queface->forg = org(checkface);
+  queface->fdest = dest(checkface);
+  queface->fapex = apex(checkface);
+}
+
+void tetgenmesh::enqueueflipedge(face& checkedge, queue* flipqueue)
+{
+  badface *queface;
+
+  queface = (badface *) flipqueue->push((void *) NULL);
+  queface->ss = checkedge;
+  queface->forg = sorg(checkedge);
+  queface->fdest = sdest(checkedge);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// flip23()    Perform a 2-to-3 flip.                                        //
+//                                                                           //
+// On input, 'flipface' represents the face will be flipped.  Let it is abc, //
+// the two tetrahedra sharing abc are abcd, bace. abc is not a subface.      //
+//                                                                           //
+// A 2-to-3 flip is to change two tetrahedra abcd, bace to three tetrahedra  //
+// edab, edbc, and edca.  As a result, face abc has been removed and three   //
+// new faces eda, edb and edc have been created.                             //
+//                                                                           //
+// On completion, 'flipface' returns edab.  If 'flipqueue' is not NULL, all  //
+// possibly non-Delaunay faces are added into it.                            //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::flip23(triface* flipface, queue* flipqueue)
+{
+  triface abcd, bace;                                  // Old configuration.
+  triface oldabd, oldbcd, oldcad;
+  triface abdcasing, bcdcasing, cadcasing;
+  face abdsh, bcdsh, cadsh;
+  triface oldbae, oldcbe, oldace;
+  triface baecasing, cbecasing, acecasing;
+  face baesh, cbesh, acesh;
+  triface edab, edbc, edca;                            // New configuration.
+  point pa, pb, pc, pd, pe;
+  REAL attrib, volume;
+  int i;
+
+  abcd = *flipface;
+  adjustedgering(abcd, CCW); // abcd represents edge ab.
+  sym(abcd, bace);
+  findedge(&bace, dest(abcd), org(abcd)); // bace represents edge ba.
+  pa = org(abcd);
+  pb = dest(abcd);
+  pc = apex(abcd);
+  pd = oppo(abcd);
+  pe = oppo(bace);
+
+  if (b->verbose > 2) {
+    printf("    Do T23 on face (%d, %d, %d, %d).\n", pointmark(pa),
+           pointmark(pb), pointmark(pc), pointmark(pd));
+  }
+  flip23s++;
+
+#ifdef SELF_CHECK
+  // Edge de must cross face abc properly.
+  assert(orient3d(pa, pb, pd, pe) >= 0.0);
+  assert(orient3d(pb, pc, pd, pe) >= 0.0);
+  assert(orient3d(pc, pa, pd, pe) >= 0.0);
+#endif
+
+  // Storing the old configuration outside the convex hull.
+  fnext(abcd, oldabd);
+  enextfnext(abcd, oldbcd);
+  enext2fnext(abcd, oldcad);
+  fnext(bace, oldbae);
+  enext2fnext(bace, oldcbe);
+  enextfnext(bace, oldace);
+  sym(oldabd, abdcasing);
+  sym(oldbcd, bcdcasing);
+  sym(oldcad, cadcasing);
+  sym(oldbae, baecasing);
+  sym(oldcbe, cbecasing);
+  sym(oldace, acecasing);
+  if (checksubfaces) {
+    tspivot(oldabd, abdsh);
+    tspivot(oldbcd, bcdsh);
+    tspivot(oldcad, cadsh);
+    tspivot(oldbae, baesh);
+    tspivot(oldcbe, cbesh);
+    tspivot(oldace, acesh);
+  }
+
+  // Creating the new configuration inside the convex hull.
+  edab.tet = abcd.tet; // Update abcd to be edab.
+  setorg (edab, pe);
+  setdest(edab, pd);
+  setapex(edab, pa);
+  setoppo(edab, pb);
+  edbc.tet = bace.tet; // Update bace to be edbc.
+  setorg (edbc, pe);
+  setdest(edbc, pd);
+  setapex(edbc, pb);
+  setoppo(edbc, pc);
+  maketetrahedron(&edca); // Create edca.
+  setorg (edca, pe);
+  setdest(edca, pd);
+  setapex(edca, pc);
+  setoppo(edca, pa);
+  // Set the element attributes of the new tetrahedron 'edca'.
+  for (i = 0; i < in->numberoftetrahedronattributes; i++) {
+    attrib = elemattribute(abcd.tet, i);
+    setelemattribute(edca.tet, i, attrib);
+  }
+  // Set the volume constraint of the new tetrahedron 'edca' if the -ra
+  //   switches are not used together. In -ra case, the various volume
+  //   constraints can be spreaded very far.
+  if (b->varvolume && !b->refine) {
+    volume = volumebound(abcd.tet);
+    setvolumebound(edca.tet, volume);
+  }
+
+  // Clear old bonds in edab(was abcd) and edbc(was bace).
+  for (i = 0; i < 4; i ++) {
+    edab.loc = i;
+    dissolve(edab);
+    edbc.loc = i;
+    dissolve(edbc);
+  }
+  // Bond the faces inside the convex hull.
+  edab.loc = 0;
+  edca.loc = 1;
+  bond(edab, edca);
+  edab.loc = 1;
+  edbc.loc = 0;
+  bond(edab, edbc);
+  edbc.loc = 1;
+  edca.loc = 0;
+  bond(edbc, edca);
+  // Bond the faces on the convex hull.
+  edab.loc = 2;
+  bond(edab, abdcasing);
+  edab.loc = 3;
+  bond(edab, baecasing);
+  edbc.loc = 2;
+  bond(edbc, bcdcasing);
+  edbc.loc = 3;
+  bond(edbc, cbecasing);
+  edca.loc = 2;
+  bond(edca, cadcasing);
+  edca.loc = 3;
+  bond(edca, acecasing);  
+  // There may exist subfaces that need to be bonded to new configuarton.
+  if (checksubfaces) {
+    // Clear old flags in edab(was abcd) and edbc(was bace).
+    for (i = 0; i < 4; i ++) {
+      edab.loc = i;
+      tsdissolve(edab);
+      edbc.loc = i;
+      tsdissolve(edbc);
+    }
+    if (abdsh.sh != dummysh) {
+      edab.loc = 2; 
+      tsbond(edab, abdsh);
+    }
+    if (baesh.sh != dummysh) {
+      edab.loc = 3; 
+      tsbond(edab, baesh);
+    }
+    if (bcdsh.sh != dummysh) {
+      edbc.loc = 2; 
+      tsbond(edbc, bcdsh);
+    }
+    if (cbesh.sh != dummysh) {
+      edbc.loc = 3; 
+      tsbond(edbc, cbesh);
+    }
+    if (cadsh.sh != dummysh) {
+      edca.loc = 2; 
+      tsbond(edca, cadsh);
+    }
+    if (acesh.sh != dummysh) {
+      edca.loc = 3; 
+      tsbond(edca, acesh);
+    }
+  }
+
+  edab.loc = 0;
+  edbc.loc = 0;
+  edca.loc = 0;
+  if (b->verbose > 3) {
+    printf("    Updating edab ");
+    printtet(&edab);
+    printf("    Updating edbc ");
+    printtet(&edbc);
+    printf("    Creating edca ");
+    printtet(&edca);
+  }
+
+  if (flipqueue != (queue *) NULL) { 
+    enextfnext(edab, abdcasing);
+    enqueueflipface(abdcasing, flipqueue);
+    enext2fnext(edab, baecasing);
+    enqueueflipface(baecasing, flipqueue);
+    enextfnext(edbc, bcdcasing);
+    enqueueflipface(bcdcasing, flipqueue);
+    enext2fnext(edbc, cbecasing);
+    enqueueflipface(cbecasing, flipqueue);
+    enextfnext(edca, cadcasing);
+    enqueueflipface(cadcasing, flipqueue);
+    enext2fnext(edca, acecasing);
+    enqueueflipface(acecasing, flipqueue);  
+  }
+
+  // Save a live handle in 'recenttet'.
+  recenttet = edbc;
+  // Set the return handle be edab.
+  *flipface = edab;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// flip32()    Perform a 3-to-2 flip.                                        //
+//                                                                           //
+// On input, 'flipface' represents the face will be flipped.  Let it is eda, //
+// where edge ed is locally non-convex. Three tetrahedra sharing ed are edab,//
+// edbc, and edca.  ed is not a subsegment.                                  //
+//                                                                           //
+// A 3-to-2 flip is to change the three tetrahedra edab, edbc, and edca into //
+// another two tetrahedra abcd and bace.  As a result, the edge ed has been  //
+// removed and the face abc has been created.                                //
+//                                                                           //
+// On completion, 'flipface' returns abcd.  If 'flipqueue' is not NULL, all  //
+// possibly non-Delaunay faces are added into it.                            //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::flip32(triface* flipface, queue* flipqueue)
+{
+  triface edab, edbc, edca;                            // Old configuration.
+  triface oldabd, oldbcd, oldcad;
+  triface abdcasing, bcdcasing, cadcasing;
+  face abdsh, bcdsh, cadsh;
+  triface oldbae, oldcbe, oldace;
+  triface baecasing, cbecasing, acecasing;
+  face baesh, cbesh, acesh;
+  triface abcd, bace;                                  // New configuration.
+  point pa, pb, pc, pd, pe;
+  int i;
+
+  edab = *flipface;
+  adjustedgering(edab, CCW);
+  fnext(edab, edbc);
+  symself(edbc);
+  findedge(&edbc, org(edab), dest(edab));
+  fnext(edbc, edca);
+  symself(edca);
+  findedge(&edca, org(edab), dest(edab));
+  pa = apex(edab);
+  pb = oppo(edab);
+  pc = oppo(edbc);
+  pd = dest(edab);
+  pe = org(edab);
+
+  if (b->verbose > 2) {
+    printf("    Do T32 on face (%d, %d, %d, %d).\n",
+           pointmark(pe), pointmark(pd), pointmark(pa), pointmark(pb));
+  }
+  flip32s++;
+
+#ifdef SELF_CHECK
+  // Edge de must cross face abc properly.
+  assert(orient3d(pa, pb, pc, pd) <= 0.0);
+  assert(orient3d(pb, pa, pc, pe) <= 0.0);
+#endif
+
+  // Storing the old configuration outside the convex hull.
+  enextfnext(edab, oldabd);
+  enext2fnext(edab, oldbae);
+  enextfnext(edbc, oldbcd);
+  enext2fnext(edbc, oldcbe);
+  enextfnext(edca, oldcad);
+  enext2fnext(edca, oldace);
+  sym(oldabd, abdcasing);
+  sym(oldbcd, bcdcasing);
+  sym(oldcad, cadcasing);
+  sym(oldbae, baecasing);
+  sym(oldcbe, cbecasing);
+  sym(oldace, acecasing);
+  if (checksubfaces) {
+    tspivot(oldabd, abdsh);
+    tspivot(oldbcd, bcdsh);
+    tspivot(oldcad, cadsh);
+    tspivot(oldbae, baesh);
+    tspivot(oldcbe, cbesh);
+    tspivot(oldace, acesh);
+  }
+
+  // Creating the new configuration inside the convex hull.
+  abcd.tet = edab.tet; // Update edab to be abcd.
+  setorg (abcd, pa);
+  setdest(abcd, pb);
+  setapex(abcd, pc);
+  setoppo(abcd, pd);
+  bace.tet = edbc.tet; // Update edbc to be bace.
+  setorg (bace, pb);
+  setdest(bace, pa);
+  setapex(bace, pc);
+  setoppo(bace, pe);
+  // Dealloc a redundant tetrahedron (edca).
+  tetrahedrondealloc(edca.tet); 
+
+  // Clear the old bonds in abcd (was edab) and bace (was edbc).
+  for (i = 0; i < 4; i ++) {
+    abcd.loc = i;
+    dissolve(abcd);
+    bace.loc = i;
+    dissolve(bace);
+  }
+  // Bond the inside face of the convex hull.
+  abcd.loc = 0;
+  bace.loc = 0;
+  bond(abcd, bace);
+  // Bond the outside faces of the convex hull.
+  abcd.loc = 1;
+  bond(abcd, abdcasing);
+  abcd.loc = 2;
+  bond(abcd, bcdcasing);
+  abcd.loc = 3;
+  bond(abcd, cadcasing);
+  bace.loc = 1;
+  bond(bace, baecasing);
+  bace.loc = 3;
+  bond(bace, cbecasing);
+  bace.loc = 2;
+  bond(bace, acecasing);
+  if (checksubfaces) {
+    // Clear old bonds in abcd(was edab) and bace(was edbc).
+    for (i = 0; i < 4; i ++) {
+      abcd.loc = i;
+      tsdissolve(abcd);
+      bace.loc = i;
+      tsdissolve(bace);
+    }
+    if (abdsh.sh != dummysh) {
+      abcd.loc = 1;
+      tsbond(abcd, abdsh);
+    }
+    if (baesh.sh != dummysh) {
+      bace.loc = 1;
+      tsbond(bace, baesh);
+    }
+    if (bcdsh.sh != dummysh) {
+      abcd.loc = 2;
+      tsbond(abcd, bcdsh);
+    }
+    if (cbesh.sh != dummysh) {
+      bace.loc = 3;
+      tsbond(bace, cbesh);
+    }
+    if (cadsh.sh != dummysh) {
+      abcd.loc = 3;
+      tsbond(abcd, cadsh);
+    }
+    if (acesh.sh != dummysh) {
+      bace.loc = 2;
+      tsbond(bace, acesh);
+    }
+  }
+
+  abcd.loc = 0;
+  bace.loc = 0;
+  if (b->verbose > 3) {
+    printf("    Updating abcd ");
+    printtet(&abcd);
+    printf("    Updating bace ");
+    printtet(&bace);
+    printf("    Deleting edca ");
+    printtet(&edca);
+  }
+
+  if (flipqueue != (queue *) NULL) { 
+    fnext(abcd, abdcasing);
+    enqueueflipface(abdcasing, flipqueue);
+    fnext(bace, baecasing);
+    enqueueflipface(baecasing, flipqueue);
+    enextfnext(abcd, bcdcasing);
+    enqueueflipface(bcdcasing, flipqueue);
+    enextfnext(bace, cbecasing);
+    enqueueflipface(cbecasing, flipqueue);
+    enext2fnext(abcd, cadcasing);
+    enqueueflipface(cadcasing, flipqueue);
+    enext2fnext(bace, acecasing);
+    enqueueflipface(acecasing, flipqueue);  
+  }
+
+  // Save a live handle in 'recenttet'.
+  recenttet = abcd;
+  // Set the return handle be abcd.
+  *flipface = abcd;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// flip22()    Perform a 2-to-2 (or 4-to-4) flip.                            //
+//                                                                           //
+// On input, 'flipface' represents the face will be flipped.  Let it is abe, //
+// ab is the flipable edge, the two tetrahedra sharing abe are abce and bade,//
+// hence a, b, c and d are coplanar. If abc, bad are interior faces, the two //
+// tetrahedra opposite to e are bacf and abdf.  ab is not a subsegment.      //
+//                                                                           //
+// A 2-to-2 flip is to change two tetrahedra abce and bade into another two  //
+// tetrahedra dcae and cdbe. If bacf and abdf exist, they're changed to cdaf //
+// and dcbf, thus a 4-to-4 flip.  As a result, two or four tetrahedra have   //
+// rotated counterclockwise (using right-hand rule with thumb points to e):  //
+// abce->dcae, bade->cdbe, and bacf->cdaf, abdf->dcbf.                       //
+//                                                                           //
+// If abc and bad are subfaces, a 2-to-2 flip is performed simultaneously by //
+// calling routine flip22sub(), hence abc->dca, bad->cdb.  The edge rings of //
+// the flipped subfaces dca and cdb have the same orientation as abc and bad.//
+// Hence, they have the same orientation as other subfaces of the facet with //
+// respect to the lift point of this facet.                                  //
+//                                                                           //
+// On completion, 'flipface' holds edge dc of tetrahedron dcae. 'flipqueue'  //
+// contains all possibly non-Delaunay faces if it is not NULL.               //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::flip22(triface* flipface, queue* flipqueue)
+{
+  triface abce, bade;
+  triface oldbce, oldcae, oldade, olddbe;
+  triface bcecasing, caecasing, adecasing, dbecasing;
+  face bcesh, caesh, adesh, dbesh;
+  triface bacf, abdf;
+  triface oldacf, oldcbf, oldbdf, olddaf;
+  triface acfcasing, cbfcasing, bdfcasing, dafcasing;
+  face acfsh, cbfsh, bdfsh, dafsh;
+  face abc, bad;
+  point pa, pb, pc, pd, pe, pf;
+  int mirrorflag;
+
+  adjustedgering(*flipface, CCW); // 'flipface' is bae.
+  fnext(*flipface, abce);
+  esymself(abce);
+  adjustedgering(*flipface, CW); // 'flipface' is abe.
+  fnext(*flipface, bade);
+  assert(bade.tet != dummytet);
+  esymself(bade);
+  pa = org(abce);
+  pb = dest(abce);
+  pc = apex(abce);
+  pd = apex(bade);
+  pe = oppo(bade);
+  assert(oppo(abce) == pe);
+  sym(abce, bacf);
+  mirrorflag = bacf.tet != dummytet;
+  if (mirrorflag) {
+    findedge(&bacf, pb, pa);
+    sym(bade, abdf);
+    assert(abdf.tet != dummytet);
+    findedge(&abdf, pa, pb);
+    pf = oppo(bacf);
+    assert(oppo(abdf) == pf);
+  } 
+
+  if (b->verbose > 2) {
+    printf("    Do %s on edge (%d, %d).\n", mirrorflag ? "T44" : "T22",
+           pointmark(pa), pointmark(pb));
+  }
+  mirrorflag ? flip44s++ : flip22s++;
+
+#ifdef SELF_CHECK
+  // The quadrilateral formed by a, b, c, and d must be convex.
+  assert(orient3d(pc, pd, pe, pa) <= 0.0);
+  assert(orient3d(pd, pc, pe, pb) <= 0.0);
+#endif
+  
+  // Save the old configuration at the convex hull.
+  enextfnext(abce, oldbce);
+  enext2fnext(abce, oldcae);
+  enextfnext(bade, oldade);
+  enext2fnext(bade, olddbe);
+  sym(oldbce, bcecasing);
+  sym(oldcae, caecasing);
+  sym(oldade, adecasing);
+  sym(olddbe, dbecasing);
+  if (checksubfaces) {
+    tspivot(oldbce, bcesh);
+    tspivot(oldcae, caesh);
+    tspivot(oldade, adesh);
+    tspivot(olddbe, dbesh);
+    tspivot(abce, abc);
+    tspivot(bade, bad);
+  }
+  if (mirrorflag) {
+    enextfnext(bacf, oldacf);
+    enext2fnext(bacf, oldcbf);
+    enextfnext(abdf, oldbdf);
+    enext2fnext(abdf, olddaf);
+    sym(oldacf, acfcasing);
+    sym(oldcbf, cbfcasing);
+    sym(oldbdf, bdfcasing);
+    sym(olddaf, dafcasing);
+    if (checksubfaces) {
+      tspivot(oldacf, acfsh);
+      tspivot(oldcbf, cbfsh);
+      tspivot(oldbdf, bdfsh);
+      tspivot(olddaf, dafsh);
+    }
+  }
+
+  // Rotate abce, bade one-quarter turn counterclockwise.
+  bond(oldbce, caecasing);
+  bond(oldcae, adecasing);
+  bond(oldade, dbecasing);
+  bond(olddbe, bcecasing);
+  if (checksubfaces) {
+    // Check for subfaces and rebond them to the rotated tets.
+    if (caesh.sh == dummysh) {
+      tsdissolve(oldbce);
+    } else {
+      tsbond(oldbce, caesh);
+    }
+    if (adesh.sh == dummysh) {
+      tsdissolve(oldcae);
+    } else {
+      tsbond(oldcae, adesh);
+    }
+    if (dbesh.sh == dummysh) {
+      tsdissolve(oldade);
+    } else {
+      tsbond(oldade, dbesh);
+    }
+    if (bcesh.sh == dummysh) {
+      tsdissolve(olddbe);
+    } else {
+      tsbond(olddbe, bcesh);
+    }
+  }
+  if (mirrorflag) {
+    // Rotate bacf, abdf one-quarter turn counterclockwise.
+    bond(oldcbf, acfcasing);
+    bond(oldacf, dafcasing);
+    bond(olddaf, bdfcasing);
+    bond(oldbdf, cbfcasing);
+    if (checksubfaces) {
+      // Check for subfaces and rebond them to the rotated tets.
+      if (acfsh.sh == dummysh) {
+        tsdissolve(oldcbf);
+      } else {
+        tsbond(oldcbf, acfsh);
+      }
+      if (dafsh.sh == dummysh) {
+        tsdissolve(oldacf);
+      } else {
+        tsbond(oldacf, dafsh);
+      }
+      if (bdfsh.sh == dummysh) {
+        tsdissolve(olddaf);
+      } else {
+        tsbond(olddaf, bdfsh);
+      }
+      if (cbfsh.sh == dummysh) {
+        tsdissolve(oldbdf);
+      } else {
+        tsbond(oldbdf, cbfsh);
+      }
+    }
+  }
+
+  // New vertex assignments for the rotated tetrahedra.
+  setorg(abce, pd); // Update abce to dcae
+  setdest(abce, pc);
+  setapex(abce, pa);
+  setorg(bade, pc); // Update bade to cdbe
+  setdest(bade, pd);
+  setapex(bade, pb);
+  if (mirrorflag) {
+    setorg(bacf, pc); // Update bacf to cdaf
+    setdest(bacf, pd);
+    setapex(bacf, pa);
+    setorg(abdf, pd); // Update abdf to dcbf
+    setdest(abdf, pc);
+    setapex(abdf, pb);
+  }
+
+  // Are there subfaces need to be flipped?
+  if (checksubfaces && abc.sh != dummysh) {
+    assert(bad.sh != dummysh);
+    // Adjust the edge be ab, so the rotation of subfaces is according with
+    //   the rotation of tetrahedra.
+    findedge(&abc, pa, pb);
+    // Flip an edge of two subfaces, ignore non-Delaunay edges.
+    flip22sub(&abc, NULL);
+  }
+
+  if (b->verbose > 3) {
+    printf("    Updating abce ");
+    printtet(&abce);
+    printf("    Updating bade ");
+    printtet(&bade);
+    if (mirrorflag) {
+      printf("    Updating bacf ");
+      printtet(&bacf);
+      printf("    Updating abdf ");
+      printtet(&abdf);
+    }
+  }
+
+  if (flipqueue != (queue *) NULL) { 
+    enextfnext(abce, bcecasing);
+    enqueueflipface(bcecasing, flipqueue);
+    enext2fnext(abce, caecasing);
+    enqueueflipface(caecasing, flipqueue);
+    enextfnext(bade, adecasing);
+    enqueueflipface(adecasing, flipqueue);
+    enext2fnext(bade, dbecasing);
+    enqueueflipface(dbecasing, flipqueue);
+    if (mirrorflag) {
+      enextfnext(bacf, acfcasing);
+      enqueueflipface(acfcasing, flipqueue);
+      enext2fnext(bacf, cbfcasing);
+      enqueueflipface(cbfcasing, flipqueue);
+      enextfnext(abdf, bdfcasing);
+      enqueueflipface(bdfcasing, flipqueue);
+      enext2fnext(abdf, dafcasing);
+      enqueueflipface(dafcasing, flipqueue);
+    }
+    // The two new faces dcae (abce), cdbe (bade) may still not be locally
+    //   Delaunay, and may need be flipped (flip23).  On the other hand, in
+    //   conforming Delaunay algorithm, two new subfaces dca (abc), and cdb
+    //   (bad) may be non-conforming Delaunay, they need be queued if they
+    //   are locally Delaunay but non-conforming Delaunay.
+    enqueueflipface(abce, flipqueue);
+    enqueueflipface(bade, flipqueue);
+  }
+
+  // Save a live handle in 'recenttet'.
+  recenttet = abce;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// flip22sub()    Perform a 2-to-2 flip on a subface edge.                   //
+//                                                                           //
+// The flip edge is given by subface 'flipedge'.  Let it is abc, where ab is //
+// the flipping edge.  The other subface is bad,  where a, b, c, d form a    //
+// convex quadrilateral.  ab is not a subsegment.                            //
+//                                                                           //
+// A 2-to-2 subface flip is to change two subfaces abc and bad to another    //
+// two subfaces dca and cdb.  Hence, edge ab has been removed and dc becomes //
+// an edge. If a point e is above abc, this flip is equal to rotate abc and  //
+// bad counterclockwise using right-hand rule with thumb points to e. It is  //
+// important to know that the edge rings of the flipped subfaces dca and cdb //
+// are keeping the same orientation as their original subfaces. So they have //
+// the same orientation with respect to the lift point of this facet.        //
+//                                                                           //
+// During rotating, the face rings of the four edges bc, ca, ad, and de need //
+// be re-connected. If the edge is not a subsegment, then its face ring has  //
+// only two faces, a sbond() will bond them together. If it is a subsegment, //
+// one should use sbond1() twice to bond two different handles to the rotat- //
+// ing subface, one is predecssor (-casin), another is successor (-casout).  //
+//                                                                           //
+// If 'flipqueue' is not NULL, it returns four edges bc, ca, ad, de, which   //
+// may be non-Delaunay.                                                      //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::flip22sub(face* flipedge, queue* flipqueue)
+{
+  face abc, bad;
+  face oldbc, oldca, oldad, olddb;
+  face bccasin, bccasout, cacasin, cacasout;
+  face adcasin, adcasout, dbcasin, dbcasout;
+  face bc, ca, ad, db;
+  face spinsh;
+  point pa, pb, pc, pd;
+
+  abc = *flipedge;
+  spivot(abc, bad);
+  if (sorg(bad) != sdest(abc)) {
+    sesymself(bad);
+  }
+  pa = sorg(abc);
+  pb = sdest(abc);
+  pc = sapex(abc);
+  pd = sapex(bad);
+
+  if (b->verbose > 2) {
+    printf("    Flip sub edge (%d, %d).\n", pointmark(pa), pointmark(pb));
+  }
+
+  // Save the old configuration outside the quadrilateral.  
+  senext(abc, oldbc);
+  senext2(abc, oldca);
+  senext(bad, oldad);
+  senext2(bad, olddb);
+  // Get the outside connection. Becareful if there is a subsegment on the
+  //   quadrilateral, two casings (casin and casout) are needed to save for
+  //   keeping the face link.
+  spivot(oldbc, bccasout);
+  sspivot(oldbc, bc);
+  if (bc.sh != dummysh) {
+    // 'bc' is a subsegment.
+    assert(bccasout.sh != dummysh);
+    if (oldbc.sh != bccasout.sh) {
+      // 'oldbc' is not self-bonded.
+      spinsh = bccasout;
+      do {
+        bccasin = spinsh;
+        spivotself(spinsh);
+      } while (spinsh.sh != oldbc.sh);
+    } else {
+      bccasout.sh = dummysh;
+    }
+    ssdissolve(oldbc);
+  }
+  spivot(oldca, cacasout);
+  sspivot(oldca, ca);
+  if (ca.sh != dummysh) {
+    // 'ca' is a subsegment. 
+    assert(cacasout.sh != dummysh);
+    if (oldca.sh != cacasout.sh) {
+      // 'oldca' is not self-bonded.
+      spinsh = cacasout;
+      do {
+        cacasin = spinsh;
+        spivotself(spinsh);
+      } while (spinsh.sh != oldca.sh);
+    } else {
+      cacasout.sh = dummysh;
+    }
+    ssdissolve(oldca);
+  }
+  spivot(oldad, adcasout);
+  sspivot(oldad, ad);
+  if (ad.sh != dummysh) {
+    // 'ad' is a subsegment. 
+    assert(adcasout.sh != dummysh);
+    if (oldad.sh != adcasout.sh) {
+      // 'adcasout' is not self-bonded.
+      spinsh = adcasout;
+      do {
+        adcasin = spinsh;
+        spivotself(spinsh);
+      } while (spinsh.sh != oldad.sh);
+    } else {
+      adcasout.sh = dummysh;
+    }
+    ssdissolve(oldad);
+  }
+  spivot(olddb, dbcasout);
+  sspivot(olddb, db);
+  if (db.sh != dummysh) {
+    // 'db' is a subsegment.
+    assert(dbcasout.sh != dummysh);
+    if (olddb.sh != dbcasout.sh) {
+      // 'dbcasout' is not self-bonded.
+      spinsh = dbcasout;
+      do {
+        dbcasin = spinsh;
+        spivotself(spinsh);
+      } while (spinsh.sh != olddb.sh);
+    } else {
+      dbcasout.sh = dummysh;
+    }
+    ssdissolve(olddb);
+  }
+
+  // Rotate abc and bad one-quarter turn counterclockwise.
+  if (ca.sh != dummysh) {
+    if (cacasout.sh != dummysh) {
+      sbond1(cacasin, oldbc);
+      sbond1(oldbc, cacasout);
+    } else {
+      // Bond 'oldbc' to itself.
+      sbond(oldbc, oldbc);
+      // Make sure that dummysh always correctly bonded.
+      dummysh[0] = sencode(oldbc);
+    }
+    ssbond(oldbc, ca);
+  } else {
+    sbond(oldbc, cacasout);
+  }
+  if (ad.sh != dummysh) {
+    if (adcasout.sh != dummysh) {
+      sbond1(adcasin, oldca);
+      sbond1(oldca, adcasout);
+    } else {
+      // Bond 'oldca' to itself.
+      sbond(oldca, oldca);
+      // Make sure that dummysh always correctly bonded.
+      dummysh[0] = sencode(oldca);
+    }
+    ssbond(oldca, ad);
+  } else {
+    sbond(oldca, adcasout);
+  }
+  if (db.sh != dummysh) {
+    if (dbcasout.sh != dummysh) {
+      sbond1(dbcasin, oldad);
+      sbond1(oldad, dbcasout);
+    } else {
+      // Bond 'oldad' to itself.
+      sbond(oldad, oldad);
+      // Make sure that dummysh always correctly bonded.
+      dummysh[0] = sencode(oldad);
+    }
+    ssbond(oldad, db);
+  } else {
+    sbond(oldad, dbcasout);
+  }
+  if (bc.sh != dummysh) {
+    if (bccasout.sh != dummysh) {
+      sbond1(bccasin, olddb);
+      sbond1(olddb, bccasout);
+    } else {
+      // Bond 'olddb' to itself.
+      sbond(olddb, olddb);
+      // Make sure that dummysh always correctly bonded.
+      dummysh[0] = sencode(olddb);
+    }
+    ssbond(olddb, bc);
+  } else {
+    sbond(olddb, bccasout);
+  }
+
+  // New vertex assignments for the rotated subfaces.
+  setsorg(abc, pd);  // Update abc to dca.
+  setsdest(abc, pc);
+  setsapex(abc, pa);
+  setsorg(bad, pc);  // Update bad to cdb.
+  setsdest(bad, pd);
+  setsapex(bad, pb);
+
+  if (flipqueue != (queue *) NULL) {
+    enqueueflipedge(bccasout, flipqueue);
+    enqueueflipedge(cacasout, flipqueue);
+    enqueueflipedge(adcasout, flipqueue);
+    enqueueflipedge(dbcasout, flipqueue);
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// flip()    Flips non-locally Delaunay faces in flipqueue until it is empty.//
+//                                                                           //
+// Assumpation:  Current tetrahedralization is non-Delaunay after inserting  //
+// a point or performing a flip operation, all possibly non-Delaunay faces   //
+// are in 'flipqueue'.                                                       //
+//                                                                           //
+// If 'plastflip' is not NULL,  it is used to return a stack of recently     //
+// flipped faces.  This stack will be used to reverse the flips done in this //
+// routine later for removing a newly inserted point because it encroaches   //
+// any subfaces or subsegments.                                              //
+//                                                                           //
+// If the quality mesh step is starting,  (indicated by pools 'badsubsegs',  //
+// 'badsubfaces' and 'badtetrahedrons' are not NULLs.)  we will check the    //
+// encroached subface or subsegments of a hull face, and queuing tetrahedra  //
+// for quality checking.                                                     //
+//                                                                           //
+// The return value is the total number of flips done during this invocation.//
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+long tetgenmesh::flip(queue* flipqueue, flipstacker **plastflip)
+{
+  badface *qface;
+  flipstacker *newflip;
+  triface flipface, symface;
+  face checkseg, checksh;
+  enum fliptype fc;
+  bool flipped;
+  REAL sign, bakepsilon;
+  long flipcount;
+  int epscount;
+  int i;
+
+  if (b->verbose > 1) {
+    printf("    Do flipface queue: %ld faces.\n", flipqueue->len());
+  }
+
+  flipcount = flip23s + flip32s + flip22s + flip44s;
+  
+  if (plastflip != (flipstacker **) NULL) {
+    // Initialize the stack of the flip sequence.
+    flipstackers->restart();
+    *plastflip = (flipstacker *) NULL;
+  }
+
+  // Loop until the queue is empty.
+  while ((qface = (badface *) flipqueue->pop()) != NULL) {
+    // Get a face.
+    flipface = qface->tt;
+    // Check the validity of this face.
+    if (isdead(&flipface) || flipface.tet == dummytet || 
+        (org(flipface) != qface->forg) || 
+        (dest(flipface) != qface->fdest) ||
+        (apex(flipface) != qface->fapex) ||
+        (oppo(flipface) == (point) NULL)) continue;
+    flipped = false;
+    sym(flipface, symface);
+    // Only do check when the adjacent tet exists and it's not a "fake" tet.
+    if (symface.tet != dummytet && oppo(symface) != (point) NULL) {
+      // For positive orientation that insphere() test requires.
+      adjustedgering(flipface, CW); 
+      sign = insphere(org(flipface), dest(flipface), apex(flipface),
+                      oppo(flipface), oppo(symface));
+    } else {
+      sign = -1.0; // A hull face is locally Delaunay.
+    }
+    if (sign > 0.0) {
+      // 'flipface' is non-locally Delaunay, try to flip it.
+      if (checksubfaces) {
+        bakepsilon = b->epsilon;
+        epscount = 0;
+        while (epscount < 16) {
+          fc = categorizeface(flipface);
+          if (fc == NONCONVEX) {
+            b->epsilon *= 1e-2;
+            epscount++;
+            continue;
+          }
+          break;
+        }
+        b->epsilon = bakepsilon;
+        // assert(epscount < 16);
+        if (epscount == 16) {
+          if (b->verbose) {
+            printf("Warning:  Can't flip a degenerate tetrahedron.\n");
+          }
+          fc = NONCONVEX;
+        }
+      } else {
+        fc = categorizeface(flipface);
+        assert(fc != NONCONVEX);
+      }
+      switch (fc) {
+      // The following face types are flipable.
+      case T44:
+      case T22:
+        flip22(&flipface, flipqueue); 
+        flipped = true;
+        break;
+      case T23:
+        flip23(&flipface, flipqueue); 
+        flipped = true;
+        break;
+      case T32:
+        flip32(&flipface, flipqueue); 
+        flipped = true;
+        break;
+      // The following face types are unflipable.
+      case UNFLIPABLE:
+        break;
+      case FORBIDDENFACE:
+        // Meet an encroaching subface, unflipable.
+        break;
+      case FORBIDDENEDGE:
+        // Meet an encroaching subsegment, unflipable.
+        break;
+      // This case is only possible when the domain is nonconvex.
+      case NONCONVEX:
+        assert(nonconvex);
+        break;
+      }
+      if (plastflip != (flipstacker **) NULL && flipped) { 
+        // Push the flipped face into stack.
+        newflip = (flipstacker *) flipstackers->alloc();
+        newflip->flippedface = flipface;
+        newflip->fc = fc;
+        newflip->forg = org(flipface);
+        newflip->fdest = dest(flipface);
+        newflip->fapex = apex(flipface);
+        newflip->prevflip = *plastflip;
+        *plastflip = newflip;  
+      }
+    }
+    if (!flipped) {
+      // 'flipface' is locally Delaunay, or it is non-locally Delaunay but
+      //   not flipable because it is a subface or contains a subsegment.
+      if (badsubsegs != (memorypool *) NULL) {
+        // Check for encroaching subsegments, add them into list.
+        for (i = 0; i < 3; i++) {
+          tsspivot(&flipface, &checkseg);
+          if ((checkseg.sh != dummysh) && !shell2badface(checkseg)) {
+            checkseg4encroach(&checkseg, NULL, true);
+          }
+          enextself(flipface);
+        }
+      }
+      if (badsubfaces != (memorypool *) NULL) {
+        // Check for encroaching subface, add it into list.
+        tspivot(flipface, checksh);
+        if ((checksh.sh != dummysh) && !shell2badface(checksh)) {
+          checksub4encroach(&checksh, NULL, true);
+        }
+      }
+      if (badtetrahedrons != (memorypool *) NULL) {
+        // Put the tetrahedra at both sides into list for quality check.
+        qualchecktetlist->append(&flipface);
+        if (symface.tet != dummytet) {
+          qualchecktetlist->append(&symface);
+        }
+      }
+    }
+  }
+
+  flipcount = flip23s + flip32s + flip22s + flip44s - flipcount;
+  if (b->verbose > 1) {
+    printf("    %ld flips.\n", flipcount);
+  }
+
+  return flipcount;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// undoflip()    Undo the most recent flip sequence induced by flip().       //
+//                                                                           //
+// 'lastflip' is the stack of recently flipped faces. Walks through the list //
+// of flips, in the reverse of the order in which they were done, and undoes //
+// them.                                                                     //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::undoflip(flipstacker *lastflip)
+{
+  while (lastflip != (flipstacker *) NULL) {
+    // Get the right flipped face.
+    findface(&lastflip->flippedface, lastflip->forg, lastflip->fdest, 
+             lastflip->fapex);
+    switch (lastflip->fc) {
+    case T23:
+      // The reverse operation of T23 is T32.
+      flip32(&lastflip->flippedface, NULL);
+      break;
+    case T32:
+      // The reverse operation of T32 is T23.
+      flip23(&lastflip->flippedface, NULL);
+      break;
+    case T22:
+    case T44:
+      // The reverse operation of T22 or T44 is again T22 or T44.
+      flip22(&lastflip->flippedface, NULL);
+      break;
+    }
+    // Go on and process the next transformation.
+    lastflip = lastflip->prevflip;
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// splittetrahedron()    Insert a point into a tetrahedron, split it into    //
+//                       four tetrahedra.                                    //
+//                                                                           //
+// The tetrahedron is given by 'splittet'.  Let it is abcd.  The inserting   //
+// point 'newpoint' v should lie strictly inside abcd.                       //
+//                                                                           //
+// Splitting a tetrahedron is to shrink abcd to abcv,  and create three new  //
+// tetrahedra badv, cbdv, and acdv.                                          //
+//                                                                           //
+// On completion, 'splittet' returns abcv.  If 'flipqueue' is not NULL, it   //
+// contains all possibly non-locally Delaunay faces.                         //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::
+splittetrahedron(point newpoint, triface* splittet, queue* flipqueue)
+{
+  triface oldabd, oldbcd, oldcad;                      // Old configuration.
+  triface abdcasing, bcdcasing, cadcasing;
+  face abdsh, bcdsh, cadsh;
+  triface abcv, badv, cbdv, acdv;                      // New configuration.
+  point pa, pb, pc, pd;
+  REAL attrib, volume;
+  int i;
+
+  abcv = *splittet;
+  abcv.ver = 0;
+  // Set the changed vertices and new tetrahedron.
+  pa = org(abcv);
+  pb = dest(abcv);
+  pc = apex(abcv);
+  pd = oppo(abcv);
+
+  if (b->verbose > 1) {
+    printf("  Inserting point %d in tetrahedron (%d, %d, %d, %d).\n",
+           pointmark(newpoint), pointmark(pa), pointmark(pb), pointmark(pc),
+           pointmark(pd));
+  }
+
+  fnext(abcv, oldabd);
+  enextfnext(abcv, oldbcd);
+  enext2fnext(abcv, oldcad);
+  sym(oldabd, abdcasing);
+  sym(oldbcd, bcdcasing);
+  sym(oldcad, cadcasing);
+  maketetrahedron(&badv);
+  maketetrahedron(&cbdv);
+  maketetrahedron(&acdv);
+
+  // Set 'badv' vertices.
+  setorg (badv, pb);
+  setdest(badv, pa);
+  setapex(badv, pd);
+  setoppo(badv, newpoint);
+  // Set 'cbdv' vertices.
+  setorg (cbdv, pc);
+  setdest(cbdv, pb);
+  setapex(cbdv, pd);
+  setoppo(cbdv, newpoint);
+  // Set 'acdv' vertices.
+  setorg (acdv, pa);
+  setdest(acdv, pc);
+  setapex(acdv, pd);
+  setoppo(acdv, newpoint);
+  // Set 'abcv' vertices
+  setoppo(abcv, newpoint);
+
+  // Set the element attributes of the new tetrahedra.
+  for (i = 0; i < in->numberoftetrahedronattributes; i++) {
+    attrib = elemattribute(abcv.tet, i);
+    setelemattribute(badv.tet, i, attrib);
+    setelemattribute(cbdv.tet, i, attrib);
+    setelemattribute(acdv.tet, i, attrib);
+  }
+  // Set the volume constraint of the new tetrahedra.
+  if (b->varvolume) {
+    volume = volumebound(abcv.tet);
+    setvolumebound(badv.tet, volume);
+    setvolumebound(cbdv.tet, volume);
+    setvolumebound(acdv.tet, volume);
+  }
+
+  // Bond the new triangles to the surrounding tetrahedron.
+  bond(badv, abdcasing);
+  bond(cbdv, bcdcasing);
+  bond(acdv, cadcasing);
+  // There may exist subfaces need to be bonded to the new tetrahedra.
+  if (checksubfaces) {
+    tspivot(oldabd, abdsh);
+    if (abdsh.sh != dummysh) {
+      tsdissolve(oldabd);
+      tsbond(badv, abdsh);
+    }
+    tspivot(oldbcd, bcdsh);
+    if (bcdsh.sh != dummysh) {
+      tsdissolve(oldbcd);
+      tsbond(cbdv, bcdsh);
+    }
+    tspivot(oldcad, cadsh);
+    if (cadsh.sh != dummysh) {
+      tsdissolve(oldcad);
+      tsbond(acdv, cadsh);
+    }
+  }
+  badv.loc = 3; 
+  cbdv.loc = 2;
+  bond(badv, cbdv);
+  cbdv.loc = 3; 
+  acdv.loc = 2;
+  bond(cbdv, acdv);
+  acdv.loc = 3; 
+  badv.loc = 2;
+  bond(acdv, badv);
+  badv.loc = 1; 
+  bond(badv, oldabd);
+  cbdv.loc = 1; 
+  bond(cbdv, oldbcd);
+  acdv.loc = 1; 
+  bond(acdv, oldcad);
+  
+  badv.loc = 0;
+  cbdv.loc = 0;
+  acdv.loc = 0;
+  if (b->verbose > 3) {
+    printf("    Updating abcv ");
+    printtet(&abcv);
+    printf("    Creating badv ");
+    printtet(&badv);
+    printf("    Creating cbdv ");
+    printtet(&cbdv);
+    printf("    Creating acdv ");
+    printtet(&acdv);
+  }
+
+  if (flipqueue != (queue *) NULL) {
+    enqueueflipface(abcv, flipqueue);
+    enqueueflipface(badv, flipqueue);
+    enqueueflipface(cbdv, flipqueue);
+    enqueueflipface(acdv, flipqueue);
+  }
+
+  // Save a handle for quick point location.
+  recenttet = abcv;
+  // Set the return handle be abcv.
+  *splittet = abcv;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// unsplittetrahedron()    Reverse the operation of inserting a point into a //
+//                         tetrahedron, so as to remove the newly inserted   //
+//                         point from the mesh.                              //
+//                                                                           //
+// Assume the origional tetrahedron is abcd, it was split by v into four     //
+// tetrahedra abcv, badv, cbdv, and acdv. 'splittet' represents face abc of  //
+// abcv (i.e., its opposite is v).                                           //
+//                                                                           //
+// Point v is removed by expanding abcv to abcd, deleting three tetrahedra   //
+// badv, cbdv and acdv.  On return, point v is not deleted in this routine.  //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::unsplittetrahedron(triface* splittet)
+{
+  triface abcv, badv, cbdv, acdv;
+  triface oldabv, oldbcv, oldcav;
+  triface badcasing, cbdcasing, acdcasing;
+  face badsh, cbdsh, acdsh;
+
+  abcv = *splittet;
+  adjustedgering(abcv, CCW);  // for sure.
+  fnext(abcv, oldabv);
+  fnext(oldabv, badv);
+  esymself(badv);
+  enextfnext(abcv, oldbcv);
+  fnext(oldbcv, cbdv);
+  esymself(cbdv);
+  enext2fnext(abcv, oldcav);
+  fnext(oldcav, acdv);
+  esymself(acdv);
+
+  if (b->verbose > 1) {
+    printf("  Removing point %d in tetrahedron (%d, %d, %d, %d).\n",
+           pointmark(oppo(abcv)), pointmark(org(abcv)), pointmark(dest(abcv)),
+           pointmark(apex(abcv)), pointmark(apex(badv)));
+  }
+
+  sym(badv, badcasing);
+  tspivot(badv, badsh);
+  sym(cbdv, cbdcasing);
+  tspivot(cbdv, cbdsh);
+  sym(acdv, acdcasing);
+  tspivot(acdv, acdsh);
+
+  // Expanding abcv to abcd.
+  setoppo(abcv, apex(badv));
+  bond(oldabv, badcasing);
+  if (badsh.sh != dummysh) {
+    tsbond(oldabv, badsh);
+  }
+  bond(oldbcv, cbdcasing);
+  if (cbdsh.sh != dummysh) {
+    tsbond(oldbcv, cbdsh);
+  }
+  bond(oldcav, acdcasing);
+  if (acdsh.sh != dummysh) {
+    tsbond(oldcav, acdsh);
+  }
+
+  // Delete the three split-out tetrahedra.
+  tetrahedrondealloc(badv.tet);
+  tetrahedrondealloc(cbdv.tet);
+  tetrahedrondealloc(acdv.tet);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// splittetface()    Insert a point on a face of a mesh.                     //
+//                                                                           //
+// 'splittet' is the splitting face.  Let it is abcd, where abc is the face  //
+// will be split. If abc is not a hull face, abce is the tetrahedron at the  //
+// opposite of d.                                                            //
+//                                                                           //
+// To split face abc by a point v is to shrink the tetrahedra abcd to abvd,  //
+// create two new tetrahedra bcvd, cavd.  If abc is not a hull face, shrink  //
+// the tetrahedra bace to bave, create two new tetrahedra cbve, acve.        //
+//                                                                           //
+// If abc is a subface, it is split into three subfaces simultaneously by    //
+// calling routine splitsubface(), hence, abv, bcv, cav.  The edge rings of  //
+// the split subfaces have the same orientation as abc's.                    //
+//                                                                           //
+// On completion, 'splittet' returns abvd.  If 'flipqueue' is not NULL, it   //
+// contains all possibly non-locally Delaunay faces.                         //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::
+splittetface(point newpoint, triface* splittet, queue* flipqueue)
+{
+  triface abcd, bace;                                  // Old configuration.
+  triface oldbcd, oldcad, oldace, oldcbe; 
+  triface bcdcasing, cadcasing, acecasing, cbecasing;
+  face abcsh, bcdsh, cadsh, acesh, cbesh;
+  triface abvd, bcvd, cavd, bave, cbve, acve;          // New configuration.
+  point pa, pb, pc, pd, pe;
+  REAL attrib, volume;
+  bool mirrorflag;
+  int i;
+
+  abcd = *splittet;
+  // abcd.ver = 0; // Adjust to be CCW edge ring.
+  adjustedgering(abcd, CCW);
+  pa = org(abcd);
+  pb = dest(abcd);
+  pc = apex(abcd);
+  pd = oppo(abcd);
+  // Is there a second tetrahderon?
+  mirrorflag = issymexist(&abcd);
+  if (mirrorflag) {
+    // This is an interior face.
+    sym(abcd, bace);
+    findedge(&bace, dest(abcd), org(abcd));
+    pe = oppo(bace);
+  }
+  if (checksubfaces) {
+    // Is there a subface need to be split together?
+    tspivot(abcd, abcsh);
+    if (abcsh.sh != dummysh) {
+      // Exists! Keep the edge ab of both handles be the same.
+      findedge(&abcsh, org(abcd), dest(abcd));
+    }
+  }
+
+  if (b->verbose > 1) {
+    printf("  Inserting point %d on face (%d, %d, %d).\n", pointmark(newpoint),
+           pointmark(pa), pointmark(pb), pointmark(pc));
+  }
+
+#ifdef SELF_CHECK
+    // Make sure no inversed tetrahedron has been created.
+    assert(orient3d(pa, pb, pd, newpoint) >= 0.0);
+    assert(orient3d(pb, pc, pd, newpoint) >= 0.0);
+    assert(orient3d(pc, pa, pd, newpoint) >= 0.0);
+#endif
+
+  // Save the old configuration at faces bcd and cad.
+  enextfnext(abcd, oldbcd);
+  enext2fnext(abcd, oldcad);
+  sym(oldbcd, bcdcasing);
+  sym(oldcad, cadcasing);
+  // Create two new tetrahedra.
+  maketetrahedron(&bcvd);
+  maketetrahedron(&cavd);
+  if (mirrorflag) {
+    // Save the old configuration at faces bce and cae.
+    enextfnext(bace, oldace);
+    enext2fnext(bace, oldcbe);
+    sym(oldace, acecasing);
+    sym(oldcbe, cbecasing);
+    // Create two new tetrahedra.
+    maketetrahedron(&acve);
+    maketetrahedron(&cbve);
+  } else {
+    // Splitting a boundary face increases the number of boundary faces.
+    hullsize += 2;
+  }
+
+  // Set vertices to the changed tetrahedron and new tetrahedra.
+  abvd = abcd;  // Update 'abcd' to 'abvd'.
+  setapex(abvd, newpoint);
+  setorg (bcvd, pb);  // Set 'bcvd'.
+  setdest(bcvd, pc);
+  setapex(bcvd, newpoint);
+  setoppo(bcvd, pd);
+  setorg (cavd, pc);  // Set 'cavd'.
+  setdest(cavd, pa);
+  setapex(cavd, newpoint);
+  setoppo(cavd, pd);
+  // Set the element attributes of the new tetrahedra.
+  for (i = 0; i < in->numberoftetrahedronattributes; i++) {
+    attrib = elemattribute(abvd.tet, i);
+    setelemattribute(bcvd.tet, i, attrib);
+    setelemattribute(cavd.tet, i, attrib);
+  }
+  if (b->varvolume) {
+    // Set the area constraint of the new tetrahedra.
+    volume = volumebound(abvd.tet);
+    setvolumebound(bcvd.tet, volume);
+    setvolumebound(cavd.tet, volume);
+  }
+  if (mirrorflag) {
+    bave = bace;  // Update 'bace' to 'bave'.
+    setapex(bave, newpoint);
+    setorg (acve, pa);  // Set 'acve'.
+    setdest(acve, pc);
+    setapex(acve, newpoint);
+    setoppo(acve, pe);
+    setorg (cbve, pc);  // Set 'cbve'.
+    setdest(cbve, pb);
+    setapex(cbve, newpoint);
+    setoppo(cbve, pe);
+    // Set the element attributes of the new tetrahedra.
+    for (i = 0; i < in->numberoftetrahedronattributes; i++) {
+      attrib = elemattribute(bave.tet, i);
+      setelemattribute(acve.tet, i, attrib);
+      setelemattribute(cbve.tet, i, attrib);
+    }
+    if (b->varvolume) {
+      // Set the area constraint of the new tetrahedra.
+      volume = volumebound(bave.tet);
+      setvolumebound(acve.tet, volume);
+      setvolumebound(cbve.tet, volume);
+    }
+  }
+
+  // Bond the new tetrahedra to the surrounding tetrahedra.
+  bcvd.loc = 1;
+  bond(bcvd, bcdcasing); 
+  cavd.loc = 1;
+  bond(cavd, cadcasing); 
+  bcvd.loc = 3;
+  bond(bcvd, oldbcd);
+  cavd.loc = 2;
+  bond(cavd, oldcad);
+  bcvd.loc = 2;
+  cavd.loc = 3;
+  bond(bcvd, cavd);  
+  if (mirrorflag) {
+    acve.loc = 1;
+    bond(acve, acecasing);
+    cbve.loc = 1;
+    bond(cbve, cbecasing);
+    acve.loc = 3;
+    bond(acve, oldace);
+    cbve.loc = 2;
+    bond(cbve, oldcbe);
+    acve.loc = 2;
+    cbve.loc = 3;
+    bond(acve, cbve);
+    // Bond two new coplanar facets.
+    bcvd.loc = 0;
+    cbve.loc = 0;
+    bond(bcvd, cbve);
+    cavd.loc = 0;
+    acve.loc = 0;
+    bond(cavd, acve);
+  }
+
+  // There may exist subface needed to be bonded to the new tetrahedra.
+  if (checksubfaces) {
+    tspivot(oldbcd, bcdsh);
+    if (bcdsh.sh != dummysh) {
+      tsdissolve(oldbcd);
+      bcvd.loc = 1;
+      tsbond(bcvd, bcdsh);
+    }
+    tspivot(oldcad, cadsh);
+    if (cadsh.sh != dummysh) {
+      tsdissolve(oldcad);
+      cavd.loc = 1;
+      tsbond(cavd, cadsh);
+    }
+    if (mirrorflag) {
+      tspivot(oldace, acesh);
+      if (acesh.sh != dummysh) {
+        tsdissolve(oldace);
+        acve.loc = 1;
+        tsbond(acve, acesh);
+      }
+      tspivot(oldcbe, cbesh);
+      if (cbesh.sh != dummysh) {
+        tsdissolve(oldcbe);
+        cbve.loc = 1;
+        tsbond(cbve, cbesh);
+      }
+    }
+    // Is there a subface needs to be split together?
+    if (abcsh.sh != dummysh) {
+      // Split this subface 'abc' into three i.e, abv, bcv, cav.
+      splitsubface(newpoint, &abcsh, (queue *) NULL);
+    }  
+  }
+
+  // Save a handle for quick point location.
+  recenttet = abvd;
+  // Set the return handle be abvd.
+  *splittet = abvd;
+
+  bcvd.loc = 0;
+  cavd.loc = 0;
+  if (mirrorflag) {
+    cbve.loc = 0;
+    acve.loc = 0;
+  }
+  if (b->verbose > 3) {
+    printf("    Updating abvd ");
+    printtet(&abvd);
+    printf("    Creating bcvd ");
+    printtet(&bcvd);
+    printf("    Creating cavd ");
+    printtet(&cavd);
+    if (mirrorflag) {
+      printf("    Updating bave ");
+      printtet(&bave);
+      printf("    Creating cbve ");
+      printtet(&cbve);
+      printf("    Creating acve ");
+      printtet(&acve);
+    }
+  }
+
+  if (flipqueue != (queue *) NULL) {
+    fnextself(abvd);
+    enqueueflipface(abvd, flipqueue);
+    fnextself(bcvd);
+    enqueueflipface(bcvd, flipqueue);
+    fnextself(cavd);
+    enqueueflipface(cavd, flipqueue);
+    if (mirrorflag) {
+      fnextself(bave);
+      enqueueflipface(bave, flipqueue);
+      fnextself(cbve);
+      enqueueflipface(cbve, flipqueue);
+      fnextself(acve);
+      enqueueflipface(acve, flipqueue);
+    }
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// unsplittetface()    Reverse the operation of inserting a point on a face, //
+//                     so as to remove the newly inserted point.             //
+//                                                                           //
+// Assume the original face is abc, the tetrahedron containing abc is abcd.  //
+// If abc is not a hull face, bace is the tetrahedron at the opposite of d.  //
+// After face abc was split by a point v, tetrahedron abcd had been split    //
+// into three tetrahedra, abvd, bcvd, cavd, and bace (if it exists) had been //
+// split into bave, cbve, acve. 'splittet' represents abvd (its apex is v).  //
+//                                                                           //
+// Point v is removed by expanding abvd to abcd, deleting two tetrahedra     //
+// bcvd, cavd. Expanding bave(if it exists) to bace, deleting two tetrahedra //
+// cbve, acve.  If abv is a subface, routine unsplitsubface() will be called //
+// to reverse the operation of splitting a subface. On completion, point v   //
+// is not deleted in this routine.                                           //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::unsplittetface(triface* splittet)
+{
+  triface abvd, bcvd, cavd, bave, cbve, acve;
+  triface oldbvd, oldvad, oldvbe, oldave;
+  triface bcdcasing, cadcasing, cbecasing, acecasing;
+  face bcdsh, cadsh, cbesh, acesh;
+  face abvsh;
+  bool mirrorflag;
+
+  abvd = *splittet;
+  adjustedgering(abvd, CCW); // for sure.
+  enextfnext(abvd, oldbvd);
+  fnext(oldbvd, bcvd);
+  esymself(bcvd);
+  enextself(bcvd);
+  enext2fnext(abvd, oldvad);
+  fnext(oldvad, cavd);
+  esymself(cavd);
+  enext2self(cavd);
+  // Is there a second tetrahedron?
+  sym(abvd, bave);
+  mirrorflag = bave.tet != dummytet;
+  if (mirrorflag) {
+    findedge(&bave, dest(abvd), org(abvd));
+    enextfnext(bave, oldave);  
+    fnext(oldave, acve);
+    esymself(acve);
+    enextself(acve);
+    enext2fnext(bave, oldvbe);  
+    fnext(oldvbe, cbve);
+    esymself(cbve);
+    enext2self(cbve);
+  } else {
+    // Unsplit a hull face decrease the number of boundary faces.
+    hullsize -= 2;
+  }
+  // Is there a subface at abv.
+  tspivot(abvd, abvsh);
+  if (abvsh.sh != dummysh) {
+    // Exists! Keep the edge ab of both handles be the same.
+    findedge(&abvsh, org(abvd), dest(abvd));
+  }
+
+  if (b->verbose > 1) {
+    printf("  Removing point %d on face (%d, %d, %d).\n",
+           pointmark(apex(abvd)), pointmark(org(abvd)), pointmark(dest(abvd)),
+           pointmark(dest(bcvd)));
+  }
+
+  fnextself(bcvd); // bcvd has changed to bcdv.
+  sym(bcvd, bcdcasing);
+  tspivot(bcvd, bcdsh);
+  fnextself(cavd); // cavd has changed to cadv.
+  sym(cavd, cadcasing);
+  tspivot(cavd, cadsh);
+  if (mirrorflag) {
+    fnextself(acve); // acve has changed to acev.
+    sym(acve, acecasing);
+    tspivot(acve, acesh);
+    fnextself(cbve); // cbve has changed to cbev.
+    sym(cbve, cbecasing);
+    tspivot(cbve, cbesh);
+  }
+
+  // Expand abvd to abcd.
+  setapex(abvd, dest(bcvd));
+  bond(oldbvd, bcdcasing);
+  if (bcdsh.sh != dummysh) {
+    tsbond(oldbvd, bcdsh);
+  }
+  bond(oldvad, cadcasing);
+  if (cadsh.sh != dummysh) {
+    tsbond(oldvad, cadsh);
+  }
+  if (mirrorflag) {
+    // Expanding bave to bace.
+    setapex(bave, dest(acve));
+    bond(oldave, acecasing);
+    if (acesh.sh != dummysh) {
+      tsbond(oldave, acesh);
+    }
+    bond(oldvbe, cbecasing);
+    if (cbesh.sh != dummysh) {
+      tsbond(oldvbe, cbesh);
+    }
+  }
+
+  // Unsplit a subface if there exists.
+  if (abvsh.sh != dummysh) {
+    unsplitsubface(&abvsh);
+  }
+
+  // Delete the split-out tetrahedra.
+  tetrahedrondealloc(bcvd.tet);
+  tetrahedrondealloc(cavd.tet);
+  if (mirrorflag) {
+    tetrahedrondealloc(acve.tet);
+    tetrahedrondealloc(cbve.tet);
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// splitsubface()    Insert a point on a subface, split it into three.       //
+//                                                                           //
+// The subface is 'splitface'.  Let it is abc. The inserting point 'newpoint'//
+// v should lie inside abc.  If the neighbor tetrahedra of abc exist, i.e.,  //
+// abcd and bace, they should have been split by routine splittetface()      //
+// before calling this routine, so the connection between the new tetrahedra //
+// and new subfaces can be correctly set.                                    //
+//                                                                           //
+// To split subface abc by point v is to shrink abc to abv, create two new   //
+// subfaces bcv and cav.  Set the connection between updated and new created //
+// subfaces. If there is a subsegment at edge bc or ca, connection of new    //
+// subface (bcv or cav) to its casing subfaces is a face link, 'casingin' is //
+// the predecessor and 'casingout' is the successor. It is important to keep //
+// the orientations of the edge rings of the updated and created subfaces be //
+// the same as abc's. So they have the same orientation as other subfaces of //
+// this facet with respect to the lift point of this facet.                  //
+//                                                                           //
+// On completion, 'splitface' returns abv.  If 'flipqueue' is not NULL, it   //
+// returns all possibly non-Delaunay edges.                                  //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::
+splitsubface(point newpoint, face* splitface, queue* flipqueue)
+{
+  triface abvd, bcvd, cavd, bave, cbve, acve;
+  face abc, oldbc, oldca, bc, ca, spinsh;
+  face bccasin, bccasout, cacasin, cacasout;
+  face abv, bcv, cav;
+  point pa, pb, pc;
+  
+  abc = *splitface;
+  // The newly created subfaces will have the same edge ring as abc.
+  adjustedgering(abc, CCW);
+  pa = sorg(abc);
+  pb = sdest(abc);
+  pc = sapex(abc);
+
+  if (b->verbose > 1) {
+    printf("  Inserting point %d on subface (%d, %d, %d).\n",
+           pointmark(newpoint), pointmark(pa), pointmark(pb), pointmark(pc));
+  }
+
+  // Save the old configuration at edge bc and ca.  Subsegments may appear
+  //   at both sides, save the face links and dissolve them.
+  senext(abc, oldbc);
+  senext2(abc, oldca);
+  spivot(oldbc, bccasout);
+  sspivot(oldbc, bc);
+  if (bc.sh != dummysh) {
+    if (oldbc.sh != bccasout.sh) {
+      // 'oldbc' is not self-bonded.
+      spinsh = bccasout;
+      do {
+        bccasin = spinsh;
+        spivotself(spinsh);
+      } while (spinsh.sh != oldbc.sh);
+    } else {
+      bccasout.sh = dummysh;
+    }
+    ssdissolve(oldbc);
+  } 
+  spivot(oldca, cacasout);
+  sspivot(oldca, ca);
+  if (ca.sh != dummysh) {
+    if (oldca.sh != cacasout.sh) {
+      // 'oldca' is not self-bonded.
+      spinsh = cacasout;
+      do {
+        cacasin = spinsh;
+        spivotself(spinsh);
+      } while (spinsh.sh != oldca.sh);
+    } else {
+      cacasout.sh = dummysh;
+    }
+    ssdissolve(oldca);
+  }
+  // Create two new subfaces.
+  makeshellface(subfaces, &bcv);
+  makeshellface(subfaces, &cav);
+
+  // Set the vertices of changed and new subfaces.
+  abv = abc;  // Update 'abc' to 'abv'.
+  setsapex(abv, newpoint);
+  setsorg(bcv, pb);  // Set 'bcv'.
+  setsdest(bcv, pc);
+  setsapex(bcv, newpoint);
+  setsorg(cav, pc);  // Set 'cav'.
+  setsdest(cav, pa);
+  setsapex(cav, newpoint);
+  if (b->quality) {
+    // Copy yhr area bound into the new subfaces.
+    setareabound(bcv, areabound(abv));
+    setareabound(cav, areabound(abv));
+  }
+  // Copy the boundary mark into the new subfaces.
+  setshellmark(bcv, shellmark(abv));
+  setshellmark(cav, shellmark(abv));  
+  // Copy the subface type into the new subfaces.
+  setshelltype(bcv, shelltype(abv));
+  setshelltype(cav, shelltype(abv));
+  // Bond the new subfaces to the surrounding subfaces.
+  if (bc.sh != dummysh) {
+    if (bccasout.sh != dummysh) {
+      sbond1(bccasin, bcv);
+      sbond1(bcv, bccasout);
+    } else {
+      // Bond 'bcv' to itsself.
+      sbond(bcv, bcv);
+    }
+    ssbond(bcv, bc);
+  } else {
+    sbond(bcv, bccasout);
+  }
+  if (ca.sh != dummysh) {
+    if (cacasout.sh != dummysh) {
+      sbond1(cacasin, cav);
+      sbond1(cav, cacasout);
+    } else {
+      // Bond 'cav' to itself.
+      sbond(cav, cav);
+    }
+    ssbond(cav, ca);
+  } else {
+    sbond(cav, cacasout);
+  }
+  senext2self(bcv);
+  sbond(bcv, oldbc);
+  senextself(cav);
+  sbond(cav, oldca);
+  senext2self(bcv);
+  senextself(cav);
+  sbond(bcv, cav);
+
+  // Bond the new subfaces to the new tetrahedra if they exist.
+  stpivot(abv, abvd);
+  if (abvd.tet != dummytet) {
+    // Get two new tetrahedra and their syms.
+    findedge(&abvd, sorg(abv), sdest(abv));
+    enextfnext(abvd, bcvd);
+    assert(bcvd.tet != dummytet);
+    fnextself(bcvd);
+    enext2fnext(abvd, cavd);
+    assert(cavd.tet != dummytet);
+    fnextself(cavd);
+    // Bond two new subfaces to the two new tetrahedra.
+    tsbond(bcvd, bcv);
+    tsbond(cavd, cav);
+  }
+  // Set the connection at the other sides if the tetrahedra exist.
+  sesymself(abv);  // bav
+  stpivot(abv, bave);
+  if (bave.tet != dummytet) {
+    sesymself(bcv);  // cbv
+    sesymself(cav);  // acv
+    // Get two new tetrahedra and their syms.
+    findedge(&bave, sorg(abv), sdest(abv));
+    enextfnext(bave, acve);
+    assert(acve.tet != dummytet);
+    fnextself(acve);
+    enext2fnext(bave, cbve);
+    assert(cbve.tet != dummytet);
+    fnextself(cbve);
+    // Bond two new subfaces to the two new tetrahedra.
+    tsbond(acve, cav);
+    tsbond(cbve, bcv);
+  }
+
+  bcv.shver = 0;
+  cav.shver = 0;
+  if (b->verbose > 3) {
+    printf("    Updating abv ");
+    printsh(&abv);
+    printf("    Creating bcv ");
+    printsh(&bcv);
+    printf("    Creating cav ");
+    printsh(&cav);
+  }
+
+  if (flipqueue != (queue *) NULL) {
+    enqueueflipedge(abv, flipqueue);
+    enqueueflipedge(bcv, flipqueue);
+    enqueueflipedge(cav, flipqueue);
+  }
+
+  // Set the return handle be abv.
+  *splitface = abv;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// unsplitsubface()    Reverse the operation of inserting a point on a       //
+//                     subface, so as to remove the newly inserted point.    //
+//                                                                           //
+// Assume the original subface is abc, it was split by a point v into three  //
+// subfaces abv, bcv and cav.  'splitsh' represents abv.                     //
+//                                                                           //
+// To remove point v is to expand abv to abc, delete bcv and cav. If edge bc //
+// or ca is a subsegment,  the connection at a subsegment is a subface link, //
+// '-casin' and '-casout' are used to save the predecessor and successor of  //
+// bcv or cav.  On completion, point v is not deleted in this routine.       //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::unsplitsubface(face* splitsh)
+{
+  face abv, bcv, cav;
+  face oldbv, oldva, bc, ca, spinsh;
+  face bccasin, bccasout, cacasin, cacasout;
+
+  abv = *splitsh;
+  senext(abv, oldbv);
+  spivot(oldbv, bcv);
+  if (sorg(bcv) != sdest(oldbv)) {
+    sesymself(bcv);
+  }
+  senextself(bcv);
+  senext2(abv, oldva);
+  spivot(oldva, cav);
+  if (sorg(cav) != sdest(oldva)) {
+    sesymself(cav);
+  }
+  senext2self(cav);
+
+  if (b->verbose > 1) {
+    printf("  Removing point %d on subface (%d, %d, %d).\n",
+           pointmark(sapex(abv)), pointmark(sorg(abv)), pointmark(sdest(abv)),
+           pointmark(sdest(bcv)));
+  }
+
+  spivot(bcv, bccasout);
+  sspivot(bcv, bc);
+  if (bc.sh != dummysh) {
+    if (bcv.sh != bccasout.sh) {
+      // 'bcv' is not self-bonded.
+      spinsh = bccasout;
+      do {
+        bccasin = spinsh;
+        spivotself(spinsh);
+      } while (spinsh.sh != bcv.sh);
+    } else {
+      bccasout.sh = dummysh;
+    }
+  }
+  spivot(cav, cacasout);
+  sspivot(cav, ca);
+  if (ca.sh != dummysh) {
+    if (cav.sh != cacasout.sh) {
+      // 'cav' is not self-bonded.
+      spinsh = cacasout;
+      do {
+       cacasin = spinsh;
+       spivotself(spinsh);
+      } while (spinsh.sh != cav.sh);
+    } else {
+      cacasout.sh = dummysh;
+    }
+  }
+
+  // Expand abv to abc.
+  setsapex(abv, sdest(bcv));
+  if (bc.sh != dummysh) {
+    if (bccasout.sh != dummysh) {
+      sbond1(bccasin, oldbv);
+      sbond1(oldbv, bccasout);
+    } else {
+      // Bond 'oldbv' to itself.
+      sbond(oldbv, oldbv);
+    }
+    ssbond(oldbv, bc);
+  } else {
+    sbond(oldbv, bccasout);
+  } 
+  if (ca.sh != dummysh) {
+    if (cacasout.sh != dummysh) {
+      sbond1(cacasin, oldva);
+      sbond1(oldva, cacasout);
+    } else {
+      // Bond 'oldva' to itself.
+      sbond(oldva, oldva);
+    }
+    ssbond(oldva, ca);
+  } else {
+    sbond(oldva, cacasout);
+  }
+
+  // Delete two split-out subfaces.
+  shellfacedealloc(subfaces, bcv.sh);
+  shellfacedealloc(subfaces, cav.sh);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// splittetedge()    Insert a point on an edge of the mesh.                  //
+//                                                                           //
+// The edge is given by 'splittet'. Assume its four corners are a, b, n1 and //
+// n2, where ab is the edge will be split. Around ab may exist any number of //
+// tetrahedra. For convenience, they're ordered in a sequence following the  //
+// right-hand rule with your thumb points from a to b. Let the vertex set of //
+// these tetrahedra be {a, b, n1, n2, ..., n(i)}. NOTE the tetrahedra around //
+// ab may not connect to each other (can only happen when ab is a subsegment,//
+// hence some faces abn(i) are subfaces).  If ab is a subsegment, abn1 must  //
+// be a subface.                                                             //
+//                                                                           //
+// To split edge ab by a point v is to split all tetrahedra containing ab by //
+// v.  More specifically, for each such tetrahedron, an1n2b, it is shrunk to //
+// an1n2v, and a new tetrahedra bn2n1v is created. If ab is a subsegment, or //
+// some faces of the splitting tetrahedra are subfaces, they must be split   //
+// either by calling routine 'splitsubedge()'.                               //
+//                                                                           //
+// On completion, 'splittet' returns avn1n2.  If 'flipqueue' is not NULL, it //
+// returns all faces which may become non-Delaunay after this operation.     //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::
+splittetedge(point newpoint, triface* splittet, queue* flipqueue)
+{
+  triface *bots, *newtops;
+  triface oldtop, topcasing;
+  triface spintet, tmpbond0, tmpbond1;
+  face abseg, splitsh, topsh, spinsh;
+  point pa, pb, n1, n2;
+  REAL attrib, volume;
+  int wrapcount, hitbdry;
+  int i, j;
+
+  if (checksubfaces) {
+    // Is there a subsegment need to be split together?
+    tsspivot(splittet, &abseg);
+    if (abseg.sh != dummysh) {
+      abseg.shver = 0;
+      // Orient the edge direction of 'splittet' be abseg.
+      if (org(*splittet) != sorg(abseg)) {
+        esymself(*splittet);
+      }
+    }
+  } 
+  spintet = *splittet;
+  pa = org(spintet);
+  pb = dest(spintet);
+
+  if (b->verbose > 1) {
+    printf("  Inserting point %d on edge (%d, %d).\n", 
+           pointmark(newpoint), pointmark(pa), pointmark(pb));
+  }
+
+  // Collect the tetrahedra containing the splitting edge (ab).
+  n1 = apex(spintet);
+  hitbdry = 0;
+  wrapcount = 1;
+  if (checksubfaces && abseg.sh != dummysh) {
+    // It may happen that some tetrahedra containing ab (a subsegment) are
+    //   completely disconnected with others. If it happens, use the face
+    //   link of ab to cross the boundary. 
+    while (true) {
+      if (!fnextself(spintet)) {
+        // Meet a boundary, walk through it.
+        hitbdry ++;
+        tspivot(spintet, spinsh);
+        assert(spinsh.sh != dummysh);
+        findedge(&spinsh, pa, pb);
+        sfnextself(spinsh);
+        stpivot(spinsh, spintet);
+        assert(spintet.tet != dummytet);
+        findedge(&spintet, pa, pb);
+        // Remember this position (hull face) in 'splittet'.
+        *splittet = spintet;
+        // Split two hull faces increase the hull size;
+        hullsize += 2;
+      }
+      if (apex(spintet) == n1) break;
+      wrapcount ++;
+    }
+    if (hitbdry > 0) {
+      wrapcount -= hitbdry;
+    }
+  } else {
+    // All the tetrahedra containing ab are connected together. If there
+    //   are subfaces, 'splitsh' keeps one of them.
+    splitsh.sh = dummysh;
+    while (hitbdry < 2) {
+      if (checksubfaces && splitsh.sh == dummysh) {
+        tspivot(spintet, splitsh);
+      }
+      if (fnextself(spintet)) {
+        if (apex(spintet) == n1) break;
+        wrapcount++;
+      } else {
+        hitbdry ++;
+        if (hitbdry < 2) {
+          esym(*splittet, spintet);
+        }
+      }
+    }
+    if (hitbdry > 0) {
+      // ab is on the hull.
+      wrapcount -= 1;
+      // 'spintet' now is a hull face, inverse its edge direction.
+      esym(spintet, *splittet);
+      // Split two hull faces increases the number of hull faces.
+      hullsize += 2;
+    }
+  }
+  
+  // Make arrays of updating (bot, oldtop) and new (newtop) tetrahedra.
+  bots = new triface[wrapcount];
+  newtops = new triface[wrapcount];
+  // Spin around ab, gather tetrahedra and set up new tetrahedra. 
+  spintet = *splittet;
+  for (i = 0; i < wrapcount; i++) {
+    // Get 'bots[i] = an1n2b'.
+    enext2fnext(spintet, bots[i]);
+    esymself(bots[i]);
+    // Create 'newtops[i]'.
+    maketetrahedron(&(newtops[i]));
+    // Go to the next.
+    fnextself(spintet);
+    if (checksubfaces && abseg.sh != dummysh) {
+      if (!issymexist(&spintet)) {
+        // We meet a hull face, walk through it.
+        tspivot(spintet, spinsh);
+        assert(spinsh.sh != dummysh);
+        findedge(&spinsh, pa, pb);
+        sfnextself(spinsh);
+        stpivot(spinsh, spintet);
+        assert(spintet.tet != dummytet);
+        findedge(&spintet, pa, pb);
+      }
+    }
+  }
+  
+  // Set the vertices of updated and new tetrahedra.
+  for (i = 0; i < wrapcount; i++) {
+    // Update 'bots[i] = an1n2v'.
+    setoppo(bots[i], newpoint);
+    // Set 'newtops[i] = bn2n1v'.
+    n1 = dest(bots[i]);
+    n2 = apex(bots[i]);
+    // Set 'newtops[i]'.
+    setorg(newtops[i], pb);
+    setdest(newtops[i], n2);
+    setapex(newtops[i], n1);
+    setoppo(newtops[i], newpoint);
+    // Set the element attributes of a new tetrahedron.
+    for (j = 0; j < in->numberoftetrahedronattributes; j++) {
+      attrib = elemattribute(bots[i].tet, j);
+      setelemattribute(newtops[i].tet, j, attrib);
+    }
+    if (b->varvolume) {
+      // Set the area constraint of a new tetrahedron.
+      volume = volumebound(bots[i].tet);
+      setvolumebound(newtops[i].tet, volume);
+    }
+#ifdef SELF_CHECK
+    // Make sure no inversed tetrahedron has been created.
+    assert(orient3d(pa, n1, n2, newpoint) <= 0.0);
+    assert(orient3d(pb, n2, n1, newpoint) <= 0.0);
+#endif
+  }
+
+  // Bond newtops to topcasings and bots.
+  for (i = 0; i < wrapcount; i++) {
+    // Get 'oldtop = n1n2va' from 'bots[i]'.
+    enextfnext(bots[i], oldtop);
+    sym(oldtop, topcasing);
+    bond(newtops[i], topcasing);
+    if (checksubfaces) {
+      tspivot(oldtop, topsh);
+      if (topsh.sh != dummysh) {
+        tsdissolve(oldtop);
+        tsbond(newtops[i], topsh);
+      }
+    }
+    enextfnext(newtops[i], tmpbond0);
+    bond(oldtop, tmpbond0);
+  }
+  // Bond between newtops.
+  fnext(newtops[0], tmpbond0);
+  enext2fnext(bots[0], spintet); 
+  for (i = 1; i < wrapcount; i ++) {
+    if (issymexist(&spintet)) {
+      enext2fnext(newtops[i], tmpbond1);
+      bond(tmpbond0, tmpbond1);
+    }
+    fnext(newtops[i], tmpbond0);
+    enext2fnext(bots[i], spintet); 
+  }
+  // Bond the last to the first if no boundary.
+  if (issymexist(&spintet)) {
+    enext2fnext(newtops[0], tmpbond1);
+    bond(tmpbond0, tmpbond1);
+  }
+
+  // Is there exist subfaces and subsegment need to be split?
+  if (checksubfaces) {
+    if (abseg.sh != dummysh) {
+      // A subsegment needs be split.
+      spivot(abseg, splitsh);
+      assert(splitsh.sh != dummysh);
+    }
+    if (splitsh.sh != dummysh) {
+      // Split subfaces (and subsegment).
+      findedge(&splitsh, pa, pb);
+      splitsubedge(newpoint, &splitsh, (queue *) NULL);
+    }
+  }
+
+  if (b->verbose > 3) {
+    for (i = 0; i < wrapcount; i++) {
+      printf("    Updating bots[%i] ", i);
+      printtet(&(bots[i]));
+      printf("    Creating newtops[%i] ", i);
+      printtet(&(newtops[i]));
+    }
+  }
+
+  if (flipqueue != (queue *) NULL) {
+    for (i = 0; i < wrapcount; i++) {
+      enqueueflipface(bots[i], flipqueue);
+      enqueueflipface(newtops[i], flipqueue);
+    }
+  }
+
+  // Set the return handle be avn1n2.  It is got by transforming from
+  //   'bots[0]' (which is an1n2v).
+  fnext(bots[0], spintet); // spintet is an1vn2.
+  esymself(spintet); // spintet is n1avn2.
+  enextself(spintet); // spintet is avn1n2.
+  *splittet = spintet;
+
+  delete [] bots;
+  delete [] newtops;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// unsplittetedge()    Reverse the operation of splitting an edge, so as to  //
+//                     remove the newly inserted point.                      //
+//                                                                           //
+// Assume the original edge is ab, the tetrahedron containing ab is abn1n2.  //
+// After ab was split by a point v, every tetrahedron containing ab (e.g.,   //
+// abn1n2) has been split into two (e.g., an1n2v and bn2n1v). 'splittet'     //
+// represents avn1n2 (i.e., its destination is v).                           //
+//                                                                           //
+// To remove point v is to expand each split tetrahedron containing ab (e.g.,//
+// (avn1n2 to abn1n2), then delete the redundant one(e.g., vbn1n2). If there //
+// exists any subface around ab, routine unsplitsubedge() will be called to  //
+// reverse the operation of splitting a edge (or a subsegment) of subfaces.  //
+// On completion, point v is not deleted in this routine.                    //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::unsplittetedge(triface* splittet)
+{
+  triface *bots, *newtops;
+  triface oldtop, topcasing;
+  triface spintet;
+  face avseg, splitsh, topsh, spinsh;
+  point pa, pv, n1;
+  int wrapcount, hitbdry;
+  int i;
+
+  spintet = *splittet;
+  pa = org(spintet);
+  pv = dest(spintet);
+  if (checksubfaces) {
+    // Is there a subsegment need to be unsplit together?
+    tsspivot(splittet, &avseg);
+    if (avseg.sh != dummysh) {
+      // The subsegment's direction should conform to 'splittet'.
+      if (sorg(avseg) != pa) {
+        sesymself(avseg);
+      }
+    }
+  } 
+
+  n1 = apex(spintet);
+  hitbdry = 0;
+  wrapcount = 1;
+  if (checksubfaces && avseg.sh != dummysh) {
+    // It may happen that some tetrahedra containing ab (a subsegment) are
+    //   completely disconnected with others. If it happens, use the face
+    //   link of ab to cross the boundary. 
+    while (true) {    
+      if (!fnextself(spintet)) {
+        // Meet a boundary, walk through it.
+        hitbdry ++;
+        tspivot(spintet, spinsh);
+        assert(spinsh.sh != dummysh);
+        findedge(&spinsh, pa, pv);
+        sfnextself(spinsh);
+        stpivot(spinsh, spintet);
+        assert(spintet.tet != dummytet);
+        findedge(&spintet, pa, pv);
+        // Remember this position (hull face) in 'splittet'.
+        *splittet = spintet;
+        // Split two hull faces increase the hull size;
+        hullsize += 2;
+      }
+      if (apex(spintet) == n1) break;
+      wrapcount ++;
+    }
+    if (hitbdry > 0) {
+      wrapcount -= hitbdry;
+    }
+  } else {
+    // All the tetrahedra containing ab are connected together. If there
+    //   are subfaces, 'splitsh' keeps one of them.
+    splitsh.sh = dummysh;
+    while (hitbdry < 2) {
+      if (checksubfaces && splitsh.sh == dummysh) {
+        tspivot(spintet, splitsh);
+      }
+      if (fnextself(spintet)) {
+        if (apex(spintet) == n1) break;
+        wrapcount++;
+      } else {
+        hitbdry ++;
+        if (hitbdry < 2) {
+          esym(*splittet, spintet);
+        }
+      }
+    }
+    if (hitbdry > 0) {
+      // ab is on the hull.
+      wrapcount -= 1;
+      // 'spintet' now is a hull face, inverse its edge direction.
+      esym(spintet, *splittet);
+      // Split two hull faces increases the number of hull faces.
+      hullsize += 2;
+    }
+  }
+  
+  // Make arrays of updating (bot, oldtop) and new (newtop) tetrahedra.
+  bots = new triface[wrapcount];
+  newtops = new triface[wrapcount];
+  // Spin around av, gather tetrahedra and set up new tetrahedra. 
+  spintet = *splittet;
+  for (i = 0; i < wrapcount; i++) {
+    // Get 'bots[i] = an1n2v'.
+    enext2fnext(spintet, bots[i]);
+    esymself(bots[i]);
+    // Get 'oldtop = n1n2va'.
+    enextfnext(bots[i], oldtop);
+    // Get 'newtops[i] = 'bn1n2v'
+    fnext(oldtop, newtops[i]); // newtop = n1n2bv
+    esymself(newtops[i]); // newtop = n2n1bv
+    enext2self(newtops[i]); // newtop = bn2n1v
+    // Go to the next.
+    fnextself(spintet);
+    if (checksubfaces && avseg.sh != dummysh) {
+      if (!issymexist(&spintet)) {
+        // We meet a hull face, walk through it.
+        tspivot(spintet, spinsh);
+        assert(spinsh.sh != dummysh);
+        findedge(&spinsh, pa, pv);
+        sfnextself(spinsh);
+        stpivot(spinsh, spintet);
+        assert(spintet.tet != dummytet);
+        findedge(&spintet, pa, pv);
+      }
+    }
+  }
+
+  if (b->verbose > 1) {
+    printf("  Removing point %d from edge (%d, %d).\n", 
+           pointmark(oppo(bots[0])), pointmark(org(bots[0])),
+           pointmark(org(newtops[0])));
+  }
+
+  for (i = 0; i < wrapcount; i++) {
+    // Expand an1n2v to an1n2b.
+    setoppo(bots[i], org(newtops[i]));
+    // Get 'oldtop = n1n2va' from 'bot[i]'.
+    enextfnext(bots[i], oldtop);
+    // Get 'topcasing' from 'newtop[i]'
+    sym(newtops[i], topcasing);
+    // Bond them.
+    bond(oldtop, topcasing);
+    if (checksubfaces) {
+      tspivot(newtops[i], topsh);
+      if (topsh.sh != dummysh) {
+        tsbond(oldtop, topsh);
+      }
+    }
+    // Delete the tetrahedron above an1n2v.
+    tetrahedrondealloc(newtops[i].tet);
+  }
+
+  // If there exists any subface, unsplit them.
+  if (checksubfaces) {
+    if (avseg.sh != dummysh) {
+      spivot(avseg, splitsh);
+      assert(splitsh.sh != dummysh);
+    }
+    if (splitsh.sh != dummysh) {
+      findedge(&splitsh, pa, pv);
+      unsplitsubedge(&splitsh);
+    }
+  }
+
+  delete [] bots;
+  delete [] newtops;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// splitsubedge()    Insert a point on an edge of the surface mesh.          //
+//                                                                           //
+// The splitting edge is given by 'splitsh'. Assume its three corners are a, //
+// b, c, where ab is the edge will be split. ab may be a subsegment.         //
+//                                                                           //
+// To split edge ab is to split all subfaces conatining ab. If ab is not a   //
+// subsegment, there are only two subfaces need be split, otherwise, there   //
+// may have any number of subfaces need be split. Each splitting subface abc //
+// is shrunk to avc, a new subface vbc is created.  It is important to keep  //
+// the orientations of edge rings of avc and vbc be the same as abc's. If ab //
+// is a subsegment, it is shrunk to av and a new subsegment vb is created.   //
+//                                                                           //
+// If there are tetrahedra adjoining to the splitting subfaces, they should  //
+// be split before calling this routine, so the connection between the new   //
+// tetrahedra and the new subfaces can be correctly set.                     //
+//                                                                           //
+// On completion, 'splitsh' returns avc.  If 'flipqueue' is not NULL, it     //
+// returns all edges which may be non-Delaunay.                              //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::splitsubedge(point newpoint, face* splitsh, queue* flipqueue)
+{
+  triface abcd, bace, vbcd, bvce;
+  face startabc, spinabc, spinsh;
+  face oldbc, bccasin, bccasout;
+  face ab, bc;
+  face avc, vbc, vbc1;
+  face av, vb;
+  point pa, pb;
+
+  startabc = *splitsh;
+  // Is there a subsegment?
+  sspivot(startabc, ab);
+  if (ab.sh != dummysh) {
+    ab.shver = 0; 
+    if (sorg(startabc) != sorg(ab)) {
+      sesymself(startabc);
+    }
+  }
+  pa = sorg(startabc);
+  pb = sdest(startabc);
+  
+  if (b->verbose > 1) {
+    printf("  Inserting point %d on subedge (%d, %d).\n",
+           pointmark(newpoint), pointmark(pa), pointmark(pb));
+  }
+  
+  // Spin arround ab, split every subface containing ab.
+  spinabc = startabc;
+  do {
+    // Adjust spinabc be edge ab.
+    if (sorg(spinabc) != pa) {
+      sesymself(spinabc);
+    }
+    // Save old configuration at edge bc, if bc has a subsegment, save the
+    //   face link of it and dissolve it from bc.
+    senext(spinabc, oldbc);
+    spivot(oldbc, bccasout);    
+    sspivot(oldbc, bc);
+    if (bc.sh != dummysh) {
+      if (spinabc.sh != bccasout.sh) {
+        // 'spinabc' is not self-bonded.
+        spinsh = bccasout;
+        do {
+          bccasin = spinsh;
+          spivotself(spinsh);
+        } while (spinsh.sh != oldbc.sh);
+      } else {
+        bccasout.sh = dummysh;
+      }
+      ssdissolve(oldbc);
+    }
+    // Create a new subface.
+    makeshellface(subfaces, &vbc);
+    // Split abc.
+    avc = spinabc;  // Update 'abc' to 'avc'.
+    setsdest(avc, newpoint);
+    // Make 'vbc' be in the same edge ring as 'avc'. 
+    vbc.shver = avc.shver; 
+    setsorg(vbc, newpoint); // Set 'vbc'.
+    setsdest(vbc, pb);
+    setsapex(vbc, sapex(avc));
+    if (b->quality) {
+      // Copy yhr area bound into the new subfaces.
+      setareabound(vbc, areabound(avc));
+    }
+    // Copy the shell marker and shell type into the new subface.
+    setshellmark(vbc, shellmark(avc));
+    setshelltype(vbc, shelltype(avc));
+    // Set the connection between updated and new subfaces.
+    senext2self(vbc);
+    sbond(vbc, oldbc);
+    // Set the connection between new subface and casings.
+    senext2self(vbc);
+    if (bc.sh != dummysh) {
+      if (bccasout.sh != dummysh) {
+        // Insert 'vbc' into face link.
+        sbond1(bccasin, vbc);
+        sbond1(vbc, bccasout);
+      } else {
+        // Bond 'vbc' to itself.
+        sbond(vbc, vbc);
+      }
+      ssbond(vbc, bc);
+    } else {
+      sbond(vbc, bccasout);
+    }
+    // Go to next subface at edge ab.
+    spivotself(spinabc);
+    if (spinabc.sh == dummysh) {
+      break; // 'ab' is a hull edge.
+    }
+  } while (spinabc.sh != startabc.sh);
+
+  // Get the new subface vbc above the updated subface avc (= startabc).
+  senext(startabc, oldbc);
+  spivot(oldbc, vbc);
+  if (sorg(vbc) == newpoint) {
+    sesymself(vbc);
+  }
+  assert(sorg(vbc) == sdest(oldbc) && sdest(vbc) == sorg(oldbc));
+  senextself(vbc);
+  // Set the face link for the new created subfaces around edge vb.
+  spinabc = startabc;
+  do {
+    // Go to the next subface at edge av.
+    spivotself(spinabc);
+    if (spinabc.sh == dummysh) {
+      break; // 'ab' is a hull edge.
+    }
+    if (sorg(spinabc) != pa) {
+      sesymself(spinabc);
+    }
+    // Get the new subface vbc1 above the updated subface avc (= spinabc).
+    senext(spinabc, oldbc);
+    spivot(oldbc, vbc1);
+    if (sorg(vbc1) == newpoint) {
+      sesymself(vbc1);
+    }
+    assert(sorg(vbc1) == sdest(oldbc) && sdest(vbc1) == sorg(oldbc));
+    senextself(vbc1);
+    // Set the connection: vbc->vbc1.
+    sbond1(vbc, vbc1);
+    // For the next connection.
+    vbc = vbc1;
+  } while (spinabc.sh != startabc.sh);
+
+  // Split ab if it is a subsegment.
+  if (ab.sh != dummysh) {
+    // Update subsegment ab to av.
+    av = ab;
+    setsdest(av, newpoint);
+    // Create a new subsegment vb.
+    makeshellface(subsegs, &vb);
+    setsorg(vb, newpoint);
+    setsdest(vb, pb);
+    // vb gets the same mark and segment type as av.
+    setshellmark(vb, shellmark(av));
+    setshelltype(vb, shelltype(av));
+    // Save the old connection at ab (re-use the handles oldbc, bccasout).
+    senext(av, oldbc);
+    spivot(oldbc, bccasout);
+    // Bond av and vb (bonded at their "fake" edges).
+    senext2(vb, bccasin);
+    sbond(bccasin, oldbc);
+    if (bccasout.sh != dummysh) {
+      // There is a subsegment connecting with ab at b. It will connect
+      //   to vb at b after splitting.
+      bccasout.shver = 0;
+      assert(sorg(bccasout) == pb); 
+      senext2self(bccasout);
+      senext(vb, bccasin);
+      sbond(bccasin, bccasout);
+    }
+    // Bond all new subfaces (vbc) to vb. 
+    spinabc = startabc;
+    do {
+      // Adjust spinabc be edge av.
+      if (sorg(spinabc) != pa) {
+        sesymself(spinabc);
+      }
+      // Get new subface vbc above the updated subface avc (= spinabc).
+      senext(spinabc, oldbc);
+      spivot(oldbc, vbc);
+      if (sorg(vbc) == newpoint) {
+        sesymself(vbc);
+      }
+      senextself(vbc);
+      // Bond the new subface and the new subsegment.
+      ssbond(vbc, vb);
+      // Go to the next.
+      spivotself(spinabc);
+      assert(spinabc.sh != dummysh);
+    } while (spinabc.sh != startabc.sh);
+  }
+
+  // Bond the new subfaces to new tetrahedra if they exist.  New tetrahedra
+  //   should have been created before calling this routine.
+  spinabc = startabc;
+  do {
+    // Adjust spinabc be edge av.
+    if (sorg(spinabc) != pa) {
+      sesymself(spinabc);
+    }
+    // Get new subface vbc above the updated subface avc (= spinabc).
+    senext(spinabc, oldbc);
+    spivot(oldbc, vbc);
+    if (sorg(vbc) == newpoint) {
+      sesymself(vbc);
+    }
+    senextself(vbc);
+    // Get the adjacent tetrahedra at 'spinabc'.
+    stpivot(spinabc, abcd);
+    if (abcd.tet != dummytet) {
+      findedge(&abcd, sorg(spinabc), sdest(spinabc));
+      enextfnext(abcd, vbcd);
+      fnextself(vbcd);
+      assert(vbcd.tet != dummytet);
+      tsbond(vbcd, vbc);
+      sym(vbcd, bvce);
+      sesymself(vbc);
+      tsbond(bvce, vbc);
+    } else {
+      // One side is empty, check the other side.
+      sesymself(spinabc);
+      stpivot(spinabc, bace);
+      if (bace.tet != dummytet) {
+        findedge(&bace, sorg(spinabc), sdest(spinabc));
+        enext2fnext(bace, bvce);
+        fnextself(bvce);
+        assert(bvce.tet != dummytet);
+        sesymself(vbc); 
+        tsbond(bvce, vbc);
+      }
+    }
+    // Go to the next.
+    spivotself(spinabc);
+    if (spinabc.sh == dummysh) {
+      break; // 'ab' is a hull edge.
+    }
+  } while (spinabc.sh != startabc.sh);
+  
+  if (b->verbose > 3) {
+    spinabc = startabc;
+    do {
+      // Adjust spinabc be edge av.
+      if (sorg(spinabc) != pa) {
+        sesymself(spinabc);
+      }
+      printf("    Updating abc:\n");
+      printsh(&spinabc);
+      // Get new subface vbc above the updated subface avc (= spinabc).
+      senext(spinabc, oldbc);
+      spivot(oldbc, vbc);
+      if (sorg(vbc) == newpoint) {
+        sesymself(vbc);
+      }
+      senextself(vbc);
+      printf("    Creating vbc:\n");
+      printsh(&vbc);
+      // Go to the next.
+      spivotself(spinabc);
+      if (spinabc.sh == dummysh) {
+        break; // 'ab' is a hull edge.
+      }
+    } while (spinabc.sh != startabc.sh);
+  }
+
+  if (flipqueue != (queue *) NULL) {
+    spinabc = startabc;
+    do {
+      // Adjust spinabc be edge av.
+      if (sorg(spinabc) != pa) {
+        sesymself(spinabc);
+      }
+      senext2(spinabc, oldbc); // Re-use oldbc.
+      enqueueflipedge(oldbc, flipqueue);
+      // Get new subface vbc above the updated subface avc (= spinabc).
+      senext(spinabc, oldbc);
+      spivot(oldbc, vbc);
+      if (sorg(vbc) == newpoint) {
+        sesymself(vbc);
+      }
+      senextself(vbc);
+      senext(vbc, oldbc); // Re-use oldbc.
+      enqueueflipedge(oldbc, flipqueue);
+      // Go to the next.
+      spivotself(spinabc);
+      if (spinabc.sh == dummysh) {
+        break; // 'ab' is a hull edge.
+      }
+    } while (spinabc.sh != startabc.sh);
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// unsplitsubedge()    Reverse the operation of splitting an edge of subface,//
+//                     so as to remove a point from the edge.                //
+//                                                                           //
+// Assume the original edge is ab, the subface containing it is abc. It was  //
+// split by a point v into avc, and vbc.  'splitsh' represents avc, further- //
+// more, if av is a subsegment, av should be the zero version of the split   //
+// subsegment (i.e., av.shver = 0), so we are sure that the destination (v)  //
+// of both avc and av is the deleting point.                                 //
+//                                                                           //
+// To remove point v is to expand avc to abc, delete vbc, do the same for    //
+// other subfaces containing av and vb. If av and vb are subsegments, expand //
+// av to ab, delete vb.  On completion, point v is not deleted.              //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::unsplitsubedge(face* splitsh)
+{
+  face startavc, spinavc, spinbcv;
+  face oldvc, bccasin, bccasout, spinsh;
+  face av, vb, bc;
+  point pa, pv, pb;
+
+  startavc = *splitsh;
+  sspivot(startavc, av);
+  if (av.sh != dummysh) {
+    // Orient the direction of subsegment to conform the subface. 
+    if (sorg(av) != sorg(startavc)) {
+      sesymself(av);
+    }
+    assert(av.shver == 0);
+  }
+  senext(startavc, oldvc);
+  spivot(oldvc, vb);  // vb is subface vbc
+  if (sorg(vb) != sdest(oldvc)) {
+    sesymself(vb);
+  }
+  senextself(vb);
+  pa = sorg(startavc);
+  pv = sdest(startavc);
+  pb = sdest(vb);
+
+  if (b->verbose > 1) {
+    printf("  Removing point %d from subedge (%d, %d).\n",
+           pointmark(pv), pointmark(pa), pointmark(pb));
+  }
+
+  // Spin arround av, unsplit every subface containing av.
+  spinavc = startavc;
+  do {
+    // Adjust spinavc be edge av.
+    if (sorg(spinavc) != pa) {
+      sesymself(spinavc);
+    }
+    // Save old configuration at edge bc, if bc has a subsegment, save the
+    //   face link of it.
+    senext(spinavc, oldvc);
+    spivot(oldvc, spinbcv);
+    if (sorg(spinbcv) != sdest(oldvc)) {
+      sesymself(spinbcv);
+    }
+    senext2self(spinbcv);
+    spivot(spinbcv, bccasout);
+    sspivot(spinbcv, bc);
+    if (bc.sh != dummysh) {
+      if (spinbcv.sh != bccasout.sh) {
+        // 'spinbcv' is not self-bonded.
+        spinsh = bccasout;
+        do {
+          bccasin = spinsh;
+          spivotself(spinsh);
+        } while (spinsh.sh != spinbcv.sh);
+      } else {
+        bccasout.sh = dummysh;
+      }
+    }
+    // Expand avc to abc.
+    setsdest(spinavc, pb);
+    if (bc.sh != dummysh) {
+      if (bccasout.sh != dummysh) {
+        sbond1(bccasin, oldvc);
+        sbond1(oldvc, bccasout);
+      } else {
+        // Bond 'oldbc' to itself.
+        sbond(oldvc, oldvc);
+      }
+      ssbond(oldvc, bc);
+    } else {
+      sbond(oldvc, bccasout);
+    }
+    // Delete bcv.
+    shellfacedealloc(subfaces, spinbcv.sh);
+    // Go to next subface at edge av.
+    spivotself(spinavc);
+    if (spinavc.sh == dummysh) {
+      break; // 'av' is a hull edge.
+    }
+  } while (spinavc.sh != startavc.sh);
+
+  // Is there a subsegment need to be unsplit?
+  if (av.sh != dummysh) {
+    senext(av, oldvc);  // Re-use oldvc.
+    spivot(oldvc, vb);
+    vb.shver = 0;
+    assert(sdest(av) == sorg(vb));
+    senext(vb, spinbcv); // Re-use spinbcv.
+    spivot(spinbcv, bccasout);
+    // Expand av to ab.
+    setsdest(av, pb);
+    sbond(oldvc, bccasout);
+    // Delete vb.
+    shellfacedealloc(subsegs, vb.sh);
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// insertsite()    Insert a point into the mesh.                             //
+//                                                                           //
+// The 'newpoint' is located.  If 'searchtet->tet' is not NULL, the search   //
+// for the containing tetrahedron begins from 'searchtet', otherwise, a full //
+// point location procedure is called.  If 'newpoint' is found inside a      //
+// tetrahedron, the tetrahedron is split into four (by splittetrahedron());  //
+// if 'newpoint' lies on a face, the face is split into three, thereby       //
+// splitting the two adjacent tetrahedra into six (by splittetface()); if    //
+// 'newpoint' lies on an edge, the edge is split into two, thereby, every    //
+// tetrahedron containing this edge is split into two. If 'newpoint' lies on //
+// an existing vertex, no action is taken, and the value DUPLICATEPOINT  is  //
+// returned and 'searchtet' is set to a handle whose origin is the vertex.   //
+//                                                                           //
+// If 'flipqueue' is not NULL, after 'newpoint' is inserted, it returns all  //
+// faces which may become non-Delaunay due to the newly inserted point. Flip //
+// operations can be performed as necessary on them to maintain the Delaunay //
+// property.                                                                 //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+enum tetgenmesh::insertsiteresult tetgenmesh::
+insertsite(point newpoint, triface* searchtet, bool approx, queue* flipqueue)
+{
+  enum locateresult intersect, exactloc;
+  point checkpt;
+  REAL epspp, checklen;
+  int count;
+
+  if (b->verbose > 1) {
+    printf("  Insert point to mesh: (%.12g, %.12g, %.12g) %d.\n",
+           newpoint[0], newpoint[1], newpoint[2], pointmark(newpoint));
+  }
+
+  if (searchtet->tet == (tetrahedron *) NULL) {
+    // Search for a tetrahedron containing 'newpoint'.
+    searchtet->tet = dummytet;
+    exactloc = locate(newpoint, searchtet);
+  } else {
+    // Start searching from the tetrahedron provided by the caller. 
+    exactloc = preciselocate(newpoint, searchtet);
+  }
+  intersect = exactloc;
+  if (approx && (exactloc != ONVERTEX)) {
+    // Adjust the exact location to an approx. location wrt. epsilon.
+    epspp = b->epsilon;
+    count = 0;
+    while (count < 16) {
+      intersect = adjustlocate(newpoint, searchtet, exactloc, epspp);
+      if (intersect == ONVERTEX) {
+        checkpt = org(*searchtet);
+        checklen = distance(checkpt, newpoint);
+        if (checklen / longest > b->epsilon) {
+          epspp *= 1e-2;
+          count++;
+          continue;
+        }
+      }
+      break;
+    }
+  }
+  // Keep current search state for next searching.
+  recenttet = *searchtet; 
+
+  // Insert the point using the right routine
+  switch (intersect) {
+  case ONVERTEX:
+    // There's already a vertex there. Return in 'searchtet' a tetrahedron
+    //   whose origin is the existing vertex.
+    if (b->verbose > 1) {
+      printf("  Not insert for duplicating point.\n");
+    }
+    return DUPLICATEPOINT;
+
+  case OUTSIDE:
+    if (b->verbose > 1) {
+      printf("  Not insert for locating outside the mesh.\n");
+    }
+    return OUTSIDEPOINT;
+
+  case ONEDGE:
+    // 'newpoint' falls on an edge.
+    splittetedge(newpoint, searchtet, flipqueue);
+    return SUCCESSONEDGE;
+
+  case ONFACE:
+    // 'newpoint' falls on a face.
+    splittetface(newpoint, searchtet, flipqueue);
+    return SUCCESSONFACE;
+
+  case INTETRAHEDRON:
+    // 'newpoint' falls inside a tetrahedron.
+    splittetrahedron(newpoint, searchtet, flipqueue);
+    return SUCCESSINTET;
+  }
+
+  // Impossible case.
+  assert(0);
+  return OUTSIDEPOINT;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// undosite()    Undo the most recently point insertion.                     //
+//                                                                           //
+// 'insresult' indicates in where the newpoint has been inserted, i.e., in a //
+// tetrahedron, on a face, or on an edge.  A correspoding routine will be    //
+// called to undo the point insertion.  'splittet' is a handle represent one //
+// of the resulting tetrahedra, but it may be changed after transformation,  //
+// even may be dead.  Four points 'torg', ... 'toppo' are the corners which  //
+// 'splittet' should have. On finish, 'newpoint' is not removed.             //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::
+undosite(enum insertsiteresult insresult, triface* splittet, point torg,
+         point tdest, point tapex, point toppo)
+{
+  // Set the four corners of 'splittet' exactly be 'torg', ... 'toppo'.
+  findface(splittet, torg, tdest, tapex);
+  if (oppo(*splittet) != toppo) {
+    symself(*splittet);
+    assert(oppo(*splittet) == toppo);
+    // The sym() operation may inverse the edge, correct it if so.
+    findedge(splittet, torg, tdest);
+  }
+  
+  // Unsplit the tetrahedron according to 'insresult'.  
+  switch (insresult) {
+  case SUCCESSINTET:
+    // 'splittet' should be the face with 'newpoint' as its opposite.
+    unsplittetrahedron(splittet);
+    break;
+  case SUCCESSONFACE:
+    // 'splittet' should be the one of three splitted face with 'newpoint'
+    //   as its apex.
+    unsplittetface(splittet);
+    break;
+  case SUCCESSONEDGE:
+    // 'splittet' should be the tet with destination is 'newpoint'.
+    unsplittetedge(splittet);
+    break;
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// inserthullsite()    Insert a point which is outside the convex hull.      //
+//                                                                           //
+// The inserting point 'inspoint' lies outside the tetrahedralization.'horiz'//
+// is one of the convex hull faces which are visible from it. (You can image //
+// that is is parallel to the horizon). To insert a point outside the convex //
+// hull we have to enlarge current convex hull of the tetrahedralization for //
+// including this point.  This routine collects convex hull faces which are  //
+// visible from the inserting point, constructs new tetrahedra from these    //
+// faces and the inserting point. On return, 'inspoint' has become a vertex  //
+// of the augmented tetrahedralization.  The convex hull has been updated.   //
+// 'flipcheckqueue' returns the old convex hull faces which may become non-  //
+// Delaunay and need be flipped.                                             //
+//                                                                           //
+// The caller can optionally provide two variables. 'hulllink' is a link for //
+// saving newly created hull faces (containing 'inspoint') which may not     //
+// convex. Non-convex hull faces will be detected and finished by mounting   //
+// new tetrahedra with other hull vertex near them.  'worklist' is an array, //
+// used for face matching.                                                   //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::
+inserthullsite(point inspoint, triface* horiz, queue* flipqueue,
+               link* hulllink, int* worklist)
+{
+  link *myhulllink;
+  triface newtet, hullface;
+  triface oldhull, newhull;
+  point workpt[3];
+  bool finished;
+  int *myworklist;
+  int idx, share;
+  int i, j, k;
+
+  if (b->verbose > 1) {
+    printf("  Collect visible convex hull faces.\n");
+  }
+
+  // Check if the 'hulllink' is provided by the caller.
+  if (hulllink != (link *) NULL) {
+    myhulllink = (link *) NULL;
+  } else {
+    myhulllink = new link(sizeof(triface), NULL, 256);
+    hulllink = myhulllink;
+  }
+
+  // Check if the 'worklist' is provided by the caller.
+  if (worklist != (int *) NULL) {
+    myworklist = (int *) NULL;
+  } else {
+    myworklist = new int[in->numberofpoints];
+    for (i = 0; i < in->numberofpoints; i++) {
+      myworklist[i] = 0;
+    }
+    worklist = myworklist;
+  }
+
+  adjustedgering(*horiz, CW);
+  // Create a new tetrahedron from 'horiz' and 'inspoint'.
+  maketetrahedron(&newtet);
+  setorg (newtet, org(*horiz));
+  setdest(newtet, dest(*horiz));
+  setapex(newtet, apex(*horiz));
+  setoppo(newtet, inspoint);
+  // Make the connection of two tets.
+  bond(newtet, *horiz);
+  // 'horiz' becomes interior face.
+  enqueueflipface(*horiz, flipqueue);
+  // Add the three sides of 'newtet' to 'hulllink'.
+  fnext(newtet, hullface);
+  hulllink->add(&hullface);
+  enextfnext(newtet, hullface);
+  hulllink->add(&hullface);
+  enext2fnext(newtet, hullface);
+  hulllink->add(&hullface);
+  if (b->verbose > 3) {
+    printf("    Creating newtet ");
+    printtet(&newtet);
+  }
+  // Hull face number decreased caused by face bond() operation.
+  hullsize--;
+
+  // Loop untill 'hulllink' is empty.  Find other visible convex hull faces,
+  //   create tetrahedra from them and 'inspoint'. Update 'hulllink'.
+  while (hulllink->len() > 0) {
+    // Remove the top hull face from the link, its apex is 'inspoint'.
+    hullface = * (triface *) hulllink->del(1);
+    // Get the neighbor convex hull face at the edge of 'hullface'.  This is
+    //   done by rotating faces around the edge from the inside until reach
+    //   outer space (The rotation of faces should always terminate).
+    esym(hullface, oldhull);
+    while (fnextself(oldhull)) ;
+    // Is 'inspoint' visible from 'oldhull'?
+    if (orient3d(org(oldhull), dest(oldhull), apex(oldhull), inspoint) < 0.0) {
+      // 'oldhull' is visible from 'inspoint'. Create a new tetrahedron
+      //   from them.
+      maketetrahedron(&newtet);
+      setorg(newtet, org(oldhull));
+      setdest(newtet, dest(oldhull));
+      setapex(newtet, apex(oldhull));
+      setoppo(newtet, inspoint);
+      // Bond 'newtet' to 'oldhull'.
+      bond(newtet, oldhull);
+      // Hull face number decrease caused by bond().
+      hullsize--;
+      // Bond 'newtet' to 'hullface'.
+      fnext(newtet, newhull);
+      bond(newhull, hullface);
+      // 'oldhull' becomes interior face.
+      enqueueflipface(oldhull, flipqueue);
+      // Check other two sides of 'newtet'.  If one exists in 'hulllink'.
+      //   remove the one in 'hulllink' (it is finished), otherwise, it
+      //   becomes a new hull face, add it into 'hulllink'.
+      for (i = 0; i < 2; i++) {
+        // Get 'newhull' and set flags for its vertices.
+        if (i == 0) {
+          enextfnext(newtet, newhull);
+        } else {
+          enext2fnext(newtet, newhull);
+        }
+        workpt[0] = org(newhull);
+        workpt[1] = dest(newhull);
+        workpt[2] = apex(newhull);
+        for (k = 0; k < 3; k++) {
+          idx = pointmark(workpt[k]) - in->firstnumber;
+          worklist[idx] = 1;
+        }
+        // Search 'newhull' in 'hulllink'.
+        finished = false;        
+        for (j = 0; j < hulllink->len() && !finished; j++) {
+          hullface = * (triface *) hulllink->getnitem(j + 1);
+          workpt[0] = org(hullface);
+          workpt[1] = dest(hullface);
+          workpt[2] = apex(hullface);
+          share = 0;
+          for (k = 0; k < 3; k++) {
+            idx = pointmark(workpt[k]) - in->firstnumber;
+            if (worklist[idx] == 1) {
+              share++;
+            }
+          }
+          if (share == 3) {
+            // Two faces are identical. Bond them togther.
+            bond(newhull, hullface);
+            // Remove 'hullface' from the link.
+            hulllink->del(j + 1);
+            finished = true;
+          }
+        }
+        if (!finished) {
+          // 'newhull' becomes a hull face, add it into 'hulllink'.
+          hulllink->add(&newhull); 
+        }
+        // Clear used flags.
+        workpt[0] = org(newhull);
+        workpt[1] = dest(newhull);
+        workpt[2] = apex(newhull);
+        for (k = 0; k < 3; k++) {
+          idx = pointmark(workpt[k]) - in->firstnumber;
+          worklist[idx] = 0;
+        }
+      }
+    } else {
+      // 'hullface' becomes a convex hull face. 
+      hullsize++;
+      // Let 'dummytet[0]' points to it for next point location.
+      dummytet[0] = encode(hullface);
+    }
+  }
+
+  if (myhulllink != (link *) NULL) {
+    delete myhulllink;
+  }
+  if (myworklist != (int *) NULL) {
+    delete [] myworklist;
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// collectcavtets()    Collect all tetrahedra whose circumsphere conatining  //
+//                     the given point.                                      //
+//                                                                           //
+// This routine first locates the newpoint. Note the mesh may not be convex, //
+// the locate() function may not find it. However, if we start from a very   //
+// close neighborhood, the function preciselocate() can be used.  Here we    //
+// assume "recenttet" suggests such a starting point. 'cavtetlist' is a list //
+// returns the tetrahedra. It is empty on input.                             //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::collectcavtets(point newpoint, list* cavtetlist)
+{
+  triface starttet, neightet;
+  enum locateresult loc;
+  REAL sign;
+  int i, j;
+
+  // First locate the newpoint. 'recenttet' suggests the starting point.
+  starttet = recenttet;
+  loc = preciselocate(newpoint, &starttet);
+  if (loc == OUTSIDE) {
+    // Principlly, the newpoint should lie inside or on the hull. But it
+    //   may happen practically when the newpoint is just slightly lies
+    //   outside the hull due to the rounding error.
+    loc = ONFACE; // This line is no meaning.
+  }
+  
+  // Now starttet contains newpoint.
+  infect(starttet);
+  cavtetlist->append(&starttet);
+  // Check the adjacent tet.
+  sym(starttet, neightet);
+  if (neightet.tet != dummytet) {
+    // For positive orientation that insphere() test requires.
+    adjustedgering(neightet, CW);
+    sign = insphere(org(neightet), dest(neightet), apex(neightet),
+                    oppo(neightet), newpoint);
+    if (sign >= 0.0) {
+      // Add neightet into list.
+      infect(neightet);
+      cavtetlist->append(&neightet);
+    }
+  }
+
+  // Find the other tetrahedra by looping in list.
+  for (i = 0; i < cavtetlist->len(); i++) {
+    starttet = * (triface *)(* cavtetlist)[i];
+    // Check the other three neighbors of starttet.
+    adjustedgering(starttet, CCW);
+    for (j = 0; j < 3; j++) {
+      fnext(starttet, neightet);
+      symself(neightet);
+      if ((neightet.tet != dummytet) && !infected(neightet)) {
+        // For positive orientation that insphere() test requires.
+        adjustedgering(neightet, CW);
+        sign = insphere(org(neightet), dest(neightet), apex(neightet),
+                        oppo(neightet), newpoint);
+        if (sign >= 0.0) {
+          // Add neightet into list.
+          infect(neightet);
+          cavtetlist->append(&neightet);
+        }
+      }
+      enextself(starttet);
+    }
+  }
+
+  // Having find all tetrahedra, uninfect them before return.
+  for (i = 0; i < cavtetlist->len(); i++) {
+    starttet = * (triface *)(* cavtetlist)[i];
+    assert(infected(starttet));
+    uninfect(starttet);
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// removetetbypeeloff()    Remove a boundary tetrahedron by peeling off it   //
+//                         from the mesh.                                    //
+//                                                                           //
+// 'badtet' (abcd) is a boundary tetrahedron and going to be peeled off. abc //
+// and bad are the outer boundary faces. To remove 'abcd' from the mesh is   //
+// simply detach its two inner faces (dca and cdb) from the adjoining tets.  //
+// A 2-to-2 flip is applied to transform subfaces abc and bad to dca and cdb.//
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::removetetbypeeloff(triface *badtet, queue* flipqueue)
+{
+  triface abcd, badc;
+  triface dcacasing, cdbcasing;
+  face abc, bad;
+  
+  if (b->verbose > 1) {
+    printf("    by peeling off it from boundary.\n");
+  }
+
+  abcd = *badtet;
+  adjustedgering(abcd, CCW);
+  // Get subfaces abc, bad.
+  fnext(abcd, badc);
+  esymself(badc);
+  tspivot(abcd, abc);
+  tspivot(badc, bad);
+  assert(abc.sh != dummysh && bad.sh != dummysh);
+  findedge(&abc, org(abcd), dest(abcd));
+  findedge(&bad, org(badc), dest(badc));
+  // Get the casing tets at the two inner sides of 'badtet'.
+  enextfnext(abcd, cdbcasing);
+  enext2fnext(abcd, dcacasing);
+  symself(cdbcasing);
+  symself(dcacasing);
+  assert(cdbcasing.tet != dummytet && dcacasing.tet != dummytet);
+
+  // Do a 2-to-2 flip on abc and bad, transform abc->dca, bad->cdb.
+  flip22sub(&abc, NULL);
+  // Detach abcd from its inner sides.
+  dissolve(cdbcasing);
+  dissolve(dcacasing);
+  // Make its inner sides be boundary.
+  tsbond(cdbcasing, bad);
+  tsbond(dcacasing, abc);
+  // Delete abcd.
+  tetrahedrondealloc(abcd.tet);
+
+  if (flipqueue != (queue *) NULL) {
+    // Edge cd maybe non-Delaunay.
+    adjustedgering(cdbcasing, CCW);
+    fnextself(cdbcasing);
+    enqueueflipface(cdbcasing, flipqueue);
+    adjustedgering(dcacasing, CCW);
+    fnextself(dcacasing);
+    enqueueflipface(dcacasing, flipqueue);
+    // Do flipping.
+    flip(flipqueue, NULL);
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// removetetbyflip32()    Remove a tetrahedron by doing a 3-to-2 flip.       //
+//                                                                           //
+// 'badtet' (abcd) is the bad tetrahedron which is going to be removed by a  //
+// 3-to-2 flip.  abc represents one of the internal faces, bad is another.   //
+// If abc and bad are subfaces, a 2-to-2 flip is performed to transform abc, //
+// bad into dca, cdb, before the 3-to-2 flip is applying.                    //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::removetetbyflip32(triface *badtet, queue* flipqueue)
+{
+  triface abcd, badc;
+  triface cdab, dcba;
+  triface baccasing, abdcasing;
+  triface dcacasing, cdbcasing;
+  face abc, bad;
+  
+  if (b->verbose > 1) {
+    printf("    by doing a 3-to-2 flip.\n");
+  }
+
+  abcd = *badtet;
+  adjustedgering(abcd, CCW);
+  fnext(abcd, badc);
+  esymself(badc);
+  sym(abcd, baccasing);
+  sym(badc, abdcasing);
+  assert(baccasing.tet != dummytet && abdcasing.tet != dummytet);
+  assert(oppo(baccasing) == oppo(abdcasing));
+  
+  // Get subfaces abc, bad.
+  tspivot(abcd, abc);
+  tspivot(badc, bad);
+  if (abc.sh != dummysh) {
+    // Because ab should not be a subsegment.
+    assert(bad.sh != dummysh);
+    findedge(&abc, org(abcd), dest(abcd));
+    findedge(&bad, org(badc), dest(badc));
+    // Detach abc, bad from tetrahedra at both sides.
+    stdissolve(abc);
+    stdissolve(bad);
+    sesymself(abc);
+    sesymself(bad);
+    stdissolve(abc);
+    stdissolve(bad);
+    sesymself(abc);
+    sesymself(bad);
+    // Detach tetrahedra witch hold abc and bad.
+    tsdissolve(abcd);
+    tsdissolve(badc);
+    tsdissolve(baccasing);
+    tsdissolve(abdcasing);
+    // Perform a 2-to-2 flip on abc, bad, transform abc->dca, bad->cdb.
+    flip22sub(&abc, NULL);
+    // Insert the flipped subfaces abc and bad into tetrahedra.
+    enextfnext(abcd, dcba); // dcba = bcda
+    esymself(dcba); // dcba = cbda
+    enext2fnext(abcd, cdab); // cdab = cadb
+    esymself(cdab); // cdab = acdb
+    findedge(&abc, org(cdab), dest(cdab));
+    tsbond(cdab, abc);
+    findedge(&bad, org(dcba), dest(dcba));
+    tsbond(dcba, bad);
+    // Bond the other sides of cdab, dcba, they may outer space.
+    sym(cdab, dcacasing);
+    sym(dcba, cdbcasing);
+    sesymself(abc);
+    sesymself(bad);
+    tsbond(dcacasing, abc);
+    tsbond(cdbcasing, bad);          
+  }
+  // Do a 3-to-2 flip on face abc to remove tetrahedron abcd.
+  flip32(&abcd, flipqueue);
+  // Do flipping if necessary.
+  if (flipqueue != (queue *) NULL) {
+    flip(flipqueue, NULL);
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// removetetbycflips()    Remove a tetrahedron by a combination of flips.    //
+//                                                                           //
+// 'badtet' (abcd) is the tetrahedron which is going to be removed. It can't //
+// be removed simply by a 3-to-2 flip.                                       //
+//                                                                           //
+// A flipstack is used for remembering flips have done, in case falure, we   //
+// can restore the original state.                                           //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::removetetbycflips(triface *badtet, queue* flipqueue)
+{
+  triface abcd;
+  triface bacd, baec;
+  triface abdc, abfd;
+  flipstacker *lastflip, *newflip;
+  enum fliptype fc;
+  int flipcount;
+
+  abcd = *badtet;
+  adjustedgering(abcd, CCW);
+  esym(abcd, bacd);
+  if (issymexist(&bacd)) {
+    fnext(bacd, baec);
+    assert(baec.tet != dummytet);
+  } else {
+    // This side is empty. But the tet can not be peeled off.
+    return false;
+  }
+  fnext(abcd, abdc);
+  if (issymexist(&abdc)) {
+    fnext(abdc, abfd);
+    assert(abfd.tet != dummytet);
+  } else {
+    // This side is empty. But the tet can not be peeled off.
+    return false;
+  }
+  assert(apex(baec) != apex(abfd));
+
+  if (b->verbose > 1) {
+    printf("    by doing a combination of flips.\n");
+  }
+
+  // Initialize the stack of the flip sequence.
+  flipstackers->restart();
+  lastflip = (flipstacker *) NULL;
+
+  // Flip faces above abc.
+  flipcount = 0;
+  do {
+    assert(baec.tet != dummytet);
+    fc = categorizeface(baec);
+    if (fc == T23) {
+      flip23(&baec, NULL);
+    } else if (fc == T22 || fc == T44) {
+      flip22(&baec, NULL);
+    }
+    if (fc == T23 || fc == T22 || fc == T44) {
+      // Push the flipped face into stack.
+      newflip = (flipstacker *) flipstackers->alloc();
+      newflip->flippedface = baec;
+      newflip->fc = fc;
+      newflip->forg = org(baec);
+      newflip->fdest = dest(baec);
+      newflip->fapex = apex(baec);
+      newflip->prevflip = lastflip;
+      lastflip = newflip;
+      // Check the flipped side.
+      fnext(bacd, baec);
+      if (apex(baec) == apex(abfd)) {
+        // We have made 'abcd' flipable.
+        removetetbyflip32(&abcd, flipqueue);
+        return true;
+      }
+      flipcount++;
+    } else {
+      // Face is unflipable, break the loop.
+      break;
+    }
+  } while (flipcount < 16);
+
+  // Flip faces above bad.
+  fnext(bacd, baec);
+  assert(apex(abfd) != apex(baec));
+  flipcount = 0;
+  do {
+    assert(abfd.tet != dummytet);
+    fc = categorizeface(abfd);
+    if (fc == T23) {
+      flip23(&abfd, NULL);
+    } else if (fc == T22 || fc == T44) {
+      flip22(&abfd, NULL);
+    }
+    if (fc == T23 || fc == T22 || fc == T44) {
+      // Push the flipped face into stack.
+      newflip = (flipstacker *) flipstackers->alloc();
+      newflip->flippedface = abfd;
+      newflip->fc = fc;
+      newflip->forg = org(abfd);
+      newflip->fdest = dest(abfd);
+      newflip->fapex = apex(abfd);
+      newflip->prevflip = lastflip;
+      lastflip = newflip;
+      // Check the flipped side.
+      fnext(abdc, abfd);
+      if (apex(abfd) == apex(baec)) {
+        // We have made 'abcd' flipable.
+        removetetbyflip32(&abcd, flipqueue);
+        return true;
+      }
+      flipcount++;
+    } else {
+      // Face is unflipable, break the loop.
+      break;
+    }
+  } while (flipcount < 16);
+
+  // Unable to use a combination of flips.
+  if (b->verbose > 1) {
+    printf("    Not success.\n");
+  }
+  // Restore the flips we've done.
+  undoflip(lastflip);
+  return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// removebadtet()    Remove a bad tetrahedron from the mesh.                 //
+//                                                                           //
+// 'bt' indicates the type of the 'badtet', assume it is abcd. If it is a    //
+// SLIVER, ab and cd are the two diagonal edges; if it is a CAP, abc is the  //
+// bottom face (the projection of d is inside abc); if it is ILLEGAL, abc,   //
+// bad are the two boundary subfaces (which are on the same facet).          //
+//                                                                           //
+// This routine first classifies the type of operation can be used to remove //
+// 'badtet', e.g. simpley do a 3-to-2 flip, or combine several flips. Then   //
+// do the corresponding flip operation.                                      //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::
+removebadtet(enum badtettype bt, triface *badtet, queue* flipqueue)
+{
+  triface abcd, badc, cdab, dcba;
+  triface bcdcasing, cadcasing;
+  triface baccasing, abdcasing;
+  face ab, cd;
+  point pa, pb, pc, pd;
+
+  abcd = *badtet;
+  adjustedgering(abcd, CCW);
+  pa = org(abcd);
+  pb = dest(abcd);
+  pc = apex(abcd);
+  pd = oppo(abcd);
+
+  if (b->verbose > 1) {
+    printf("    Remove tetrahedron (%d, %d, %d, %d).\n", pointmark(pa),
+           pointmark(pb), pointmark(pc), pointmark(pd));
+  }
+  
+  // Get the casing tets at the four sides of 'badtet'.
+  fnext(abcd, badc);
+  esymself(badc);
+  sym(abcd, baccasing);
+  sym(badc, abdcasing);
+  tsspivot(&abcd, &ab);
+  enextfnext(abcd, dcba); // dcba = bcda
+  esymself(dcba); // dcba = cbda
+  enext2self(dcba);
+  enext2fnext(abcd, cdab); // cdab = cadb
+  esymself(cdab); // cdab = acdb
+  enextself(cdab);
+  sym(dcba, bcdcasing);
+  sym(cdab, cadcasing);
+  tsspivot(&dcba, &cd);
+
+  if (ab.sh != dummysh && cd.sh != dummysh) {
+    printf("Warning:  Two subsegments (%d, %d), (%d, %d) cross in one tet.\n",
+           pointmark(pa), pointmark(pb), pointmark(pc), pointmark(pd));
+    return false;
+  }
+
+  if (bt == ILLEGAL || bt == SLIVER) {
+    if (cd.sh == dummysh) {
+      // Test if edge 'cd' is removeable.
+      if (bcdcasing.tet == dummytet && cadcasing.tet == dummytet) {
+        removetetbypeeloff(&cdab, flipqueue);
+        return true;
+      }
+      if (oppo(bcdcasing) == oppo(cadcasing)) {
+        removetetbyflip32(&cdab, flipqueue);
+        return true;
+      }
+    } 
+    if (ab.sh == dummysh) {
+      // Test if edge 'ab' is removeable.
+      if (baccasing.tet == dummytet && abdcasing.tet == dummytet) {
+        removetetbypeeloff(&abcd, flipqueue);
+        return true;
+      }
+      if (oppo(baccasing) == oppo(abdcasing)) {
+        removetetbyflip32(&abcd, flipqueue);
+        return true;
+      }
+    }
+    // 'badtet' can not be easily removed, try to remove it by a combination
+    //   of flips.
+    if (cd.sh == dummysh) {
+      if (removetetbycflips(&cdab, flipqueue)) {
+        return true;
+      }
+    }
+    if (ab.sh == dummysh) {
+      if (removetetbycflips(&abcd, flipqueue)) {
+        return true;
+      }
+    }
+    if (b->verbose) {
+      printf("Warning:  Unable to remove bad tet (%d, %d, %d, %d).\n",
+             pointmark(pa), pointmark(pb), pointmark(pc), pointmark(pd));
+    }
+    return false;
+  }
+
+  return false;
+}
+
+//
+// End of mesh transformation routines
+//
+
+//
+// Begin of incremental flip Delaunay triangulation routines
+//
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// incrflipinit()    Create an initial tetrahedralization.                   //
+//                                                                           //
+// The initial tetrahedralization only contains one tetrahedron formed from  //
+// four affinely linear independent vertices from the input point set.       //
+//                                                                           //
+// 'insertqueue' returns the rest of vertices of the input point set.  These //
+// vertices will be inserted one by one in the later step.                   //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::incrflipinit(queue* insertqueue)
+{
+  triface newtet;
+  point *plist, pointloop;
+  point pa, pb, pc, pd;
+  REAL det;
+  int count;
+  int i, j;
+
+  if (b->verbose > 1) {
+    printf("  Constructing an initial tetrahedron.\n");
+  }
+
+  // Create a point list and initialize it.
+  plist = new point[in->numberofpoints];
+  i = 0;
+  points->traversalinit();
+  pointloop = pointtraverse();
+  while (pointloop != (point) NULL) {
+    plist[i++] = pointloop;
+    pointloop = pointtraverse();
+  }
+  assert(i == in->numberofpoints);
+
+  if (b->dopermute) {
+    // Do permutation.  Initialize the random seed.
+    randomseed = b->srandseed;
+    for (i = 0; i < in->numberofpoints; i++) {
+      // Get a index j (from 0 to in->numberofpoints - i - 1).
+      j = (int) randomnation(in->numberofpoints - i);
+      // Exchange the i-th point and (j + i)-th point.
+      pointloop = plist[j + i];
+      plist[j + i] = plist[i];
+      plist[i] = pointloop;
+    }
+  }
+
+  // Set the plist into insertqueue.
+  if (!insertqueue->empty()) {
+    insertqueue->clear(); 
+  }
+  for (i = 0; i < in->numberofpoints; i++) {
+    pointloop = plist[i];
+    insertqueue->push(&pointloop);
+  }
+  delete [] plist;
+
+  // Get the first two point 'pa'.
+  pa = * (point *) insertqueue->pop();
+
+  // Get the second point 'pb', which is not identical with 'pa'.
+  count = 0;
+  pb = * (point *) insertqueue->pop();
+  while ((pb != (point) NULL) && (count < in->numberofpoints)) {
+    if ((pb[0] == pa[0]) && (pb[1] == pa[1]) && (pb[2] == pa[2])) {
+      // 'pb' is identical to 'pa', skip it.
+      insertqueue->push(&pb);
+    } else {
+      break;
+    }
+    pb = * (point *) insertqueue->pop();
+    count++;
+  }
+  if (pb == (point) NULL) {
+    printf("\nAll points are identical, no triangulation be constructed.\n");
+    exit(1);
+  }
+
+  // Get the third point 'pc', which is not collinear with 'pa' and 'pb'.
+  count = 0;
+  pc = * (point *) insertqueue->pop();
+  while ((pc != (point) NULL) && (count < in->numberofpoints)) {
+    if (iscollinear(pa, pb, pc, (b->epsilon * 1e-2))) {
+      // They are collinear or identical, put it back to queue.
+      insertqueue->push(&pc);
+    } else {
+      break;
+    }
+    pc = * (point *) insertqueue->pop();
+    count++;
+  }
+  if (pc == (point) NULL) {
+    printf("\nAll points are collinear, no triangulation be constructed.\n");
+    exit(1);
+  }
+
+  // Get the fourth point which is not coplanar with pa, pb, and pc.
+  count = 0;
+  pd = * (point *) insertqueue->pop();
+  while ((pd != (point) NULL) && (count < in->numberofpoints)) {
+    det = orient3d(pa, pb, pc, pd);
+    if (det == 0.0) {
+      // They are coplanar or identical, put it back to queue.
+      insertqueue->push(&pd);
+    } else {
+      break;
+    }
+    pd = * (point *) insertqueue->pop();
+    count++;
+  }
+  if (pd == (point) NULL) {
+    printf("\nAll points are coplanar, no triangulation be constructed.\n");
+    exit(1);
+  }
+  if (det > 0.0) {
+    pointloop = pa; pa = pb; pb = pointloop;
+  }
+
+  // Create the tetrahedron with corners pa, pb, pc and pd.
+  maketetrahedron(&newtet);
+  setorg(newtet, pa);
+  setdest(newtet, pb);
+  setapex(newtet, pc);
+  setoppo(newtet, pd);
+  // Set the vertices be FREEVOLVERTEX to indicate they belong to the mesh.
+  setpointtype(pa, FREEVOLVERTEX);
+  setpointtype(pb, FREEVOLVERTEX);
+  setpointtype(pc, FREEVOLVERTEX);
+  setpointtype(pd, FREEVOLVERTEX);
+  // Bond to 'dummytet' for point location.
+  dummytet[0] = encode(newtet);
+  if (b->verbose > 3) {
+    printf("    Creating tetra ");
+    printtet(&newtet);
+  }
+  // At init, all faces of this tet are hull faces.
+  hullsize = 4;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// incrflipdelaunay()   Construct a delaunay tetrahedrization from a set of  //
+//                      3D points using the incremental flip algorithm.      //
+//                                                                           //
+// The incremental flip algorithm is described in the paper of Edelsbrunner  //
+// and Shah, "Incremental Topological Flipping Works for Regular Triangulat- //
+// ions",  Algorithmica 15: 223-241, 1996.  It can be described as follows:  //
+//                                                                           //
+//   S be a set of points in 3D, Let 4 <= i <= n and assume that the         //
+//   Delaunay triangulation of the first i-1 points in S is already          //
+//   constructed; call it D(i-1). Add the i-th point p_i (belong to S) to    //
+//   the triangulation,and restore Delaunayhood by flipping; this result     //
+//   in D(i). Repeat this procedure until i = n.                             //
+//                                                                           //
+// This strategy always leads to the Ddelaunay triangulation of a point set. //
+// The return value is the number of convex hull faces of this point set.    //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+long tetgenmesh::incrflipdelaunay()
+{
+  triface starttet;
+  point pointloop;
+  queue *flipqueue;
+  queue *insertqueue;
+  link *hulllink;
+  enum insertsiteresult insres;
+  int *worklist, i;
+
+  if (!b->quiet) {
+    if (!b->noflip) {
+      printf("Constructing Delaunay tetrahedrization.\n");
+    } else {
+      printf("Constructing tetrahedrization.\n");
+    }
+  }
+
+  // Initialize 'flipqueue'.
+  flipqueue = new queue(sizeof(badface));
+  // Create a queue for all inserting points.
+  insertqueue = new queue(sizeof(point*), in->numberofpoints);
+  // Create a 'hulllink' used in inserthullsite().
+  hulllink = new link(sizeof(triface), NULL, 256);
+  // Create and initialize 'worklist' used in inserthullsite().
+  worklist = new int[in->numberofpoints];
+  for (i = 0; i < in->numberofpoints; i++) worklist[i] = 0;
+  // Initialize global counters.
+  flip23s = flip32s = flip22s = flip44s = 0;
+
+  // Algorithm starts from here.
+  
+  // Construct an initial tetrahedralization and fill 'insertqueue'.
+  incrflipinit(insertqueue);
+
+  // Loop untill all points are inserted.
+  while (!insertqueue->empty()) {
+    pointloop = * (point *) insertqueue->pop();
+    // It will become a mesh point unless it duplicates an existing point.
+    setpointtype(pointloop, FREEVOLVERTEX);
+    // Try to insert the point first.
+    starttet.tet = (tetrahedron *) NULL;
+    insres = insertsite(pointloop, &starttet, false, flipqueue);
+    if (insres == OUTSIDEPOINT) {
+      // Point locates outside the convex hull.
+      inserthullsite(pointloop, &starttet, flipqueue, hulllink, worklist);
+    } else if (insres == DUPLICATEPOINT) {
+      if (b->object != tetgenbehavior::STL) {
+        if (!b->quiet) {
+          printf("Warning:  Point %d is identical with point %d.\n",
+                 pointmark(pointloop), pointmark(org(starttet)));
+        }
+        // Count the number of duplicated points.
+        dupverts++;
+      }
+      // Remember it is a duplicated point.
+      setpointtype(pointloop, DUPLICATEDVERTEX);
+      if (b->plc || b->refine) {
+        // Set a pointer to the point it duplicates.
+        setpoint2pt(pointloop, org(starttet));
+      }
+    }
+    if (!b->noflip) {
+      // Call flip algorithm to recover Delaunayness.
+      flip(flipqueue, NULL); 
+    } else {
+      // Not perform flip.
+      flipqueue->clear();
+    }
+  }
+
+  delete flipqueue;
+  delete insertqueue;
+  delete hulllink;
+  delete [] worklist;
+
+  if (!b->noflip && b->verbose) {
+    printf("  Total flips: %ld, where T23 %ld, T32 %ld, T22 %ld, T44 %ld\n",
+           flip23s + flip32s + flip22s + flip44s,
+           flip23s, flip32s, flip22s, flip44s);
+  }
+
+  return hullsize;
+}
+
+//
+// End of incremental flip Delaunay triangulation routines
+//
+
+//
+// Begin of surface triangulation routines
+//
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// The lift points                                                           //
+//                                                                           //
+// A 'lifting point' of a facet is a point which lies exactly non-coplanar   //
+// with the plane containing that facet.  With such an additional point, the //
+// three-dimensional geometric predicates (orient3d, insphere) can be used   //
+// to substitute the lower dimensional predicates (orient2d, incircle). The  //
+// advantage is there is no need to project 3D points back into 2D, so the   //
+// rounding error can be avoid.                                              //
+//                                                                           //
+// These points are calculated during the initialization of triangulating    //
+// the facets. It is important to orient subfaces of the same facet to have  //
+// the same orientation with respect to its lift point. This way guarantees  //
+// the test results are consistent. We take the convention that the lift     //
+// point of a facet always lies above the CCW edge rings of subfaces of the  //
+// same facet. By this convention, given three points a, b, and c in a facet,//
+// we say c has the counterclockwise order with ab is corresponding to say   //
+// that c is below the plane abp, where p is the lift point.                 //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// locatesub()    Find a point in the surface mesh.                          //
+//                                                                           //
+// Searching begins from the input 'searchsh', it should be a handle on the  //
+// convex hull of the facet triangulation.                                   //
+//                                                                           //
+// On completion, 'searchsh' is a subface that contains 'searchpoint'.       //
+//   - Returns ONVERTEX if the point lies on an existing vertex. 'searchsh'  //
+//     is a handle whose origin is the existing vertex.                      //
+//   - Returns ONEDGE if the point lies on a mesh edge.  'searchsh' is a     //
+//     handle whose primary edge is the edge on which the point lies.        //
+//   - Returns ONFACE if the point lies strictly within a subface.           //
+//     'searchsh' is a handle on which the point lies.                       //
+//   - Returns OUTSIDE if the point lies outside the triangulation.          //
+//                                                                           //
+// WARNING: This routine is designed for convex triangulations, and will not //
+// not generally work after the holes and concavities have been carved.      //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+enum tetgenmesh::locateresult tetgenmesh::
+locatesub(point searchpt, face* searchsh, point abovept)
+{
+  face backtracksh, checkedge;
+  point forg, fdest, fapex, liftpoint;
+  REAL orgori, destori;
+  int moveleft, i;
+
+  if (searchsh->sh == dummysh) {
+    searchsh->shver = 0;
+    spivotself(*searchsh);
+    assert(searchsh->sh != dummysh);
+  }
+
+  // Set the liftpoint. (Note, the liftpoint is always above the face.)
+  if (abovept == (point) NULL) {
+    liftpoint = getliftpoint(shellmark(*searchsh));
+    adjustedgering(*searchsh, CCW);
+  } else {
+    liftpoint = abovept;
+    forg = sorg(*searchsh);
+    fdest = sdest(*searchsh);
+    fapex = sapex(*searchsh);
+    orgori = orient3d(forg, fdest, fapex, liftpoint);
+    assert(orgori != 0.0);
+    if (orgori > 0.0) {
+      sesymself(*searchsh);
+    }
+  }
+
+  // Orient 'searchsh' so that 'searchpt' is below it (i.e., searchpt has
+  //   CCW orientation with respect to searchsh in plane).  Such edge
+  //   should always exist. Save it as (forg, fdest).
+  for (i = 0; i < 3; i++) {
+    forg = sorg(*searchsh);
+    fdest = sdest(*searchsh);
+    if (orient3d(forg, fdest, liftpoint, searchpt) > 0.0) break;
+    senextself(*searchsh);
+  }
+  assert(i < 3);
+  
+  while (1) {
+    fapex = sapex(*searchsh);
+    // Check whether the apex is the point we seek.
+    if (fapex[0] == searchpt[0] && fapex[1] == searchpt[1] &&
+        fapex[2] == searchpt[2]) {
+      senext2self(*searchsh);
+      return ONVERTEX;
+    }
+    // Does the point lie on the other side of the line defined by the
+    //   triangle edge opposite the triangle's destination?
+    destori = orient3d(forg, fapex, liftpoint, searchpt);
+    // Does the point lie on the other side of the line defined by the
+    //   triangle edge opposite the triangle's origin? 
+    orgori = orient3d(fapex, fdest, liftpoint, searchpt);
+    if (destori > 0.0) {
+      moveleft = 1;
+    } else {
+      if (orgori > 0.0) {
+        moveleft = 0;
+      } else {
+        // The point must be on the boundary of or inside this triangle.
+        if (destori == 0.0) {
+          senext2self(*searchsh);
+          return ONEDGE;
+        } 
+        if (orgori == 0.0) {
+          senextself(*searchsh);
+          return ONEDGE;
+        }
+        return ONFACE;
+      }
+    }
+    // Move to another triangle.  Leave a trace `backtracksh' in case
+    //   walking off a boundary of the triangulation.
+    if (moveleft) {
+      senext2(*searchsh, backtracksh);
+      fdest = fapex;
+    } else {
+      senext(*searchsh, backtracksh);
+      forg = fapex;
+    }
+    spivot(backtracksh, *searchsh);
+    // Check for walking right out of the triangulation.
+    if (searchsh->sh == dummysh) {
+      // Go back to the last triangle.
+      *searchsh = backtracksh;
+      return OUTSIDE;
+    }
+    // To keep the same orientation wrt. liftpoint.
+    // adjustedgering(*searchsh, CCW);
+    if (sorg(*searchsh) != forg) {
+      sesymself(*searchsh);
+    }
+    assert((sorg(*searchsh) == forg) && (sdest(*searchsh) == fdest));
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// flipsub()    Flip all non-Delaunay edges in a given queue of subfaces.    //
+//                                                                           //
+// Assumpation:  Current triangulation is non-Delaunay after inserting a     //
+// point or performing a flip operation, all possibly non-Delaunay edges are //
+// in 'facequeue'. The return value is the total number of flips done during //
+// this invocation.                                                          //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+long tetgenmesh::flipsub(queue* flipqueue)
+{
+  badface *qedge;
+  face flipedge, symedge, bdedge;
+  point pa, pb, pc, pd, liftpoint;
+  REAL sign;
+  int edgeflips;
+
+  if (b->verbose > 1) {
+    printf("  Start do edge queue: %ld edges.\n", flipqueue->len());
+  }
+
+  edgeflips = 0;
+
+  while ((qedge = (badface *) flipqueue->pop()) != NULL) {
+    flipedge = qedge->ss;
+    if (flipedge.sh == dummysh) continue;
+    if ((sorg(flipedge) != qedge->forg) || 
+        (sdest(flipedge) != qedge->fdest)) continue; 
+    sspivot(flipedge, bdedge);
+    if (bdedge.sh != dummysh) continue;  // Can't flip a subsegment.
+    spivot(flipedge, symedge);
+    if (symedge.sh == dummysh) continue; // Can't flip a hull edge.
+    pa = sorg(flipedge);
+    pb = sdest(flipedge);
+    pc = sapex(flipedge);
+    pd = sapex(symedge);
+    liftpoint = getliftpoint(shellmark(flipedge)); 
+    // Check whether pd lies inside the circumcircle of pa, pb, pc or not.
+    sign = insphere(pa, pb, pc, liftpoint, pd) 
+         * orient3d(pa, pb, pc, liftpoint);
+    if (sign > 0.0) {
+      // Flip the non-Delaunay edge.
+      flip22sub(&flipedge, flipqueue);
+      edgeflips++;
+    }
+  }
+
+  if (b->verbose > 1) {
+    printf("  Total %d flips.\n", edgeflips);
+  }
+
+  return edgeflips;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// incrflipinitsub()    Create a initial triangulation.                      //
+//                                                                           //
+// The initial triangulation only consists of one triangle formed by three   //
+// non-collinear points. 'facetidx' is the index of the facet in 'facetlist' //
+// (starts from 1) of the tetgenio structure;  'ptlist' is a list of indices //
+// of the facet vertices; 'idx2verlist' is a map from indices to vertices.   //
+//                                                                           //
+// The 'lift point' of this facet is calculated.  If not all vertices of the //
+// facet are collinear,  such point is found by lifting the centroid of the  //
+// set of vertices for a certain distance along the normal of this facet.    //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::
+incrflipinitsub(int facetidx, list* ptlist, point* idx2verlist)
+{
+  face newsh;
+  point pt1, pt2, pt3;
+  point liftpoint, ptloop;
+  REAL cent[3], norm[3];
+  REAL v1[3], v2[3];
+  REAL smallcos, cosa;
+  REAL liftdist, len, vol;
+  int smallidx;
+  int idx, i;
+  
+  if (ptlist->len() > 3) {
+    // Find a (non-degenerate) vector from the vertex set.
+    idx =  * (int *) (* ptlist)[0];
+    pt1 = idx2verlist[idx - in->firstnumber];
+    len = 0.0;
+    // Loop the set of vertices until a not too small edge be found.
+    for (i = 1; i < ptlist->len(); i++) {
+      idx =  * (int *) (* ptlist)[i];
+      pt2 = idx2verlist[idx - in->firstnumber];
+      v1[0] = pt2[0] - pt1[0];
+      v1[1] = pt2[1] - pt1[1];
+      v1[2] = pt2[2] - pt1[2];
+      len = sqrt(dot(v1, v1));
+      if ((len / longest) > (b->epsilon * 1e+2)) break;
+    } 
+    // Remember this size as lift distance.
+    liftdist = len;
+    // 'v1' is a reasonable vector, normalize it.
+    for (i = 0; i < 3; i++) v1[i] /= len;
+    // Continue to find another (non-degenerate) vector, which forms an
+    //   angle with v1 most close to 90 degree.
+    smallcos = 1.0; // The cosine value of 0 degree.
+    for (i = 1; i < ptlist->len(); i++) {
+      idx =  * (int *) (* ptlist)[i];
+      pt3 = idx2verlist[idx - in->firstnumber];
+      if (pt3 == pt2) continue; // Skip the same point.
+      v2[0] = pt3[0] - pt1[0];
+      v2[1] = pt3[1] - pt1[1];
+      v2[2] = pt3[2] - pt1[2];
+      len = sqrt(dot(v2, v2));
+      if (len > 0.0) { // v2 is not too small.
+        cosa = fabs(dot(v1, v2)) / len;
+        if (cosa < smallcos) {
+          smallidx = idx;
+          smallcos = cosa;
+        }
+      } else {  // len == 0.0, two identical points defined in a facet.
+        printf("Warning:  Facet %d has two identical vertices: %d, %d.\n",
+               facetidx, pointmark(pt1), pointmark(pt3));
+        return false; // Invalid polygon, do not procced.
+      }
+    }
+    if (smallcos == 1.0) {
+      // The input set of vertices is not a good set (or nearly degenerate).
+      printf("Warning:  Facet %d with vertices: ", facetidx);
+      for (i = 0; i < 3; i++) {
+        idx =  * (int *) (* ptlist)[i];
+        ptloop = idx2verlist[idx - in->firstnumber];
+        printf("%d ", pointmark(ptloop));
+      }
+      printf("... is degenerate.\n");
+      return false; // Invalid polygon, do not procced.
+    }
+    // Get the right point to form v2.
+    pt3 = idx2verlist[smallidx - in->firstnumber];
+    assert(pt3 != pt2);
+    v2[0] = pt3[0] - pt1[0];
+    v2[1] = pt3[1] - pt1[1];
+    v2[2] = pt3[2] - pt1[2];
+    len = sqrt(dot(v2, v2));
+    assert(len > 0.0);
+    // Remember this size as lift distance.
+    liftdist = (liftdist > len ? liftdist : len);
+    // 'v2' is a reasonable vector, normalize it.
+    for (i = 0; i < 3; i++) v2[i] /= len;
+  } else { 
+    // There are only three vertices of this facet (a triangle).
+    idx =  * (int *) (* ptlist)[0];
+    pt1 = idx2verlist[idx - in->firstnumber];
+    idx =  * (int *) (* ptlist)[1];
+    pt2 = idx2verlist[idx - in->firstnumber];
+    idx =  * (int *) (* ptlist)[2];
+    pt3 = idx2verlist[idx - in->firstnumber];
+    v1[0] = pt2[0] - pt1[0];
+    v1[1] = pt2[1] - pt1[1];
+    v1[2] = pt2[2] - pt1[2];
+    len = sqrt(dot(v1, v1));
+    if (len == 0.0) {
+      printf("Warning:  Facet %d has two identical vertices: %d, %d.\n",
+             facetidx, pointmark(pt1), pointmark(pt2));
+      return false; // Invalid polygon, do not procced.
+    }
+    // Remember this size as lift distance.
+    liftdist = len;
+    // 'v1' is a reasonable vector, normalize it.
+    for (i = 0; i < 3; i++) v1[i] /= len;
+    v2[0] = pt3[0] - pt1[0];
+    v2[1] = pt3[1] - pt1[1];
+    v2[2] = pt3[2] - pt1[2];
+    len = sqrt(dot(v2, v2));
+    if (len == 0.0) {
+      printf("Warning:  Facet %d has two identical vertices: %d, %d.\n",
+             facetidx, pointmark(pt1), pointmark(pt3));
+      return false; // Invalid polygon, do not procced.
+    }
+    // Remember this size as lift distance.
+    liftdist = (liftdist > len ? liftdist : len);
+    // 'v2' is a reasonable vector, normalize it.
+    for (i = 0; i < 3; i++) v2[i] /= len;
+  }
+  // Calculate the unit normal of this facet.
+  cross(v1, v2, norm);
+
+  // Calculate the centroid point of the vertex set. At the same time, check
+  //   whether vertices of this facet are roughly coplanar or not.
+  cent[0] = cent[1] = cent[2] = 0.0;
+  for (i = 0; i < ptlist->len(); i++) {
+    idx =  * (int *) (* ptlist)[i];
+    ptloop = idx2verlist[idx - in->firstnumber];
+    if (ptlist->len() > 3) {
+      vol = orient3d(pt1, pt2, pt3, ptloop);
+      if (vol != 0.0) {
+        if (!iscoplanar(pt1, pt2, pt3, ptloop, vol, b->epsilon * 1e+3)) {
+          printf("Warning:  Facet %d has a non-coplanar vertex %d.\n",
+                 facetidx, pointmark(ptloop));
+          // This is not a fatal problem, we still can procced.
+        }
+      }
+    }
+    cent[0] += ptloop[0];
+    cent[1] += ptloop[1];
+    cent[2] += ptloop[2];
+  }
+  for (i = 0; i < 3; i++) cent[i] /= ptlist->len();
+  // Calculate the lifting point of the facet. It is lifted from 'cent'
+  //   along the normal direction with a certain ditance.
+  liftpoint = getliftpoint(facetidx); 
+  for (i = 0; i < 3; i++) {
+    liftpoint[i] = cent[i] + liftdist * norm[i];
+  }
+
+  // Create the initial triangle. The liftpoint is above (pt1, pt2, pt3).
+  makeshellface(subfaces, &newsh);
+  setsorg(newsh, pt1);
+  setsdest(newsh, pt2);
+  setsapex(newsh, pt3);
+  // Remeber the facet it belongs to.
+  setshellmark(newsh, facetidx);
+  // Set vertices be type FACETVERTEX to indicate they belong to a facet.
+  setpointtype(pt1, FACETVERTEX);
+  setpointtype(pt2, FACETVERTEX);
+  setpointtype(pt3, FACETVERTEX);
+  // Bond this subface to 'dummysh' for point location routine.
+  dummysh[0] = sencode(newsh);
+
+  return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// collectvisiblesubs()    Collect convex hull edges which are visible from  //
+//                         the inserting point. Construct new subfaces from  //
+//                         these edges and the point.                        //
+//                                                                           //
+// 'facetidx' is the index of the facet in 'in->facetlist' (starts from 1),  //
+// 'inspoint' is located outside current triangulation, 'horiz' is the hull  //
+// edge it is visible. 'flipqueue' returns the visible hull edges which have //
+// become interior edges on completion of this routine.                      //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::
+collectvisiblesubs(int facetidx, point inspoint, face* horiz, queue* flipqueue)
+{
+  face newsh, hullsh;
+  face rightsh, leftsh, spinedge;
+  point horg, hdest, liftpoint;
+  bool aboveflag;
+
+  liftpoint = getliftpoint(facetidx); 
+
+  // Create a new subface above 'horiz'.
+  adjustedgering(*horiz, CCW);
+  makeshellface(subfaces, &newsh);
+  setsorg(newsh, sdest(*horiz));
+  setsdest(newsh, sorg(*horiz));
+  setsapex(newsh, inspoint);
+  setshellmark(newsh, facetidx);
+  // Make the connection.
+  sbond(newsh, *horiz);
+  // 'horiz' becomes interior edge.
+  enqueueflipedge(*horiz, flipqueue);
+  
+  // Finish the hull edges at the right side of the newsh.
+  hullsh = *horiz;
+  while (1) {
+    senext(newsh, rightsh);
+    // Get the right hull edge of 'horiz' by spinning inside edges around
+    //   the origin of 'horiz' until reaching the 'dummysh'.
+    spinedge = hullsh;
+    do {
+      hullsh = spinedge;
+      senext2self(hullsh);
+      spivot(hullsh, spinedge);
+      adjustedgering(spinedge, CCW);
+    } while (spinedge.sh != dummysh);
+    // Test whether 'inspoint' is visible from 'hullsh'.
+    horg = sorg(hullsh);
+    hdest = sdest(hullsh);
+    aboveflag = orient3d(horg, hdest, liftpoint, inspoint) < 0.0;
+    if (aboveflag) {
+      // It's a visible hull edge.
+      makeshellface(subfaces, &newsh);
+      setsorg(newsh, sdest(hullsh));
+      setsdest(newsh, sorg(hullsh));
+      setsapex(newsh, inspoint);
+      setshellmark(newsh, facetidx);
+      // Make the connection.
+      sbond(newsh, hullsh);
+      senext2(newsh, leftsh);
+      sbond(leftsh, rightsh);
+      // 'horiz' becomes interior edge.
+      enqueueflipedge(hullsh, flipqueue); 
+    } else {
+      // 'rightsh' is a new hull edge.
+      dummysh[0] = sencode(rightsh);
+      break;
+    }
+  }
+
+  // Finish the hull edges at the left side of the newsh.
+  hullsh = *horiz;
+  spivot(*horiz, newsh);
+  while (1) {
+    senext2(newsh, leftsh);
+    // Get the left hull edge of 'horiz' by spinning edges around the
+    //   destination of 'horiz'.
+    spinedge = hullsh;
+    do {
+      hullsh = spinedge;
+      senextself(hullsh);
+      spivot(hullsh, spinedge);
+      adjustedgering(spinedge, CCW);
+    } while (spinedge.sh != dummysh);
+    // Test whether 'inspoint' is visible from 'hullsh'.
+    horg = sorg(hullsh);
+    hdest = sdest(hullsh);
+    aboveflag = orient3d(horg, hdest, liftpoint, inspoint) < 0.0;
+    if (aboveflag) {
+      // It's a visible hull edge.
+      makeshellface(subfaces, &newsh);
+      setsorg(newsh, sdest(hullsh));
+      setsdest(newsh, sorg(hullsh));
+      setsapex(newsh, inspoint);
+      setshellmark(newsh, facetidx);
+      // Make the connection.
+      sbond(newsh, hullsh);
+      senext(newsh, rightsh);
+      sbond(rightsh, leftsh);
+      // 'horiz' becomes interior edge.
+      enqueueflipedge(hullsh, flipqueue); 
+    } else {
+      // 'leftsh' is a new hull edge.
+      dummysh[0] = sencode(leftsh);
+      break;
+    }
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// incrflipdelaunaysub()    Create a Delaunay triangulation from a 3D point  //
+//                          set using the incremental flip algorithm.        //
+//                                                                           //
+// 'facetidx' is the index of the facet in 'in->facetlist' (starts from 1),  //
+// 'ptlist' is the index list of the vertices of the facet, 'idx2verlist' is //
+// a map from indices to vertices.                                           //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::
+incrflipdelaunaysub(int facetidx, list* ptlist, point* idx2verlist,
+                    queue* flipqueue)
+{
+  face startsh;
+  point pointloop;
+  enum locateresult loc;
+  int idx, i;  
+
+  for (i = 1; i < ptlist->len(); i++) {
+    idx =  * (int *) (* ptlist)[i];
+    pointloop = idx2verlist[idx - in->firstnumber];
+    // Set vertices be type FACETVERTEX to indicate they belong to a facet.
+    setpointtype(pointloop, FACETVERTEX);
+    startsh.sh = dummysh;
+    loc = locatesub(pointloop, &startsh, NULL);
+    if (loc == ONVERTEX) continue;
+    if (loc == ONFACE) {
+      splitsubface(pointloop, &startsh, flipqueue);
+    } else if (loc == ONEDGE) {
+      splitsubedge(pointloop, &startsh, flipqueue);
+    } else if (loc == OUTSIDE) {
+      collectvisiblesubs(facetidx, pointloop, &startsh, flipqueue);
+    }
+    flipsub(flipqueue);
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// finddirectionsub()    Find the first subface in a facet on the path from  //
+//                       one point to another.                               //
+//                                                                           //
+// Finds the subface in the facet that intersects a line segment drawn from  //
+// the origin of `searchsh' to the point `tend', and returns the result in   //
+// `searchsh'.  The origin of `searchsh' does not change,  even though the   //
+// subface returned may differ from the one passed in.                       //
+//                                                                           //
+// The return value notes whether the destination or apex of the found face  //
+// is collinear with the two points in question.                             //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+enum tetgenmesh::finddirectionresult tetgenmesh::
+finddirectionsub(face* searchsh, point tend)
+{
+  face checksh;
+  point startpoint, liftpoint;
+  point leftpoint, rightpoint;
+  REAL leftccw, rightccw;
+  int leftflag, rightflag;
+
+  adjustedgering(*searchsh, CCW);
+  liftpoint = getliftpoint(shellmark(*searchsh)); 
+  startpoint = sorg(*searchsh);
+  rightpoint = sdest(*searchsh);
+  leftpoint = sapex(*searchsh);
+  // Is `tend' to the left?
+  leftccw = orient3d(tend, startpoint, liftpoint, leftpoint);
+  leftflag = leftccw > 0.0;
+  // Is `tend' to the right?
+  rightccw = orient3d(startpoint, tend, liftpoint, rightpoint);
+  rightflag = rightccw > 0.0;
+  if (leftflag && rightflag) {
+    // `searchsh' faces directly away from `tend'.  We could go left or
+    //   right.  Ask whether it's a triangle or a boundary on the left.
+    senext2(*searchsh, checksh);
+    spivotself(checksh);
+    if (checksh.sh == dummysh) {
+      leftflag = 0;
+    } else {
+      rightflag = 0;
+    }
+  }
+  while (leftflag) {
+    // Turn left until satisfied.
+    senext2self(*searchsh);
+    spivotself(*searchsh);
+    if (searchsh->sh == dummysh) {
+      printf("Internal error in finddirectionsub():  Unable to find a\n");
+      printf("  triangle leading from %d to %d.\n", pointmark(startpoint),
+             pointmark(tend));
+      internalerror();
+    }
+    adjustedgering(*searchsh, CCW);
+    leftpoint = sapex(*searchsh);
+    rightccw = leftccw;
+    leftccw = orient3d(tend, startpoint, liftpoint, leftpoint);
+    leftflag = leftccw > 0.0;
+  }
+  while (rightflag) {
+    // Turn right until satisfied.
+    spivotself(*searchsh);
+    if (searchsh->sh == dummysh) {
+      printf("Internal error in finddirectionsub():  Unable to find a\n");
+      printf("  triangle leading from %d to %d.\n", pointmark(startpoint),
+             pointmark(tend));
+      internalerror();
+    }
+    adjustedgering(*searchsh, CCW);
+    senextself(*searchsh);
+    rightpoint = sdest(*searchsh);
+    leftccw = rightccw;
+    rightccw = orient3d(startpoint, tend, liftpoint, rightpoint);
+    rightflag = rightccw > 0.0;
+  }
+  if (leftccw == 0.0) {
+    return LEFTCOLLINEAR;
+  } else if (rightccw == 0.0) {
+    return RIGHTCOLLINEAR;
+  } else {
+    return ACROSSEDGE;
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// insertsubseg()    Create a subsegment and insert it between two subfaces. //
+//                                                                           //
+// The new subsegment is inserted at the edge described by the handle 'tri'. //
+// If 'tri' is not on the hull, the segment is inserted between two faces.   //
+// If 'tri' is a hull face, the initial face ring of this segment will be    //
+// set only one face which is self-bonded.  The official face ring will be   //
+// constructed later in routine unifysegments().                             //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::insertsubseg(face* tri)
+{
+  face oppotri;
+  face newsubseg;
+
+  // Check if there's already a subsegment here.
+  sspivot(*tri, newsubseg);
+  if (newsubseg.sh == dummysh) {
+    // Make new subsegment and initialize its vertices.
+    makeshellface(subsegs, &newsubseg);
+    setsorg(newsubseg, sorg(*tri));
+    setsdest(newsubseg, sdest(*tri));
+    // Bond new subsegment to the two triangles it is sandwiched between.
+    ssbond(*tri, newsubseg);
+    spivot(*tri, oppotri);
+    // 'oppotri' might be "out space".
+    if (oppotri.sh != dummysh) {
+      ssbond(oppotri, newsubseg);
+    } else {
+      // Outside! Bond '*tri' to itself.
+      sbond(*tri, *tri);
+    }
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// scoutsegmentsub()    Scout the first triangle on the path from one point  //
+//                      to another, and check for completion (reaching the   //
+//                      second point), a collinear point,or the intersection //
+//                      of two segments.                                     //
+//                                                                           //
+// Returns true if the entire segment is successfully inserted, and false if //
+// the job must be finished by constrainededge().                            //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::scoutsegmentsub(face* searchsh, point tend)
+{
+  face newsubseg;
+  face crosssub, crosssubseg;
+  point leftpoint, rightpoint;
+  enum finddirectionresult collinear;
+
+  collinear = finddirectionsub(searchsh, tend);
+  rightpoint = sdest(*searchsh);
+  leftpoint = sapex(*searchsh);
+  if (rightpoint == tend || leftpoint == tend) {
+    // The segment is already an edge.
+    if (leftpoint == tend) {
+      senext2self(*searchsh);
+    }
+    // Insert a subsegment.
+    insertsubseg(searchsh);
+    return true;
+  } else if (collinear == LEFTCOLLINEAR) {
+    // We've collided with a vertex between the segment's endpoints.
+    // Make the collinear vertex be the triangle's origin.
+    senextself(*searchsh); // lprevself(*searchtri);
+    // Insert a subsegment.
+    insertsubseg(searchsh);
+    // Insert the remainder of the segment.
+    return scoutsegmentsub(searchsh, tend);
+  } else if (collinear == RIGHTCOLLINEAR) {
+    // We've collided with a vertex between the segment's endpoints.
+    // Insert a subsegment.
+    insertsubseg(searchsh);
+    // Make the collinear vertex be the triangle's origin.
+    senextself(*searchsh); // lnextself(*searchtri);
+    // Insert the remainder of the segment.
+    return scoutsegmentsub(searchsh, tend);
+  } else {
+    senext(*searchsh, crosssub); // lnext(*searchtri, crosstri);
+    // Check for a crossing segment.
+    sspivot(crosssub, crosssubseg);
+    assert(crosssubseg.sh == dummysh);
+    return false;
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// delaunayfixup()    Enforce the Delaunay condition at an edge, fanning out //
+//                    recursively from an existing point. Pay special        //
+//                    attention to stacking inverted triangles.              //
+//                                                                           //
+// This is a support routine for inserting segments into a constrained       //
+// Delaunay triangulation.                                                   //
+//                                                                           //
+// The origin of 'fixupsh' is treated as if it has just been inserted, and   //
+// the local Delaunay condition needs to be enforced. It is only enforced in //
+// one sector, however, that being the angular range defined by 'fixupsh'.   //
+//                                                                           //
+// `leftside' indicates whether or not fixupsh is to the left of the segment //
+// being inserted.  (Imagine that the segment is pointing up from endpoint1  //
+// to endpoint2.)                                                            //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::delaunayfixup(face* fixupsh, int leftside)
+{
+  face nearsh, farsh, faredge;
+  point nearpoint, leftpoint, rightpoint, farpoint;
+  point liftpoint;
+  REAL sign;
+
+  // It is up to the caller, that 'fixupsh' must be in CCW edge ring.
+  // adjustedgering(*fixupsh, CCW);
+  assert((fixupsh->shver % 2) == 0);
+  senext(*fixupsh, nearsh);
+  spivot(nearsh, farsh);
+  if (nearsh.sh == farsh.sh) {
+    farsh.sh = dummysh;
+  }
+  // Check if the edge opposite the origin of fixupsh can be flipped.
+  if (farsh.sh == dummysh) {
+    return;
+  }
+  adjustedgering(farsh, CCW);
+  sspivot(nearsh, faredge);
+  if (faredge.sh != dummysh) {
+    return;
+  }
+  // Find all the relevant vertices.
+  liftpoint = getliftpoint(shellmark(*fixupsh));
+  nearpoint = sapex(nearsh);
+  leftpoint = sorg(nearsh);
+  rightpoint = sdest(nearsh);
+  farpoint = sapex(farsh);
+  // Check whether the previous polygon point is a reflex point.
+  if (leftside) {
+    if (orient3d(nearpoint, leftpoint, liftpoint, farpoint) <= 0.0) {
+      // leftpoint is a reflex point too.  Nothing can
+      //   be done until a convex section is found. 
+      return;
+    }
+  } else {
+    if (orient3d(farpoint, rightpoint, liftpoint, nearpoint) <= 0.0) {
+      // rightpoint is a reflex point too.  Nothing can
+      //   be done until a convex section is found.
+      return;
+    }
+  }
+  if (orient3d(rightpoint, leftpoint, liftpoint, farpoint) > 0.0) {
+    // farsh is not an inverted triangle, and farpoint is not a reflex
+    //   point.  As there are no reflex vertices, fixupsh isn't an
+    //   inverted triangle, either.  Hence, test the edge between the
+    //   triangles to ensure it is locally Delaunay.
+    sign = insphere(leftpoint, farpoint, rightpoint, liftpoint, nearpoint)
+         * orient3d(leftpoint, farpoint, rightpoint, liftpoint);
+    if (sign <= 0.0) {
+      return;
+    }
+    // Not locally Delaunay; go on to an edge flip.
+  }         // else farsh is inverted; remove it from the stack by flipping.
+  flip22sub(&nearsh, NULL);
+  senext2self(*fixupsh);    // Restore the origin of fixupsh after the flip.
+  // Recursively process the two triangles that result from the flip.
+  delaunayfixup(fixupsh, leftside);
+  delaunayfixup(&farsh, leftside);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// constrainededge()    Force a segment into a constrained Delaunay          //
+//                      triangulation by deleting the triangles it           //
+//                      intersects, and triangulating the polygons that      //
+//                      form on each side of it.                             //
+//                                                                           //
+// Generates a single subsegment connecting `tstart' to `tend'. The triangle //
+// `startsh' has `tstart' as its origin.                                     //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::constrainededge(face* startsh, point tend)
+{
+  face fixupsh, fixupsh2;
+  face crosssubseg, newsubseg;
+  point tstart, farpoint;
+  point liftpoint;
+  REAL area;
+  int collision;
+  int done;
+
+  liftpoint = getliftpoint(shellmark(*startsh));
+  tstart = sorg(*startsh);
+  // Always works in the CCW edge ring.
+  adjustedgering(*startsh, CCW);
+  // Make sure the 'tstart' remians be the origin.
+  if (sorg(*startsh) != tstart) {
+    senextself(*startsh);
+    assert(sorg(*startsh) == tstart);
+  }
+  senext(*startsh, fixupsh);
+  flip22sub(&fixupsh, NULL);
+  // `collision' indicates whether we have found a vertex directly
+  //   between endpoint1 and endpoint2.
+  collision = 0;
+  done = 0;
+  do {
+    farpoint = sorg(fixupsh);
+    // `farpoint' is the extreme point of the polygon we are "digging"
+    //   to get from tstart to tend.
+    if (farpoint == tend) {
+      spivot(fixupsh, fixupsh2);  // oprev(fixupsh, fixupsh2);
+      adjustedgering(fixupsh2, CCW);
+      senextself(fixupsh2);
+      // Enforce the Delaunay condition around tend.
+      delaunayfixup(&fixupsh, 0);
+      delaunayfixup(&fixupsh2, 1);
+      done = 1;
+    } else {
+      // Check whether farpoint is to the left or right of the segment
+      //   being inserted, to decide which edge of fixupsh to dig 
+      //   through next.
+      area = orient3d(tstart, tend, liftpoint, farpoint);
+      if (area == 0.0) {
+        // We've collided with a vertex between tstart and tend.
+        collision = 1;
+        spivot(fixupsh, fixupsh2);  // oprev(fixupsh, fixupsh2);
+        adjustedgering(fixupsh2, CCW);
+        senextself(fixupsh2);
+        // Enforce the Delaunay condition around farpoint.
+        delaunayfixup(&fixupsh, 0);
+        delaunayfixup(&fixupsh2, 1);
+        done = 1;
+      } else {
+        if (area > 0.0) {        // farpoint is to the left of the segment.
+          spivot(fixupsh, fixupsh2);  // oprev(fixupsh, fixupsh2);
+          adjustedgering(fixupsh2, CCW);
+          senextself(fixupsh2); 
+          // Enforce the Delaunay condition around farpoint, on the
+          //   left side of the segment only.
+          delaunayfixup(&fixupsh2, 1);
+          // Flip the edge that crosses the segment.  After the edge is
+          //   flipped, one of its endpoints is the fan vertex, and the
+          //   destination of fixupsh is the fan vertex.
+          senext2self(fixupsh); // lprevself(fixupsh);
+        } else {                // farpoint is to the right of the segment.
+          delaunayfixup(&fixupsh, 0);
+          // Flip the edge that crosses the segment.  After the edge is
+          //   flipped, one of its endpoints is the fan vertex, and the
+          //   destination of fixupsh is the fan vertex.
+          spivotself(fixupsh);  // oprevself(fixupsh);
+          adjustedgering(fixupsh, CCW);
+          senextself(fixupsh); 
+        }
+        // Check for two intersecting segments.
+        sspivot(fixupsh, crosssubseg);
+        if (crosssubseg.sh == dummysh) {
+          flip22sub(&fixupsh, NULL);// May create inverted triangle at left.
+        } else {
+          // We've collided with a segment between tstart and tend.
+          /* collision = 1;
+          // Insert a vertex at the intersection.
+          segmentintersection(m, b, &fixupsh, &crosssubseg, tend);
+          done = 1;
+          */
+          assert(0);
+        }
+      }
+    }
+  } while (!done);
+  // Insert a subsegment to make the segment permanent.
+  insertsubseg(&fixupsh);
+  // If there was a collision with an interceding vertex, install another
+  //   segment connecting that vertex with endpoint2.
+  if (collision) {
+    // Insert the remainder of the segment.
+    if (!scoutsegmentsub(&fixupsh, tend)) {
+      constrainededge(&fixupsh, tend);
+    }
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// insertsegmentsub()    Insert a PSLG segment into a triangulation.         //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::insertsegmentsub(point tstart, point tend)
+{
+  face searchsh1, searchsh2;
+
+  if (b->verbose > 2) {
+    printf("    Insert subsegment (%d, %d).\n", pointmark(tstart),
+           pointmark(tend));
+  }
+
+  // Find a triangle whose origin is the segment's first endpoint.
+  searchsh1.sh = dummysh;
+  // Search for the segment's first endpoint by point location.
+  if (locatesub(tstart, &searchsh1, NULL) != ONVERTEX) {
+    printf("Internal error in insertsegmentsub():");
+    printf("  Unable to locate PSLG vertex %d.\n", pointmark(tstart));
+    internalerror();
+  }
+  // Scout the beginnings of a path from the first endpoint
+  //   toward the second. 
+  if (scoutsegmentsub(&searchsh1, tend)) {
+    // The segment was easily inserted.
+    return;
+  }
+  // The first endpoint may have changed if a collision with an intervening
+  //   vertex on the segment occurred.
+  tstart = sorg(searchsh1);
+  
+  // Find a boundary triangle to search from.
+  searchsh2.sh = dummysh;
+  // Search for the segment's second endpoint by point location.
+  if (locatesub(tend, &searchsh2, NULL) != ONVERTEX) {
+    printf("Internal error in insertsegmentsub():");
+    printf("  Unable to locate PSLG vertex %d.\n", pointmark(tend));
+    internalerror();
+  }
+  // Scout the beginnings of a path from the second endpoint
+  //   toward the first.
+  if (scoutsegmentsub(&searchsh2, tstart)) {
+    // The segment was easily inserted.
+    return;
+  }
+  // The second endpoint may have changed if a collision with an intervening
+  //   vertex on the segment occurred. 
+  tend = sorg(searchsh2);
+  
+  // Insert the segment directly into the triangulation.
+  constrainededge(&searchsh1, tend);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// infecthullsub()    Virally infect all of the triangles of the convex hull //
+//                    that are not protected by subsegments.                 //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::infecthullsub(memorypool* viri)
+{
+  face hulltri, nexttri, starttri;
+  face hullsubseg;
+  shellface **deadshellface;
+
+  // Find a triangle handle on the hull.
+  hulltri.sh = dummysh;
+  hulltri.shver = 0;
+  spivotself(hulltri);
+  adjustedgering(hulltri, CCW);
+  // Remember where we started so we know when to stop.
+  starttri = hulltri;
+  // Go once counterclockwise around the convex hull.
+  do {
+    // Ignore triangles that are already infected.
+    if (!sinfected(hulltri)) {
+      // Is the triangle protected by a subsegment?
+      sspivot(hulltri, hullsubseg);
+      if (hullsubseg.sh == dummysh) {
+        // The triangle is not protected; infect it.
+        if (!sinfected(hulltri)) {
+          sinfect(hulltri);
+          deadshellface = (shellface **) viri->alloc();
+          *deadshellface = hulltri.sh;
+        }
+      } 
+    }
+    // To find the next hull edge, go clockwise around the next vertex.
+    senextself(hulltri); // lnextself(hulltri);
+    spivot(hulltri, nexttri); // oprev(hulltri, nexttri);
+    if (nexttri.sh == hulltri.sh) {
+      nexttri.sh = dummysh;  // 'hulltri' is self-bonded.
+    } else {
+      adjustedgering(nexttri, CCW);
+      senextself(nexttri);
+    }
+    while (nexttri.sh != dummysh) {
+      hulltri = nexttri;
+      spivot(hulltri, nexttri); // oprev(hulltri, nexttri);
+      if (nexttri.sh == hulltri.sh) {
+        nexttri.sh = dummysh;  // 'hulltri' is self-bonded.
+      } else {
+        adjustedgering(nexttri, CCW);
+        senextself(nexttri);
+      }
+    }
+  } while (hulltri != starttri);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// plaguesub()    Spread the virus from all infected triangles to any        //
+//                neighbors not protected by subsegments.  Delete all        //
+//                infected triangles.                                        //
+//                                                                           //
+// This is the procedure that actually creates holes and concavities.        //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::plaguesub(memorypool* viri)
+{
+  face testtri, neighbor, ghostsh;
+  face neighborsubseg;
+  shellface **virusloop;
+  shellface **deadshellface;
+  int i;
+
+  // Loop through all the infected triangles, spreading the virus to
+  //   their neighbors, then to their neighbors' neighbors.
+  viri->traversalinit();
+  virusloop = (shellface **) viri->traverse();
+  while (virusloop != (shellface **) NULL) {
+    testtri.sh = *virusloop;
+    // Check each of the triangle's three neighbors.
+    for (i = 0; i < 3; i++) {
+      // Find the neighbor.
+      spivot(testtri, neighbor);
+      // Check for a subsegment between the triangle and its neighbor.
+      sspivot(testtri, neighborsubseg);
+      // Check if the neighbor is nonexistent or already infected.
+      if ((neighbor.sh == dummysh) || sinfected(neighbor)) {
+        if (neighborsubseg.sh != dummysh) {
+          // There is a subsegment separating the triangle from its
+          //   neighbor, but both triangles are dying, so the subsegment
+          //   dies too.
+          shellfacedealloc(subsegs, neighborsubseg.sh);
+          if (neighbor.sh != dummysh) {
+            // Make sure the subsegment doesn't get deallocated again
+            //   later when the infected neighbor is visited.
+            ssdissolve(neighbor);
+          }
+        }
+      } else {                   // The neighbor exists and is not infected.
+        if (neighborsubseg.sh == dummysh) {
+          // There is no subsegment protecting the neighbor, so the
+          //   neighbor becomes infected.
+          sinfect(neighbor);
+          // Ensure that the neighbor's neighbors will be infected.
+          deadshellface = (shellface **) viri->alloc();
+          *deadshellface = neighbor.sh;
+        } else {               // The neighbor is protected by a subsegment.
+          // Remove this triangle from the subsegment.
+          ssbond(neighbor, neighborsubseg);
+        }
+      }
+      senextself(testtri);
+    }
+    virusloop = (shellface **) viri->traverse();
+  }
+
+  ghostsh.sh = dummysh; // A handle of outer space.
+  viri->traversalinit();
+  virusloop = (shellface **) viri->traverse();
+  while (virusloop != (shellface **) NULL) {
+    testtri.sh = *virusloop;
+    // Record changes in the number of boundary edges, and disconnect
+    //   dead triangles from their neighbors. 
+    for (i = 0; i < 3; i++) {
+      spivot(testtri, neighbor);
+      if (neighbor.sh != dummysh) {
+        // Disconnect the triangle from its neighbor.
+        // sdissolve(neighbor);
+        sbond(neighbor, ghostsh); 
+      }
+      senextself(testtri);
+    }
+    // Return the dead triangle to the pool of triangles.
+    shellfacedealloc(subfaces, testtri.sh);
+    virusloop = (shellface **) viri->traverse();
+  }
+  // Empty the virus pool.
+  viri->restart();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// carveholessub()    Find the holes and infect them.  Find the area         //
+//                    constraints and infect them.  Infect the convex hull.  //
+//                    Spread the infection and kill triangles.  Spread the   //
+//                    area constraints.                                      //
+//                                                                           //
+// This routine mainly calls other routines to carry out all these functions.//
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::carveholessub(int holes, REAL* holelist)
+{
+  face searchtri, triangleloop;
+  shellface **holetri;
+  memorypool *viri;
+  enum locateresult intersect;
+  int i;
+
+  // Initialize a pool of viri to be used for holes, concavities.
+  viri = new memorypool(sizeof(shellface *), 1024, POINTER, 0);
+
+  // Mark as infected any unprotected triangles on the boundary.
+  //   This is one way by which concavities are created.
+  infecthullsub(viri);
+
+  if (holes > 0) {
+    // Infect each triangle in which a hole lies.
+    for (i = 0; i < 3 * holes; i += 3) {
+      // Ignore holes that aren't within the bounds of the mesh.
+      if ((holelist[i] >= xmin) && (holelist[i] <= xmax)
+          && (holelist[i + 1] >= ymin) && (holelist[i + 1] <= ymax)
+          && (holelist[i + 2] >= zmin) && (holelist[i + 2] <= zmax)) {
+        // Start searching from some triangle on the outer boundary.
+        searchtri.sh = dummysh;
+        // Find a triangle that contains the hole.
+        intersect = locatesub(&holelist[i], &searchtri, NULL);
+        if ((intersect != OUTSIDE) && (!sinfected(searchtri))) {
+          // Infect the triangle.  This is done by marking the triangle
+          //   as infected and including the triangle in the virus pool.
+          sinfect(searchtri);
+          holetri = (shellface **) viri->alloc();
+          *holetri = searchtri.sh;
+        }
+      }
+    }
+  }
+
+  if (viri->items > 0) {
+    // Carve the holes and concavities.
+    plaguesub(viri);
+  }
+  // The virus pool should be empty now.
+
+  // Free up memory.
+  delete viri;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// triangulatefacet()    Create the constrained Delaunay triang. of a facet. //
+//                                                                           //
+// 'facetidx' is the index of the facet in 'in->facetlist' (starts from 1),  //
+// 'idx2verlist' is a map from indices to vertices.  'ptlist' and 'conlist'  //
+// are two lists used to assemble the input data for each facet, 'ptlist'    //
+// stores the index set of its vertices, 'conlist' stores the set of its     //
+// segments, they should be empty on input and output.                       //
+//                                                                           //
+// On completion, the CDT of this facet is constructed in pool 'subfaces'.   //
+// Every isolated point on the facet will be set a type of FACETVERTEX.      //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::
+triangulatefacet(int facetidx, list* ptlist, list* conlist, point* idx2verlist,
+                 queue* flipqueue)
+{
+  tetgenio::facet *f;
+  tetgenio::polygon *p; 
+  point tstart, tend;
+  int end1, end2;
+  int *cons, idx1, idx2;
+  int i, j;
+  
+  if (b->verbose > 1) {
+    printf("  Triangulate facet %d.\n", facetidx);
+  }
+
+  // Get the pointer of the facet.  
+  f = &in->facetlist[facetidx - 1];
+
+  // Are there duplicated points?
+  if ((b->object == tetgenbehavior::STL) || dupverts) {
+    // Loop all polygons of this facet.
+    for (i = 0; i < f->numberofpolygons; i++) {
+      p = &(f->polygonlist[i]);
+      // Loop other vertices of this polygon.
+      for (j = 0; j < p->numberofvertices; j++) {
+        idx1 = p->vertexlist[j];
+        tstart = idx2verlist[idx1 - in->firstnumber];
+        if (pointtype(tstart) == DUPLICATEDVERTEX) {
+          // Reset the index of vertex-j.
+          tend = point2pt(tstart);
+          idx2 = pointmark(tend);
+          p->vertexlist[j] = idx2;
+        }
+      }
+    }
+  }
+
+  // Loop all polygons of this facet, get the sets of vertices and segments.
+  for (i = 0; i < f->numberofpolygons; i++) {
+    p = &(f->polygonlist[i]);
+    // Get the first vertex.
+    end1 = p->vertexlist[0];
+    if ((end1 < in->firstnumber) || 
+        (end1 >= in->firstnumber + in->numberofpoints)) {
+      if (!b->quiet) {
+        printf("Warning:  Invalid the 1st vertex %d of polygon", end1);
+        printf(" %d in facet %d.\n", i + 1, facetidx);
+      }
+      break; // Skip to mesh this facet.
+    }
+    // Save it in 'ptlist' if it didn't be added, and set its position.
+    idx1 = ptlist->hasitem(&end1);
+    if (idx1 == -1) {
+      ptlist->append(&end1);
+      idx1 = ptlist->len() - 1;
+    }
+    // Loop other vertices of this polygon.
+    for (j = 1; j <= p->numberofvertices; j++) {
+      // get a vertex.
+      if (j < p->numberofvertices) {
+        end2 = p->vertexlist[j];
+      } else {
+        end2 = p->vertexlist[0];  // Form a loop from last to first.
+      }
+      if ((end2 < in->firstnumber) ||
+          (end2 >= in->firstnumber + in->numberofpoints)) {
+        if (!b->quiet) {
+          printf("Warning:  Invalid vertex %d in polygon %d", end2, i + 1);
+          printf(" in facet %d.\n", facetidx);
+        }
+      } else {
+        if (end1 != end2) {
+          // 'end1' and 'end2' form a segment.  Save 'end2' in 'ptlist' if
+          //   it didn't be added before.
+          idx2 = ptlist->hasitem(&end2);
+          if (idx2 == -1) {
+            ptlist->append(&end2);
+            idx2 = ptlist->len() - 1;
+          }
+          // Save the segment in 'conlist'.
+	  cons = (int *) conlist->append(NULL);
+          cons[0] = idx1;
+          cons[1] = idx2;
+          // Set the start for next continuous segment.
+          end1 = end2;
+          idx1 = idx2;
+        } else {
+          // It's a (degenerate) segment with identical endpoints, which
+          //   represents an isolate vertex in facet.
+          if (p->numberofvertices > 2) {
+            // This may be an error in the input, anyway, we let it be.
+            if (!b->quiet) {
+              printf("Warning:  Polygon %d has two identical vertices", i + 1);
+              printf(" in facet %d.\n", facetidx);
+            }
+          } 
+          // Set the vertex type be 'FACETVERTEX'.
+          // setpointtype(idx2verlist[end1 - in->firstnumber], FACETVERTEX);
+          // Ignore this vertex.
+        } 
+      }
+      if (p->numberofvertices == 2) {
+        // This case the polygon is either a segment or an isolated vertex.
+        break;  
+      }
+    } 
+  } 
+
+  // Have got the vertex list and segment list.
+  if (b->verbose > 1) {
+    printf("    %d vertices, %d segments", ptlist->len(), conlist->len());
+    if (f->numberofholes > 0) {
+      printf(", %d holes\n", f->numberofholes);
+    }
+    printf(".\n");
+  }
+
+  if (ptlist->len() > 2) {
+    // Construct an initial triangulation.
+    if (incrflipinitsub(facetidx, ptlist, idx2verlist)) {
+      if (ptlist->len() > 3) {
+        // Create the Delaunay triangulation of 'ptlist'.
+        incrflipdelaunaysub(facetidx, ptlist, idx2verlist, flipqueue);
+      }
+      // Insert segments (in 'conlist') into the Delaunay triangulation.
+      for (i = 0; i < conlist->len(); i++) {
+        cons = (int *)(* conlist)[i];
+        idx1 = * (int *)(* ptlist)[cons[0]];
+        tstart = idx2verlist[idx1 - in->firstnumber];
+        idx2 = * (int *)(* ptlist)[cons[1]];
+        tend = idx2verlist[idx2 - in->firstnumber];
+        insertsegmentsub(tstart, tend);        
+      }
+      if (ptlist->len() > 3 && conlist->len() > 3) {
+        // Carve holes and concavities.
+        carveholessub(f->numberofholes, f->holelist);
+      }
+    }
+  }
+
+  // Clear working lists.
+  ptlist->clear();
+  conlist->clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// unifysegments()    Unify identical segments and build facet connections.  //
+//                                                                           //
+// After the surface mesh has been created. Each facet has its own segments. //
+// There are many segments having the same endpoints, which are indentical.  //
+// This routine has two purposes: (1) identify the set of segments which     //
+// have the same endpoints and unify them into one segment, remove redundant //
+// ones; and (2) create the face rings of the unified segments, hence, setup //
+// the facet connections.                                                    //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::unifysegments()
+{
+  list *sfacelist;
+  shellface **facesperverlist;
+  face subsegloop, testseg;
+  face sface, sface1, sface2;
+  point torg, tdest;
+  REAL da1, da2;
+  int *idx2facelist;
+  int idx, k, m;
+
+  if (b->verbose) {
+    printf("  Unifying segments.\n");
+  }
+
+  // Compute a mapping from indices of vertices to subfaces.
+  makesubfacemap(idx2facelist, facesperverlist);
+  // Initialize 'sfacelist' for constructing the face link of each segment.
+  sfacelist = new list(sizeof(face), NULL); 
+  
+  subsegs->traversalinit();
+  subsegloop.sh = shellfacetraverse(subsegs);
+  while (subsegloop.sh != (shellface *) NULL) {
+    subsegloop.shver = 0; // For sure.
+    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 = idx2facelist[idx]; k < idx2facelist[idx + 1]; k++) {
+      sface.sh = facesperverlist[k];
+      sface.shver = 0;
+      // sface may be died due to the removing of duplicated subfaces.
+      if (!isdead(&sface) && isfacehasedge(&sface, torg, tdest)) {
+        // 'sface' contains this segment.
+        findedge(&sface, torg, tdest);
+        // Save it in 'sfacelist'.
+        if (sfacelist->len() < 2) {
+          sfacelist->append(&sface);
+        } else {
+          for (m = 0; m < sfacelist->len() - 1; m++) {
+            sface1 = * (face *)(* sfacelist)[m];
+            sface2 = * (face *)(* sfacelist)[m + 1];
+            da1 = facedihedral(torg, tdest, sapex(sface1), sapex(sface));
+            da2 = facedihedral(torg, tdest, sapex(sface1),sapex(sface2));
+            if (da1 < da2) {
+              break;  // Insert it after m.
+            }
+          }
+          sfacelist->insert(m + 1, &sface);
+        }
+      }
+    }
+    if (b->verbose > 1) {
+      printf("    Identifying %d segments of (%d  %d).\n", sfacelist->len(),
+             pointmark(torg), pointmark(tdest));
+    }
+    // Set the connection between this segment and faces containing it,
+    //   at the same time, remove redundant segments.
+    for (k = 0; k < sfacelist->len(); k++) {
+      sface = *(face *)(* sfacelist)[k];
+      sspivot(sface, testseg);
+      // If 'testseg' is not 'subsegloop', it is a redundant segment that
+      //   needs be removed. BE CAREFUL it may already be removed. Do not
+      //   remove it twice, i.e., we need do test 'isdead()' together.
+      if ((testseg.sh != subsegloop.sh) && !isdead(&testseg)) {
+        shellfacedealloc(subsegs, testseg.sh);
+      }
+      // 'ssbond' bonds the subface and the segment together, and dissloves
+      //   the old bond as well.
+      ssbond(sface, subsegloop);
+    }
+    // Set connection between these faces.
+    sface = *(face *)(* sfacelist)[0];
+    for (k = 1; k <= sfacelist->len(); k++) {
+      if (k < sfacelist->len()) {
+        sface1 = *(face *)(* sfacelist)[k];
+      } else {
+        sface1 = *(face *)(* sfacelist)[0];    // Form a face loop.
+      }
+      /*
+      // Check if these two subfaces are the same. It is possible when user
+      //   defines one facet (or polygon) two or more times. If they are,
+      //   they should not be bonded together, instead of that, one of them
+      //   should be delete from the surface mesh.
+      if ((sfacelist->len() > 1) && sapex(sface) == sapex(sface1)) {
+        // They are duplicated faces.
+        if (b->verbose) {
+          printf("  A duplicated subface (%d, %d, %d) is removed.\n",
+                 pointmark(torg), pointmark(tdest), pointmark(sapex(sface)));
+        }
+        if (k == sfacelist->len()) {
+          // 'sface' is the last face, however, it is same as the first one.
+          //   In order to form the ring, we have to let the second last
+          //   face bond to the first one 'sface1'.
+          shellfacedealloc(subfaces, sface.sh);
+          assert(sfacelist->len() >= 2);
+          assert(k == sfacelist->len());
+          sface = *(face *)(* sfacelist)[k - 2];
+        } else {
+          // 'sface1' is in the middle and may be the last one. 
+          shellfacedealloc(subfaces, sface1.sh);
+          // Skip this face and go to the next one.
+          continue;
+        }
+      }
+      */ 
+      if (b->verbose > 2) {
+        printf("    Bond subfaces (%d, %d, %d) and (%d, %d, %d).\n",
+               pointmark(torg), pointmark(tdest), pointmark(sapex(sface)),
+               pointmark(torg), pointmark(tdest), pointmark(sapex(sface1)));
+      }
+      sbond1(sface, sface1);
+      sface = sface1;
+    }
+    // Clear the working list.
+    sfacelist->clear(); 
+    subsegloop.sh = shellfacetraverse(subsegs);
+  }
+
+  delete [] idx2facelist;
+  delete [] facesperverlist;
+  delete sfacelist;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// mergefacets()    Merge adjacent facets to be one facet if they are        //
+//                  coplanar and have the same boundary marker.              //
+//                                                                           //
+// Segments between two merged facets will be removed from the mesh.  If all //
+// segments around a vertex have been removed, change its vertex type to be  //
+// FACETVERTEX. Edge flips will be performed to ensure the Delaunay criteria //
+// of the triangulation of merged facets.                                    //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::mergefacets(queue* flipqueue)
+{
+  face parentsh, neighsh, neineighsh;
+  face segloop;
+  point eorg, edest;
+  REAL ori;
+  bool mergeflag;
+  int* segspernodelist;
+  int fidx1, fidx2;
+  int i, j;
+
+  if (b->verbose) {
+    printf("  Merging coplanar facets.\n");
+  }
+  // Create and initialize 'segspernodelist'.
+  segspernodelist = new int[points->items + 1];
+  for (i = 0; i < points->items + 1; i++) {
+    segspernodelist[i] = 0;
+  }
+
+  // Loop the segments, counter the number of segments sharing each vertex.
+  subsegs->traversalinit();
+  segloop.sh = shellfacetraverse(subsegs);
+  while (segloop.sh != (shellface *) NULL) {
+    // Increment the number of sharing segments for each endpoint.
+    for (i = 0; i < 2; i++) {
+      j = pointmark((point) segloop.sh[3 + i]);
+      segspernodelist[j]++;
+    }
+    segloop.sh = shellfacetraverse(subsegs);
+  }
+
+  // Loop the segments, find out dead segments.
+  subsegs->traversalinit();
+  segloop.sh = shellfacetraverse(subsegs);
+  while (segloop.sh != (shellface *) NULL) {
+    eorg = sorg(segloop);
+    edest = sdest(segloop);
+    spivot(segloop, parentsh);
+    spivot(parentsh, neighsh);
+    spivot(neighsh, neineighsh);
+    if (parentsh.sh != neighsh.sh && parentsh.sh == neineighsh.sh) {
+      // Exactly two subfaces at this segment.
+      fidx1 = shellmark(parentsh) - 1;
+      fidx2 = shellmark(neighsh) - 1;
+      // Possibly merge them if they are not in the same facet.
+      if (fidx1 != fidx2) {
+        // Test if they are coplanar.
+        ori = orient3d(eorg, edest, sapex(parentsh), sapex(neighsh));
+        if (ori != 0.0) {
+          if (iscoplanar(eorg, edest, sapex(parentsh), sapex(neighsh), ori,
+                         b->epsilon)) {
+            ori = 0.0; // They are assumed as coplanar.
+          }
+        }
+        if (ori == 0.0) {
+          mergeflag = (in->facetmarkerlist == (int *) NULL || 
+	    in->facetmarkerlist[fidx1] == in->facetmarkerlist[fidx2]);
+          if (mergeflag) {
+            // This segment becomes dead.
+            if (b->verbose > 1) {
+              printf("  Removing segment (%d, %d).\n", pointmark(eorg),
+                     pointmark(edest));
+            }
+            ssdissolve(parentsh);
+            ssdissolve(neighsh);
+            shellfacedealloc(subsegs, segloop.sh);
+            j = pointmark(eorg);
+            segspernodelist[j]--;
+            if (segspernodelist[j] == 0) {
+              setpointtype(eorg, FACETVERTEX);
+            }
+            j = pointmark(edest);
+            segspernodelist[j]--;
+            if (segspernodelist[j] == 0) {
+              setpointtype(edest, FACETVERTEX);
+            }
+            // Add 'parentsh' to queue checking for flip.
+            enqueueflipedge(parentsh, flipqueue);
+          }
+        }
+      }
+    }
+    segloop.sh = shellfacetraverse(subsegs);
+  }
+
+  if (!flipqueue->empty()) {
+    // Restore the Delaunay property in the facet triangulation.
+    flipsub(flipqueue);
+  }
+
+  delete [] segspernodelist;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// meshsurface()    Create a surface triangulation of a PLC.                 //
+//                                                                           //
+// The surface mesh consists of a set of subfaces which are two dimensional  //
+// constrained Delaunay triangulations of the facets of the PLC and a set of //
+// subsegments which are edges bounded the facets.  Subfaces belong to one   //
+// facet are connecting each other. Around each subsegment is a subface ring,//
+// which saves the connection between facets sharing at this subsegment.     //
+//                                                                           //
+// This routine first creates the CDTs separatly, that is, each facet will   //
+// be meshed into a set of subfaces and subsegments.  As a result, subfaces  //
+// only have connections to subfaces which are belong to the same facet. And //
+// subsegments are over-created.  Then, routine unifysegment() is called to  //
+// remove redundant subsegments and create the face ring around subsegments. //
+//                                                                           //
+// Return the number of (input) segments.                                    //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+long tetgenmesh::meshsurface()
+{
+  list *ptlist, *conlist;
+  queue *flipqueue;
+  point *idx2verlist;
+  int i;
+
+  if (!b->quiet) {
+    printf("Creating surface mesh.\n");
+  }
+
+  // Compute a mapping from indices to points.
+  makeindex2pointmap(idx2verlist);
+  // Initialize 'liftpointarray'.
+  liftpointarray = new REAL[in->numberoffacets * 3];
+  // Initialize 'flipqueue'.
+  flipqueue = new queue(sizeof(badface));
+  // Two re-useable lists 'ptlist' and 'conlist'.
+  ptlist = new list("int");
+  conlist = new list(sizeof(int) * 2, NULL);
+
+  // Loop the facet list, triangulate each facet. On finish, all subfaces
+  //   are in 'subfaces', all segments are in 'subsegs' (Note: there exist
+  //   duplicated segments).
+  for (i = 0; i < in->numberoffacets; i++) {
+    triangulatefacet(i + 1, ptlist, conlist, idx2verlist, flipqueue);
+  }
+
+  // Unify segments in 'subsegs', remove redundant segments.  Face links
+  //   of segments are also built.
+  unifysegments();
+
+  if (b->object == tetgenbehavior::STL) {
+    // Remove redundant vertices (for .stl input mesh).
+    jettisonnodes();
+  }
+
+  if (!b->nomerge) {
+    // Merge adjacent facets if they are coplanar.
+    mergefacets(flipqueue);
+  }
+
+  delete [] idx2verlist;
+  delete flipqueue;
+  delete conlist;
+  delete ptlist;
+
+  return subsegs->items;
+}
+
+//
+// End of surface triangulation routines
+//
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// interecursive()    Recursively do intersection test on a set of triangles.//
+//                                                                           //
+// Recursively split the set 'subfacearray' of subfaces into two sets using  //
+// a cut plane parallel to x-, or, y-, or z-axies.  The split criteria are   //
+// follows. Assume the cut plane is H, and H+ denotes the left halfspace of  //
+// H, and H- denotes the right halfspace of H; and s be a subface:           //
+//                                                                           //
+//    (1) If all points of s lie at H+, put it into left array;              //
+//    (2) If all points of s lie at H-, put it into right array;             //
+//    (3) If some points of s lie at H+ and some of lie at H-, or some       //
+//        points lie on H, put it into both arraies.                         //
+//                                                                           //
+// Partitions by x-axis if axis == '0'; by y-axis if axis == '1'; by z-axis  //
+// if axis == '2'. If current cut plane is parallel to the x-axis, the next  //
+// one will be parallel to y-axis, and the next one after the next is z-axis,//
+// and then alternately return back to x-axis.                               //
+//                                                                           //
+// Stop splitting when the number of triangles of the input array is not     //
+// decreased anymore. Do tests on the current set.                           //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::
+interecursive(shellface** subfacearray, int arraysize, int axis, REAL bxmin,
+              REAL bxmax, REAL bymin, REAL bymax, REAL bzmin, REAL bzmax,
+              int* internum)
+{
+  shellface **leftarray, **rightarray;
+  face sface1, sface2;
+  point p1, p2, p3;
+  point p4, p5, p6;
+  enum intersectresult intersect;
+  REAL split;
+  bool toleft, toright;
+  int leftsize, rightsize;
+  int i, j;
+
+  if (b->verbose > 1) {
+    printf("  Recur %d faces. Bbox (%g, %g, %g),(%g, %g, %g). %s-axis\n",
+           arraysize, bxmin, bymin, bzmin, bxmax, bymax, bzmax,
+           axis == 0 ? "x" : (axis == 1 ? "y" : "z"));
+  }
+    
+  leftarray = new shellface*[arraysize];
+  if (leftarray == NULL) {
+    printf("Error in interecursive():  Insufficient memory.\n");
+    exit(1);
+  }
+  rightarray = new shellface*[arraysize];
+  if (rightarray == NULL) {
+    printf("Error in interecursive():  Insufficient memory.\n");
+    exit(1);
+  }
+  leftsize = rightsize = 0;
+
+  if (axis == 0) {
+    // Split along x-axis.
+    split = 0.5 * (bxmin + bxmax);
+  } else if (axis == 1) {
+    // Split along y-axis.
+    split = 0.5 * (bymin + bymax);
+  } else {
+    // Split along z-axis.
+    split = 0.5 * (bzmin + bzmax);
+  }
+
+  for (i = 0; i < arraysize; i++) {
+    sface1.sh = subfacearray[i];
+    p1 = (point) sface1.sh[3];
+    p2 = (point) sface1.sh[4];
+    p3 = (point) sface1.sh[5];
+    toleft = toright = false;
+    if (p1[axis] < split) {
+      toleft = true;
+      if (p2[axis] >= split || p3[axis] >= split) {
+        toright = true;
+      } 
+    } else if (p1[axis] > split) {
+      toright = true;
+      if (p2[axis] <= split || p3[axis] <= split) {
+        toleft = true;
+      } 
+    } else {
+      // p1[axis] == split;
+      toleft = true;
+      toright = true;
+    }
+    // At least one is true;
+    assert(!(toleft == false && toright == false));
+    if (toleft) {
+      leftarray[leftsize] = sface1.sh;
+      leftsize++;
+    }
+    if (toright) {
+      rightarray[rightsize] = sface1.sh;
+      rightsize++;
+    }
+  }
+
+  if (leftsize < arraysize && rightsize < arraysize) {
+    // Continue to partition the input set. Now 'subfacearray' has been
+    //   split into two sets, it's memory can be freed. 'leftarray' and
+    //   'rightarray' will be freed in the next recursive (after they're
+    //   partitioned again or performing tests).
+    delete [] subfacearray;
+    // Continue to split these two sets.
+    if (axis == 0) {
+      interecursive(leftarray, leftsize, 1, bxmin, split, bymin, bymax,
+                    bzmin, bzmax, internum);
+      interecursive(rightarray, rightsize, 1, split, bxmax, bymin, bymax,
+                    bzmin, bzmax, internum);
+    } else if (axis == 1) {
+      interecursive(leftarray, leftsize, 2, bxmin, bxmax, bymin, split,
+                    bzmin, bzmax, internum);
+      interecursive(rightarray, rightsize, 2, bxmin, bxmax, split, bymax,
+                    bzmin, bzmax, internum);
+    } else {
+      interecursive(leftarray, leftsize, 0, bxmin, bxmax, bymin, bymax,
+                    bzmin, split, internum);
+      interecursive(rightarray, rightsize, 0, bxmin, bxmax, bymin, bymax,
+                    split, bzmax, internum);
+    }
+  } else {
+    if (b->verbose > 1) {
+      printf("  Checking intersecting faces.\n");
+    }
+    // Perform a brute-force compare on the set.
+    for (i = 0; i < arraysize; i++) {
+      sface1.sh = subfacearray[i];
+      p1 = (point) sface1.sh[3];
+      p2 = (point) sface1.sh[4];
+      p3 = (point) sface1.sh[5];
+      for (j = i + 1; j < arraysize; j++) {
+        sface2.sh = subfacearray[j];
+        p4 = (point) sface2.sh[3];
+        p5 = (point) sface2.sh[4];
+        p6 = (point) sface2.sh[5];
+        intersect = triangle_triangle_inter(p1, p2, p3, p4, p5, p6);
+        if (intersect == INTERSECT || intersect == SHAREFACE) {
+          if (!b->quiet) {
+            if (intersect == INTERSECT) {
+              printf("  Facet #%d intersects facet #%d at triangles:\n",
+                     shellmark(sface1), shellmark(sface2));
+              printf("    (%4d, %4d, %4d) and (%4d, %4d, %4d)\n",
+                     pointmark(p1), pointmark(p2), pointmark(p3),
+                     pointmark(p4), pointmark(p5), pointmark(p6));
+            } else {
+              printf("  Facet #%d duplicates facet #%d at triangle:\n",
+                     shellmark(sface1), shellmark(sface2));
+              printf("    (%4d, %4d, %4d)\n", pointmark(p1), pointmark(p2),
+                     pointmark(p3));
+            }
+          }
+          // Increase the number of intersecting pairs.
+          (*internum)++; 
+          // Infect these two faces (although they may already be infected).
+          sinfect(sface1);
+          sinfect(sface2);
+        }
+      }
+    }
+    // Don't forget to free all three arrays. No further partition.
+    delete [] leftarray;
+    delete [] rightarray;  
+    delete [] subfacearray;
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// detectinterfaces()    Detect intersecting triangles.                      //
+//                                                                           //
+// Given a set of triangles,  find the pairs of intersecting triangles from  //
+// them.  Here the set of triangles is in 'subfaces' which is a surface mesh //
+// of a PLC (.poly or .smesh).                                               //
+//                                                                           //
+// To detect whether or not two triangles are intersecting is done by the    //
+// routine 'triangle_triangle_inter()'.  The algorithm for the test is very  //
+// simple and stable. It is based on geometric orientation test which uses   //
+// exact arithmetics.                                                        //
+//                                                                           //
+// Use divide-and-conquer algorithm for reducing the number of intersection  //
+// tests.  Start from the bounding box of the input point set, recursively   //
+// partition the box into smaller boxes, until the number of triangles in a  //
+// box is not decreased anymore. Then perform triangle-triangle tests on the //
+// remaining set of triangles.  The memory allocated in the input set is     //
+// freed immediately after it has been partitioned into two arrays.  So it   //
+// can be re-used for the consequent partitions.                             //
+//                                                                           //
+// On return, pool 'subfaces' will be cleared, and only the intersecting     //
+// triangles remain for output (to a .face file).                            //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::detectinterfaces()
+{
+  shellface **subfacearray;
+  face shloop;
+  int internum;
+  int i;
+
+  if (!b->quiet) {
+    printf("Detecting intersecting facets.\n");
+  }
+
+  // Construct a map from indices to subfaces;
+  subfacearray = new shellface*[subfaces->items];
+  subfaces->traversalinit();
+  shloop.sh = shellfacetraverse(subfaces);
+  i = 0;
+  while (shloop.sh != (shellface *) NULL) {
+    subfacearray[i] = shloop.sh;
+    shloop.sh = shellfacetraverse(subfaces);
+    i++;
+  }
+
+  internum = 0;
+  // Recursively split the set of triangles into two sets using a cut plane
+  //   parallel to x-, or, y-, or z-axies.  Stop splitting when the number
+  //   of subfaces is not decreasing anymore. Do tests on the current set.
+  interecursive(subfacearray, subfaces->items, 0, xmin, xmax, ymin, ymax,
+                zmin, zmax, &internum);
+
+  if (!b->quiet) {
+    if (internum > 0) {
+      printf("\n!! Found %d pairs of faces are intersecting.\n\n", internum);
+    } else {
+      printf("\nNo faces are intersecting.\n\n");
+    }
+  }
+
+  if (internum > 0) {
+    // Traverse all subfaces, deallocate those have not been infected (they
+    //   are not intersecting faces). Uninfect those have been infected.
+    //   After this loop, only intersecting faces remain.
+    subfaces->traversalinit();
+    shloop.sh = shellfacetraverse(subfaces);
+    while (shloop.sh != (shellface *) NULL) {
+      if (sinfected(shloop)) {
+        suninfect(shloop);
+      } else {
+        shellfacedealloc(subfaces, shloop.sh);
+      }
+      shloop.sh = shellfacetraverse(subfaces);
+    }
+  } else {
+    // Deallocate all subfaces.
+    subfaces->restart();
+  }
+}
+
+//
+// Begin of segments recovery routines
+//
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// markacutevertices()    Set the proper type (ACUTEVERTEX, NONACUTEVERTEX)  //
+//                        for segment vertices.                              //
+//                                                                           //
+// Parameter 'acuteangle' gives the upperbound (in degree). Angles which are //
+// smaller or equal than it are assumed as acute angles.  A vertex is acute  //
+// if at least two segments incident at it with an acute angle.              //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::markacutevertices(REAL acuteangle)
+{
+  shellface** segsperverlist;
+  face segloop, workseg, inciseg;
+  point eorg, edest, eapex;
+  REAL cosbound, anglearc;
+  REAL v1[3], v2[3], L, D;
+  bool isacute;
+  int* idx2seglist;
+  int idx, i, j, k;
+
+  if (b->verbose) {
+    printf("  Marking segments have acute corners.\n");
+  }
+
+  // Constructing a map from vertex to segments.
+  makesegmentmap(idx2seglist, segsperverlist);
+
+  // Initialize all vertices be unknown.
+  subsegs->traversalinit();
+  segloop.sh = shellfacetraverse(subsegs);
+  while (segloop.sh != (shellface *) NULL) {
+    // Check and set types for the two ends of this segment.
+    for (segloop.shver = 0; segloop.shver < 2; segloop.shver++) {
+      eorg = sorg(segloop);
+      setpointtype(eorg, FACETVERTEX);
+    }
+    segloop.sh = shellfacetraverse(subsegs);
+  }
+
+  anglearc = acuteangle * 3.1415926535897932 / 180.0;
+  cosbound = cos(anglearc);
+  
+  // Loop over the set of subsegments.
+  subsegs->traversalinit();
+  segloop.sh = shellfacetraverse(subsegs);
+  while (segloop.sh != (shellface *) NULL) {
+    // Check and set types for the two ends of this segment.
+    for (segloop.shver = 0; segloop.shver < 2; segloop.shver++) {
+      eorg = sorg(segloop);
+      if ((pointtype(eorg) != ACUTEVERTEX) && 
+          (pointtype(eorg) != NONACUTEVERTEX)) {
+        // This vertex has no type be set yet.
+        idx = pointmark(eorg) - in->firstnumber;
+        isacute = false;
+        for (i = idx2seglist[idx]; i < idx2seglist[idx + 1] && !isacute; i++) {
+          workseg.sh = segsperverlist[i];
+          workseg.shver = 0;
+          if (sorg(workseg) != eorg) {
+            sesymself(workseg);
+          }
+          assert(sorg(workseg) == eorg);
+          edest = sdest(workseg);
+          for (j = i + 1; j < idx2seglist[idx + 1] && !isacute; j++) {
+            inciseg.sh = segsperverlist[j];
+            inciseg.shver = 0;
+            assert(inciseg.sh != workseg.sh);
+            if (sorg(inciseg) != eorg) {
+              sesymself(inciseg);
+            }
+            assert(sorg(inciseg) == eorg);
+            eapex = sdest(inciseg);
+            // Check angles between segs (eorg, edest) and (eorg, eapex).
+            for (k = 0; k < 3; k++) {
+              v1[k] = edest[k] - eorg[k];
+              v2[k] = eapex[k] - eorg[k];
+            }
+            L = sqrt(v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2]);
+            for (k = 0; k < 3; k++) v1[k] /= L;
+            L = sqrt(v2[0] * v2[0] + v2[1] * v2[1] + v2[2] * v2[2]);
+            for (k = 0; k < 3; k++) v2[k] /= L;
+            D = v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];  
+            if (D >= cosbound) {
+              isacute = true; 
+            }
+          }
+        }
+        if (isacute) {
+          setpointtype(eorg, ACUTEVERTEX);
+        } else {
+          setpointtype(eorg, NONACUTEVERTEX);
+        }
+      }
+    }
+    segloop.sh = shellfacetraverse(subsegs);
+  }
+
+  delete [] idx2seglist;
+  delete [] segsperverlist;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// finddirection()    Find the first tetrahedron on the path from one point  //
+//                    to another.                                            //
+//                                                                           //
+// Find the tetrahedron that intersects a line segment L (from the origin of //
+// 'searchtet' to the point 'tend'), and returns the result in 'searchtet'.  //
+// The origin of 'searchtet' does not change, even though the tetrahedron    //
+// returned may differ from the one passed in.  This routine is used to find //
+// the direction to move in to get from one point to another.                //
+//                                                                           //
+// The return value notes the location of the line segment L with respect to //
+// 'searchtet':                                                              //
+//   - Returns RIGHTCOLLINEAR indicates L is collinear with the line segment //
+//     from the origin to the destination of 'searchtet'.                    //
+//   - Returns LEFTCOLLINEAR indicates L is collinear with the line segment  //
+//     from the origin to the apex of 'searchtet'.                           //
+//   - Returns TOPCOLLINEAR indicates L is collinear with the line segment   //
+//     from the origin to the opposite of 'searchtet'.                       //
+//   - Returns ACROSSEDGE indicates L intersects with the line segment from  //
+//     the destination to the apex of 'searchtet'.                           //
+//   - Returns ACROSSFACE indicates L intersects with the face opposite to   //
+//     the origin of 'searchtet'.                                            //
+//   - Returns BELOWHULL indicates L crosses outside the mesh domain. This   //
+//     can only happen when the domain is non-convex.                        //
+//                                                                           //
+// NOTE: This routine only works correctly when the mesh is exactly Delaunay.//
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+enum tetgenmesh::finddirectionresult tetgenmesh::
+finddirection(triface *searchtet, point tend)
+{
+  triface neightet;
+  point tstart, tdest, tapex, toppo;
+  REAL ori1, ori2, ori3;
+
+  tstart = org(*searchtet);
+  assert(tstart != tend);
+  adjustedgering(*searchtet, CCW);
+  if (tstart != org(*searchtet)) {
+    enextself(*searchtet); // For keeping the same origin.
+  }
+  tdest = dest(*searchtet);
+  if (tdest == tend) {
+    return RIGHTCOLLINEAR;
+  }
+  tapex = apex(*searchtet); 
+  if (tapex == tend) {
+    return LEFTCOLLINEAR;
+  } 
+
+  ori1 = orient3d(tstart, tdest, tapex, tend);
+  if (ori1 > 0.0) {
+    // 'tend' is below the face, get the neighbor of this side.
+    sym(*searchtet, neightet);
+    if (neightet.tet != dummytet) {
+      findorg(&neightet, tstart); 
+      adjustedgering(neightet, CCW);
+      if (org(neightet) != tstart) {
+        enextself(neightet); // keep the same origin.
+      }
+      // Set the changed configuratiuon.
+      *searchtet = neightet; 
+      ori1 = -1.0; 
+      tdest = dest(*searchtet);
+      tapex = apex(*searchtet);
+    } else {
+      // A hull face. Only possible for a nonconvex mesh.
+#ifdef SELF_CHECK
+      assert(nonconvex);
+#endif
+      return BELOWHULL; 
+    }
+  }
+
+  // Repeatedly change the 'searchtet', remain 'tstart' be its origin, until
+  //   find a tetrahedron contains 'tend' or is crossed by the line segment
+  //   from 'tstart' to 'tend'.
+  while (true) {
+    toppo = oppo(*searchtet);
+    if (toppo == tend) {
+      return TOPCOLLINEAR;
+    }
+    ori2 = orient3d(tstart, toppo, tdest, tend);
+    if (ori2 > 0.0) {
+      // 'tend' is below the face, get the neighbor at this side.
+      fnext(*searchtet, neightet);
+      symself(neightet);
+      if (neightet.tet != dummytet) {
+        findorg(&neightet, tstart); 
+        adjustedgering(neightet, CCW);
+        if (org(neightet) != tstart) {
+          enextself(neightet); // keep the same origin.
+        }
+        // Set the changed configuration.
+        *searchtet = neightet; 
+        ori1 = -1.0; 
+        tdest = dest(*searchtet);
+        tapex = apex(*searchtet);
+        // Continue the search from the changed 'searchtet'.
+        continue;
+      } else {
+        // A hull face. Only possible for a nonconvex mesh.
+#ifdef SELF_CHECK
+        assert(nonconvex);
+#endif
+        return BELOWHULL; 
+      }
+    }
+    ori3 = orient3d(tapex, toppo, tstart, tend);
+    if (ori3 > 0.0) {
+      // 'tend' is below the face, get the neighbor at this side.
+      enext2fnext(*searchtet, neightet);
+      symself(neightet);
+      if (neightet.tet != dummytet) {
+        findorg(&neightet, tstart); 
+        adjustedgering(neightet, CCW);
+        if (org(neightet) != tstart) {
+          enextself(neightet); // keep the same origin.
+        }
+        // Set the changed configuration.
+        *searchtet = neightet; 
+        ori1 = -1.0; 
+        tdest = dest(*searchtet);
+        tapex = apex(*searchtet);
+        // Continue the search from the changed 'searchtet'.
+        continue;
+      } else {
+        // A hull face. Only possible for a nonconvex mesh.
+#ifdef SELF_CHECK
+        assert(nonconvex);
+#endif
+        return BELOWHULL; 
+      }
+    }
+    // Now 'ori1', 'ori2' and 'ori3' are possible be 0.0 or all < 0.0;
+    if (ori1 < 0.0) {
+      // Possible cases are: ACROSSFACE, ACROSSEDGE, TOPCOLLINEAR.
+      if (ori2 < 0.0) {
+        if (ori3 < 0.0) {
+          return ACROSSFACE;
+        } else { // ori3 == 0.0;
+          // Cross edge (apex, oppo)
+          enext2fnextself(*searchtet);
+          esymself(*searchtet); // org(*searchtet) == tstart;
+          return ACROSSEDGE;
+        }
+      } else { // ori2 == 0.0; 
+        if (ori3 < 0.0) {
+          // Cross edge (dest, oppo)
+          fnextself(*searchtet);
+          esymself(*searchtet);
+          enextself(*searchtet); // org(*searchtet) == tstart;
+          return ACROSSEDGE;
+        } else { // ori3 == 0.0;
+          // Collinear with edge (org, oppo)
+          return TOPCOLLINEAR;
+        }
+      }
+    } else { // ori1 == 0.0;
+      // Possible cases are: RIGHTCOLLINEAR, LEFTCOLLINEAR, ACROSSEDGE.
+      if (ori2 < 0.0) {
+        if (ori3 < 0.0) {
+          // Cross edge (tdest, tapex)
+          return ACROSSEDGE;
+        } else { // ori3 == 0.0
+          // Collinear with edge (torg, tapex)
+          return LEFTCOLLINEAR;
+        }
+      } else { // ori2 == 0.0;
+        assert(ori3 != 0.0);
+        // Collinear with edge (torg, tdest)
+        return RIGHTCOLLINEAR;
+      }
+    }
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// getsearchtet()    Find a tetrahedron whose origin is either 'p1' or 'p2'. //
+//                                                                           //
+// On return, the origin of 'searchtet' is either 'p1' or 'p2',  and 'tend'  //
+// returns the other point.  'searchtet' serves as the starting tetrahedron  //
+// for searching of the line segment from 'p1' to 'p2' or vice versa.        //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::
+getsearchtet(point p1, point p2, triface* searchtet, point* tend)
+{
+  tetrahedron encodedtet1, encodedtet2;
+
+  // Is there a valid handle provided by the user?
+  if ((searchtet->tet != (tetrahedron *) NULL) && !isdead(searchtet)) {
+    // Find which endpoint the handle holds.
+    if (findorg(searchtet, p1)) {
+      *tend = p2;
+      return;
+    } else {
+      if (findorg(searchtet, p2)) {
+        *tend = p1;
+        return;
+      }
+    }
+  }
+  // If not, search the handle stored in 'p1' or 'p2'.
+  *tend = (point) NULL;
+  encodedtet1 = point2tet(p1);
+  encodedtet2 = point2tet(p2);
+  if (encodedtet1 != (tetrahedron) NULL) {
+    decode(encodedtet1, *searchtet);
+    // Be careful, here 'searchtet' may be dead.
+    if (findorg(searchtet, p1)) {
+      *tend = p2;
+    }
+  } else if (encodedtet2 != (tetrahedron) NULL) {
+    decode(encodedtet2, *searchtet);
+    // Be careful, here 'searchtet' may be dead.
+    if (findorg(searchtet, p2)) {
+      *tend = p1;
+    }
+  }
+  // If still not, perform a full point location.  The starting tetrahedron
+  //   is chosen as follows: Use the handle stored in 'p1' or 'p2' if it is
+  //   alive; otherwise, start from a tetrahedron on the convex hull.
+  if (*tend == (point) NULL) {
+    if (encodedtet1 != (tetrahedron) NULL) {
+      decode(encodedtet1, *searchtet);
+      // Be careful, here 'searchtet' may be dead.
+    }
+    if (isdead(searchtet)) {
+      if (encodedtet2 != (tetrahedron) NULL) {
+        decode(encodedtet2, *searchtet);
+        // Be careful, here 'searchtet' may be dead.
+      }
+      if (isdead(searchtet)) {
+        searchtet->tet = dummytet;
+        searchtet->loc = 0;
+        symself(*searchtet);
+      }
+      assert(!isdead(searchtet));
+    }
+    if (locate(p1, searchtet) != ONVERTEX) {
+      printf("Internal error in getsearchtet():  Failed to locate point\n");
+      printf("  (%.12g, %.12g, %.12g) %d.\n", p1[0], p1[1], p1[2],
+             pointmark(p1));
+      internalerror();
+    }
+    // Remember this handle in 'p1' to enhance the search speed.
+    setpoint2tet(p1, encode(*searchtet));
+    *tend = p2;
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// isedgeencroached()    Check whether or not a subsegment is encroached by  //
+//                       a given point.                                      //
+//                                                                           //
+// A segment with endpoints 'p1' and 'p2' is encroached by the point 'testpt'//
+// if it lies in the diametral sphere of this segment.  The degenerate case  //
+// that 'testpt' lies on the sphere can be treated as either be encroached   //
+// or not so. If you want to regard this case as be encroached, set the flag //
+// 'degflag' be TRUE.                                                        //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::
+isedgeencroached(point p1, point p2, point testpt, bool degflag)
+{
+  REAL dotproduct;
+
+  // Check if the segment is facing an angle larger than 90 degree?
+  dotproduct = (p1[0] - testpt[0]) * (p2[0] - testpt[0])
+             + (p1[1] - testpt[1]) * (p2[1] - testpt[1])
+             + (p1[2] - testpt[2]) * (p2[2] - testpt[2]);
+  if (dotproduct < 0) {
+    return true;
+  } else if (dotproduct == 0 && degflag) {
+    return true;
+  } else {
+    return false;
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// scoutrefpoint()    Search the reference point of a missing segment.       //
+//                                                                           //
+// A segment S is missing in current Delaunay tetrahedralization DT and will //
+// be split by inserting a point V in it.  The two end points of S are the   //
+// origin of 'searchtet' and 'tend'. And we know that S is crossing the face //
+// of 'searchtet' opposite to its origin (may be intersecting with the edge  //
+// from the destination to the apex of the 'searchtet').  The search of P is //
+// completed by walking through all faces of DT across by S.                 //
+//                                                                           //
+// The reference point P of S is an existing vertex of DT which is 'respon-  //
+// sible' for deciding where to insert V. P is chosen as follows:            //
+//    (1) P encroaches upon S; and                                           //
+//    (2) the circumradius of the smallest circumsphere of the triangle      //
+//        formed by the two endpoints of S and P is maximum over other       //
+//        encroaching points of S.                                           //
+// The reference point of S may not unique, choose arbitrary one if there're //
+// several points available.                                                 //
+//                                                                           //
+// Warning:  This routine is correct when the tetrahedralization is Delaunay //
+// and convex. Otherwise, the search loop may not terminate.                 //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+tetgenmesh::point tetgenmesh::scoutrefpoint(triface* searchtet, point tend)
+{
+  triface checkface;
+  point tstart, testpt, refpoint;
+  REAL cent[3], radius, largest;
+  REAL ahead;
+  bool ncollinear;
+  int sides;
+
+  if (b->verbose > 2) {
+    printf("  Scout the reference point of segment (%d, %d).\n",
+           pointmark(org(*searchtet)), pointmark(tend));
+  }
+
+  tstart = org(*searchtet);
+  refpoint = (point) NULL;
+  
+  // Check the three vertices of the crossing face.
+  testpt = apex(*searchtet);
+  if (isedgeencroached(tstart, tend, testpt, true)) {
+    ncollinear = circumsphere(tstart, tend, testpt, NULL, cent, &radius);
+    assert(ncollinear);
+    refpoint = testpt;
+    largest = radius;
+  }
+  testpt = dest(*searchtet);
+  if (isedgeencroached(tstart, tend, testpt, true)) {
+    ncollinear = circumsphere(tstart, tend, testpt, NULL, cent, &radius);
+    assert(ncollinear);
+    if (refpoint == (point) NULL) {
+      refpoint = testpt;
+      largest = radius;
+    } else {
+      if (radius > largest) {
+        refpoint = testpt;
+        largest = radius;
+      }
+    }
+  }
+  testpt = oppo(*searchtet);
+  if (isedgeencroached(tstart, tend, testpt, true)) {
+    ncollinear = circumsphere(tstart, tend, testpt, NULL, cent, &radius);
+    assert(ncollinear);
+    if (refpoint == (point) NULL) {
+      refpoint = testpt;
+      largest = radius;
+    } else {
+      if (radius > largest) {
+        refpoint = testpt;
+        largest = radius;
+      }
+    }
+  }
+  // Check the opposite vertex of the neighboring tet in case the segment
+  //   crosses the edge (leftpoint, rightpoint) of the crossing face.
+  sym(*searchtet, checkface);
+  if (checkface.tet != dummytet) {
+    testpt = oppo(checkface);
+    if (isedgeencroached(tstart, tend, testpt, true)) {
+      ncollinear = circumsphere(tstart, tend, testpt, NULL, cent, &radius);
+      assert(ncollinear);
+      if (refpoint == (point) NULL) {
+        refpoint = testpt;
+        largest = radius;
+      } else {
+        if (radius > largest) {
+          refpoint = testpt;
+          largest = radius;
+        }
+      }
+    }
+  }
+
+  // Walk through all crossing faces.
+  enextfnext(*searchtet, checkface);
+  sym(checkface, *searchtet);
+  while (true) {
+    // Check if we are reaching the boundary of the triangulation.
+    assert(searchtet->tet != dummytet);
+    // Search for an adjoining tetrahedron we can walk through.
+    searchtet->ver = 0;
+    // 'testpt' is the shared vertex for the following orientation tests.
+    testpt = oppo(*searchtet);
+    if (testpt == tend) {
+      // The searching is finished.
+      break; 
+    } else {
+      // 'testpt' may encroach the segment.
+      if ((testpt != tstart) && (testpt != refpoint)) {
+        if (isedgeencroached(tstart, tend, testpt, true)) {
+          ncollinear = circumsphere(tstart, tend, testpt, NULL, cent, &radius);
+          if (!ncollinear) {
+            // 'testpt' is collinear with the segment. It may happen when a
+            //   set of collinear and continuous segments is defined by two
+            //   extreme endpoints.  In this case, we should choose 'testpt'
+            //   as the splitting point immediately.  No new point should be
+            //   created.
+            refpoint = testpt;
+            break;
+          }
+          if (refpoint == (point) NULL) {
+            refpoint = testpt;
+            largest = radius;
+          } else {
+            if (radius > largest) {
+              refpoint = testpt;
+              largest = radius;
+            }
+          }
+        }
+      }
+    }
+    // Check three side-faces of 'searchtet' to find the one through
+    //   which we can walk next.
+    for (sides = 0; sides < 3; sides++) {
+      fnext(*searchtet, checkface);
+      ahead = orient3d(org(checkface), dest(checkface), testpt, tend);
+      if (ahead < 0.0) {
+        // We can walk through this face and continue the searching. 
+        sym(checkface, *searchtet);
+        break;
+      }
+      enextself(*searchtet);
+    }
+    assert (sides < 3);
+  }
+
+  assert(refpoint != (point) NULL);
+  return refpoint;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// getsegmentorigin()    Return the origin of the (unsplit) segment.         //
+//                                                                           //
+// After a segment (or a subsegment) is split. Two resulting subsegments are //
+// connecting each other through the pointers saved in their data fields.    //
+// With these pointers, the whole (unsplit) segment can be found. 'splitseg' //
+// may be a split subsegment.  Returns the origin of the unsplit segment.    //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+tetgenmesh::point tetgenmesh::getsegmentorigin(face* splitseg)
+{
+  face workseg;
+  point farorg;
+
+  farorg = sorg(*splitseg);
+  if ((pointtype(farorg) != ACUTEVERTEX) &&
+      (pointtype(farorg) != NONACUTEVERTEX)) {
+    workseg = *splitseg;
+    do {
+      senext2self(workseg);
+      spivotself(workseg);
+      if (workseg.sh != dummysh) {
+        workseg.shver = 0;  // It's a subsegment.
+        if (sdest(workseg) != farorg) {
+          sesymself(workseg);
+          assert(sdest(workseg) == farorg);
+        }
+        farorg = sorg(workseg);
+        if ((pointtype(farorg) == ACUTEVERTEX) ||
+            (pointtype(farorg) == NONACUTEVERTEX)) break;
+      }
+    } while (workseg.sh != dummysh);
+  }
+  assert((pointtype(farorg) == ACUTEVERTEX) ||
+         (pointtype(farorg) == NONACUTEVERTEX));
+  return farorg;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// getsplitpoint()    Get a point for splitting a segment.                   //
+//                                                                           //
+// 'splitseg' is the segment will be split. 'refpoint' is a reference point  //
+// for splitting this segment. Moreover, it should not collinear with the    //
+// splitting segment. (The collinear case will be detected by iscollinear()  //
+// before entering this routine.)  The calculation of the splitting point is //
+// governed by three rules introduced in my paper.                           //
+//                                                                           //
+// After the position is calculated, a new point is created at this location.//
+// The new point has one of the two pointtypes: FREESEGVERTEX indicating it  //
+// is an inserting vertex on segment, and NONACUTEVERTEX indicating it is an //
+// endpoint of a segment which original has type-3 now becomes type-2.       //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+tetgenmesh::point tetgenmesh::getsplitpoint(face* splitseg, point refpoint)
+{
+  point splitpoint;
+  point farorg, fardest;
+  point ei, ej, ek, c;
+  REAL v[3], r, split;
+  bool acuteorg, acutedest;
+  int stype, ptmark;
+  int i;   
+
+  // First determine the type of the segment (type-1, type-2, or type-3).
+  farorg = getsegmentorigin(splitseg);
+  acuteorg = (pointtype(farorg) == ACUTEVERTEX);
+  sesymself(*splitseg);
+  fardest = getsegmentorigin(splitseg);
+  acutedest = (pointtype(fardest) == ACUTEVERTEX);
+  sesymself(*splitseg);
+
+  if (acuteorg) {
+    if (acutedest) {
+      stype = 3;
+    } else {
+      stype = 2;
+      ek = farorg;
+    }
+  } else {
+    if (acutedest) {
+      stype = 2;
+      // Adjust splitseg, so that its origin is acute.
+      sesymself(*splitseg);
+      ek = fardest;
+    } else {
+      stype = 1;
+    }
+  }
+  ei = sorg(*splitseg);
+  ej = sdest(*splitseg);
+
+  if (b->verbose > 1) {
+    printf("  Splitting segment (%d, %d) type-%d with refpoint %d.\n",
+           pointmark(ei), pointmark(ej), stype, pointmark(refpoint));
+  }
+
+  if (stype == 1 || stype == 3) {
+    // Use rule-1.
+    REAL eij, eip, ejp;
+    eij = distance(ei, ej);
+    eip = distance(ei, refpoint);
+    ejp = distance(ej, refpoint);
+    if ((eip < ejp) && (eip < 0.5 * eij)) {
+      c = ei;
+      r = eip;
+    } else if ((eip > ejp) && (ejp < 0.5 * eij)) {
+      c = ej;
+      ej = ei;
+      r = ejp;
+    } else {
+      c = ei;
+      r = 0.5 * eij;
+    }
+    split = r / eij;
+    for (i = 0; i < 3; i++) {
+      v[i] = c[i] + split * (ej[i] - c[i]);
+    }
+  } else {
+    // Use rule-2 or rule-3.
+    REAL eki, ekj, ekp, evj, evp, eiv;
+    c = ek;
+    eki = distance(ek, ei);  // eki may equal zero.
+    ekj = distance(ek, ej);
+    ekp = distance(ek, refpoint);
+    // Calculate v (the going to split position between ei, ej).
+    r = ekp;
+    assert(eki < r && r < ekj);
+    split = r / ekj;
+    for (i = 0; i < 3; i++) {
+      v[i] = c[i] + split * (ej[i] - c[i]);
+    }
+    evj = ekj - r; // distance(v, ej);
+    evp = distance(v, refpoint);
+    if (evj < evp) {
+      // v is rejected, use rule-3.
+      eiv = distance(ei, v);
+      if (evp <= 0.5 * eiv) {
+        r = eki + eiv - evp;
+      } else {
+        r = eki + 0.5 * eiv;
+      }
+      assert(eki < r && r < ekj);
+      split = r / ekj;
+      for (i = 0; i < 3; i++) {
+        v[i] = c[i] + split * (ej[i] - c[i]);
+      }
+      if (b->verbose > 1) {
+        printf("    Using rule-3.\n");
+      }
+    } 
+  }
+
+  if (b->verbose > 1) {
+    if (stype == 2) {
+      printf("    Split = %.12g.\n", distance(ei, v) / distance(ei, ej));
+    } else {
+      printf("    Split = %.12g.\n", distance(c, v) / distance(c, ej));
+    }
+  }
+
+  // Allocate a point from points.
+  splitpoint = (point) points->alloc();
+  // Set its coordinates.
+  for (i = 0; i < 3; i++) {
+    splitpoint[i] = v[i];
+  }
+  // Interpolate its attributes.
+  for (i = 0; i < in->numberofpointattributes; i++) {
+    splitpoint[i + 3] = c[i + 3] + split * (ej[i + 3] - c[i + 3]);
+  }
+  // Remember the index (starts from 'in->firstnumber') of this vertex.
+  ptmark = (int) points->items - (in->firstnumber == 1 ? 0 : 1);
+  setpointmark(splitpoint, ptmark);
+  if (stype == 3) {
+    // Change a type-3 segment into two type-2 segments. 
+    setpointtype(splitpoint, NONACUTEVERTEX);
+  } else {
+    // Set it's type be FREESEGVERTEX.
+    setpointtype(splitpoint, FREESEGVERTEX);
+  }
+  // Init this field.
+  setpoint2tet(splitpoint, NULL);
+
+  return splitpoint;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// delaunizesegments()    Split segments repeatedly until they appear in a   //
+//                        Delaunay tetrahedralization together.              //
+//                                                                           //
+// Given a PLC X, which has a set V of vertices and a set of segments. Start //
+// from a Delaunay tetrahedralization D of V, this routine recovers segments //
+// of X in D by incrementally inserting points on missing segments, updating //
+// D with the newly inserted points into D', which remains to be a Delaunay  //
+// tetrahedralization and respects the segments of X. Hence, each segment of //
+// X appears as a union of edges in D'.                                      //
+//                                                                           //
+// This routine dynamically maintains two meshes, one is DT, another is the  //
+// surface mesh F of X.  DT and F have exactly the same vertices.  They are  //
+// updated simultaneously with the newly inserted points.                    //
+//                                                                           //
+// Missing segments are found by looping the set S of segments, checking the //
+// existence of each segment in DT.  Once a segment is found missing in DT,  //
+// it is split into two subsegments by inserting a point into both DT and F, //
+// and S is updated accordingly.  However, the inserted point may cause some //
+// other existing segments be non-Delaunay,  hence are missing from the DT.  //
+// In order to force all segments to appear in DT, we have to loop S again   //
+// after some segments are split. (A little ugly method)  Use a handle to    //
+// remember the last segment be split in one loop, hence all segments after  //
+// it are existing and need not be checked.                                  //
+//                                                                           //
+// In priciple, a segment on the convex hull should exist in DT. However, if //
+// there are four coplanar points on the convex hull, and the DT only can    //
+// contain one diagonal edge which is unfortunately not the segment, then it //
+// is missing. During the recovery of the segment, it is possible that the   //
+// calculated inserting point for recovering this convex hull segment is not //
+// exact enough and lies (slightly) outside the DT. In order to insert the   //
+// point, we enlarge the convex hull of the DT, so it can contain the point  //
+// and remains convex.  'inserthullsite()' is called under this case.        //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::delaunizesegments()
+{
+  queue *flipqueue;
+  triface searchtet;
+  face segloop, lastsplit;
+  face splitsh;
+  point p1, p2;
+  point tend, checkpoint;
+  point refpoint, splitpoint; 
+  enum finddirectionresult collinear;
+  enum insertsiteresult success;
+  bool finish;
+
+  if (!b->quiet) {
+    printf("Delaunizing segments.\n");
+  }
+
+  // Mark segment vertices (acute or not) for determining segment types.
+  markacutevertices(60.0);
+  // Construct a map from points to tetrahedra for speeding point location.
+  makepoint2tetmap();
+  // Initialize a queue for returning non-Delaunay faces and edges.
+  flipqueue = new queue(sizeof(badface));
+  // 'lastsplit' is the last segment be split in one loop, all segments
+  //   after it are existing. At first, set it be NULL;
+  lastsplit.sh = (shellface *) NULL;
+
+  finish = false;
+  while (!finish && (steinerleft != 0)) {
+    subsegs->traversalinit();
+    segloop.sh = shellfacetraverse(subsegs);
+    while ((segloop.sh != (shellface *) NULL) && (steinerleft != 0)) {
+      // Search the segment in DT.
+      p1 = sorg(segloop);
+      p2 = sdest(segloop);
+      if (b->verbose > 2) {
+        printf("  Checking segment (%d, %d).\n", pointmark(p1), pointmark(p2));
+      }
+      getsearchtet(p1, p2, &searchtet, &tend);
+      collinear = finddirection(&searchtet, tend);
+      if (collinear == LEFTCOLLINEAR) {
+        checkpoint = apex(searchtet);
+      } else if (collinear == RIGHTCOLLINEAR) {
+        checkpoint = dest(searchtet);
+      } else if (collinear == TOPCOLLINEAR) {
+        checkpoint = oppo(searchtet);
+      } else {
+        assert(collinear == ACROSSFACE || collinear == ACROSSEDGE);
+        checkpoint = (point) NULL;
+      }
+      if (checkpoint != tend) {
+        // The segment is missing.
+        if (checkpoint != (point) NULL) {
+          splitpoint = checkpoint;
+        } else {
+          refpoint = scoutrefpoint(&searchtet, tend);
+          if (iscollinear(p1, p2, refpoint, b->epsilon)) {
+            splitpoint = refpoint;
+          } else {
+            splitpoint = getsplitpoint(&segloop, refpoint);
+            // Insert 'splitpoint' into DT.
+            success = insertsite(splitpoint, &searchtet, false, flipqueue);
+            if (success == OUTSIDEPOINT) {
+              // A convex hull edge is mssing, and the inserting point lies
+              //   (slightly) outside the convex hull due to the significant
+              //   digits lost in the calculation. Enlarge the convex hull.
+              inserthullsite(splitpoint, &searchtet, flipqueue, NULL, NULL);
+            }
+            if (steinerleft > 0) steinerleft--;
+            // Remember a handle in 'splitpoint' to enhance the speed of
+            //   consequent point location.
+            setpoint2tet(splitpoint, encode(searchtet));
+            // Maintain Delaunayness in DT.
+            flip(flipqueue, NULL);
+          }
+        }
+        // Insert 'splitpoint' into F.
+        spivot(segloop, splitsh);
+        splitsubedge(splitpoint, &splitsh, flipqueue);
+        flipsub(flipqueue);
+        // Remember 'segloop'.
+        lastsplit = segloop;
+      } else {
+        // The segment exists.
+        if (segloop.sh == lastsplit.sh) {
+          finish = true;
+          break;
+        }
+      }
+      segloop.sh = shellfacetraverse(subsegs);
+    }
+    if (lastsplit.sh == (shellface *) NULL) {
+      // No missing segment!
+      finish = true;
+    }
+  }
+
+  delete flipqueue;
+}
+
+//
+// End of segments recovery routines
+//
+
+//
+// Begin of constrained Delaunay triangulation routines
+//
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// insertsubface()    Insert a subface into the Delaunay tetrahedralization. //
+//                                                                           //
+// Search the subface in current Delaunay tetrahedralization. Return TRUE if //
+// the subface exists, i.e., it appears as a face of the DT and is inserted. //
+// Otherwise, return FALSE indicating it is a missing face.                  //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::insertsubface(face* insertsh, triface* searchtet)
+{
+  triface spintet, symtet;
+  face testsh, testseg;
+  face spinsh, casin, casout;
+  point tapex, checkpoint;
+  enum finddirectionresult collinear;
+  int hitbdry;
+
+  // Search one edge of 'insertsh'.
+  getsearchtet(sorg(*insertsh), sdest(*insertsh), searchtet, &checkpoint);
+  collinear = finddirection(searchtet, checkpoint);
+  if (collinear == LEFTCOLLINEAR) {
+    enext2self(*searchtet);
+    esymself(*searchtet);
+  } else if (collinear == TOPCOLLINEAR) {
+    fnextself(*searchtet);
+    enext2self(*searchtet);
+    esymself(*searchtet);
+  }
+  if (dest(*searchtet) != checkpoint) {
+    // The edge is missing => subface is missing.
+    return false;
+  }
+
+  // Spin around the edge (torg, tdest), look for a face containing tapex.
+  tapex = sapex(*insertsh);
+  spintet = *searchtet;
+  hitbdry = 0;
+  do {
+    if (apex(spintet) == tapex) {
+      // The subface is exist in DT. We will insert this subface. Before
+      //   insertion, make sure there is no subface at this position.
+      tspivot(spintet, testsh);
+      if (testsh.sh == dummysh) {
+        adjustedgering(spintet, CCW);
+        findedge(insertsh, org(spintet), dest(spintet));
+        tsbond(spintet, *insertsh);
+        sym(spintet, symtet); // 'symtet' maybe outside, use it anyway.
+        sesymself(*insertsh);
+        tsbond(symtet, *insertsh);
+      } else {
+        // There already exists one subface. They're Duplicated.
+        printf("Warning:  Two subfaces are found duplicated at ");
+        printf("(%d, %d, %d)\n", pointmark(sorg(testsh)),
+               pointmark(sdest(testsh)), pointmark(sapex(testsh)));
+        printf("  The one of facet #%d is ignored.\n", shellmark(*insertsh));
+        // printf("  Hint: -d switch can find all duplicated facets.\n");
+      }
+      return true;
+    }
+    if (!fnextself(spintet)) {
+      hitbdry ++;
+      if (hitbdry < 2) {
+        esym(*searchtet, spintet);
+        if (!fnextself(spintet)) {
+          hitbdry ++;
+        }
+      }
+    }
+  } while (hitbdry < 2 && apex(spintet) != apex(*searchtet));
+
+  // The face is missing.
+  return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// tritritest()    Test if two triangles are intersecting in their interior. //
+//                                                                           //
+// One triangles is represented by 'checktet', the other is given by three   //
+// corners 'p1', 'p2' and 'p3'. This routine calls triangle_triangle_inter() //
+// to detect whether or not these two triangles are exactly intersecting in  //
+// their interior (excluding the cases share a vertex, share an edge, or are //
+// coincide).                                                                //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::tritritest(triface* checktet, point p1, point p2, point p3)
+{
+  point forg, fdest, fapex;
+  enum intersectresult intersect;
+
+  forg = org(*checktet);
+  fdest = dest(*checktet);
+  fapex = apex(*checktet);
+
+#ifdef SELF_CHECK
+  REAL ax, ay, az, bx, by, bz;
+  REAL n[3];
+  // face (torg, tdest, tapex) should not be degenerate. However p1, p2,
+  //   and p3 may be collinear. Check it.
+  ax = forg[0] - fdest[0];
+  ay = forg[1] - fdest[1];
+  az = forg[2] - fdest[2];
+  bx = forg[0] - fapex[0];
+  by = forg[1] - fapex[1];
+  bz = forg[2] - fapex[2];
+  n[0] = ay * bz - by * az;
+  n[1] = az * bx - bz * ax;
+  n[2] = ax * by - bx * ay;
+  assert(fabs(n[0]) + fabs(n[1]) + fabs(n[2]) > 0.0);
+  // The components of n should not smaller than the machine epsilon.
+
+  ax = p1[0] - p2[0];
+  ay = p1[1] - p2[1];
+  az = p1[2] - p2[2];
+  bx = p1[0] - p3[0];
+  by = p1[1] - p3[1];
+  bz = p1[2] - p3[2];
+  n[0] = ay * bz - by * az;
+  n[1] = az * bx - bz * ax;
+  n[2] = ax * by - bx * ay;
+  assert(fabs(n[0]) + fabs(n[1]) + fabs(n[2]) > 0.0);
+  // The components of n should not smaller than the machine epsilon.
+#endif
+
+  intersect = triangle_triangle_inter(forg, fdest, fapex, p1, p2, p3);
+  return intersect == INTERSECT;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// initializecavity()    Create the initial fronts.                          //
+//                                                                           //
+// 'floorlist' is a list of coplanar subfaces, they are oriented in the same //
+// direction pointing to the ceiling.  'ceilinglist' is a list of faces of   //
+// tetrahedra which are crossing the cavity, they form the rest part of the  //
+// boundary of the cavity. 'frontlink' is used to return the list of fronts, //
+// it is empty on input.                                                     //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::
+initializecavity(list* floorlist, list* ceillist, list* floorptlist,
+                 list* ceilptlist, link* frontlink, link* ptlink)
+{
+  triface neightet, casingtet;
+  triface faketet;
+  face worksh;
+  int i;
+
+  // First add all faces in 'floorlist' into 'frontlink'.
+  for (i = 0; i < floorlist->len(); i++) {
+    worksh = * (face *)(* floorlist)[i];
+    // Current side of 'worksh' should be empty.
+    stpivot(worksh, neightet);
+    assert(neightet.tet == dummytet);
+    // Check another side, if there is no tetrahedron, create a 'fake'
+    //   tetrahedron in order to hold this side. It will be removed
+    //   during filling the cavity.
+    sesymself(worksh);
+    stpivot(worksh, casingtet);
+    if (casingtet.tet == dummytet) {
+      maketetrahedron(&faketet);
+      setorg(faketet, sorg(worksh));
+      setdest(faketet, sdest(worksh));
+      setapex(faketet, sapex(worksh));
+      setoppo(faketet, (point) NULL); // Indicates it is 'fake'.
+      tsbond(faketet, worksh);
+      frontlink->add(&faketet);
+    } else {
+      frontlink->add(&casingtet);
+    }
+  }
+  // Second add all casing faces in 'ceilinglist' into 'frontlink'.
+  for (i = 0; i < ceillist->len(); i++) {
+    neightet = * (triface *) (* ceillist)[i];
+    // The ceil is a face of cavity tetrahedron (going to be deleted).
+    assert(infected(neightet));
+    sym(neightet, casingtet);
+    if (casingtet.tet == dummytet) {
+      // This side is on the hull. Create a 'fake' tetrahedron in order to
+      //   hold this side. It will be removed during filling the cavity.
+      tspivot(neightet, worksh);
+      maketetrahedron(&faketet);
+      setorg(faketet, org(neightet));
+      setdest(faketet, dest(neightet));
+      setapex(faketet, apex(neightet));
+      setoppo(faketet, (point) NULL); // Indicates it is 'fake'.
+      if (worksh.sh != dummysh) {
+        sesymself(worksh);
+        tsbond(faketet, worksh);
+      }
+      frontlink->add(&faketet);
+    } else {
+      frontlink->add(&casingtet);
+    }
+  }
+  // Put points in 'equatptlist' and 'ceilptlist' into 'ptlink'.
+  for (i = 0; i < floorptlist->len(); i++) {
+    ptlink->add((point *)(* floorptlist)[i]);
+  }
+  for (i = 0; i < ceilptlist->len(); i++) {
+    ptlink->add((point *)(* ceilptlist)[i]);
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// reducecavity()    Reduce the cavity by chopping off tetrahedra formed by  //
+//                   faces in 'frontlink' without creating new edges.        //
+//                                                                           //
+// When a face of cavity has three neighbors which are sharing a same vertex,//
+// form a tetrahedron from the face and the vertex, consequently, four faces //
+// of the cavity are removed.  If only two of its three neighbors share a    //
+// common vertex,  it only can form a tetrahedron when no vertex of the      //
+// cavity lies inside the tetrahedorn, consequently, three faces are removed //
+// from the cavity and a face(at the open side) becomes a face of the cavity.//
+//                                                                           //
+// Not every face of the cavity can be removed by this way.  This routine    //
+// returns when there is no face can be removed.                             //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::reducecavity(link* frontlink, link* ptlink, queue* flipqueue)
+{
+  triface front, *neigh[3], *checkface;
+  triface newtet, newface;
+  face checksh;
+  point *ploop;
+  point forg, fdest;
+  point workpt[3], shareoppo;
+  REAL sign;
+  bool isneighbor, isconvex;
+  int loopcount, share;
+  int i, j, k;  
+
+  if (b->verbose > 2) {
+    printf("    Reducecavity: %d faces.\n", (int) frontlink->len());
+  }
+
+  loopcount = 0;
+  while (loopcount < frontlink->len()) {
+    // Get and remove a front face from 'fronlink'.
+    front = * (triface *) frontlink->del(1);
+    // Make the front point to insde the cavity.
+    adjustedgering(front, CW);
+    if (b->verbose > 2) {
+      printf("    Get front (%d, %d, %d).\n", pointmark(org(front)),
+             pointmark(dest(front)), pointmark(apex(front)));
+    }
+    // Find the three neighbors of 'front' in 'frontlink'.  They must exist,
+    //   because the cavity is closed.
+    for (i = 0; i < 3; i++) {
+      forg = org(front);
+      fdest = dest(front);
+      isneighbor = false;
+      for (j = 0; j < frontlink->len() && !isneighbor; j++) {
+        checkface = (triface *) frontlink->getnitem(j + 1);
+        for (k = 0; k < 3; k++) {
+          workpt[0] = org(*checkface);
+          workpt[1] = dest(*checkface);
+          if (workpt[0] == forg) {
+            if (workpt[1] == fdest) isneighbor = true;
+          } else if (workpt[0] == fdest) {
+            if (workpt[1] == forg) isneighbor = true;
+          }
+          if (isneighbor) {
+            neigh[i] = checkface;
+            break;
+          }
+          enextself(*checkface);
+        }
+      }
+      assert(isneighbor);
+      enextself(front);
+    }
+    
+    // Find the number of common apexes.
+    for (i = 0; i < 3; i++) {
+      workpt[i] = apex(*neigh[i]);
+    }
+    if (workpt[0] == workpt[1]) {
+      shareoppo = workpt[0];
+      if (workpt[1] == workpt[2]) {
+        share = 3;
+      } else {
+        share = 2;
+      }
+    } else if (workpt[0] == workpt[2]) {
+      shareoppo = workpt[0];
+      share = 2;
+    } else {
+      if (workpt[1] == workpt[2]) {
+        shareoppo = workpt[1];
+        share = 2;
+      } else {
+        share = 1;
+      }
+    }
+
+    if (share == 2) {
+      // It is possible that the open side is also a cavity face, but not
+      //   pop up because there are more than two cavity faces sharing the
+      //   edge. Check is there a cavity face having this edge and having
+      //   its apex be 'shareoppo'.
+      for (i = 0; i < 3; i++) {
+        if (workpt[i] != shareoppo) {
+          // 'neigh[i]' is the open side. Get the edge.
+          forg = org(*neigh[i]);
+          fdest = dest(*neigh[i]);
+          break;
+        }
+      }
+      assert(i < 3);
+      // Search faces containing edge (forg, fdest) in 'frontlist'. If
+      //   the found face containing 'shareoppo', stop;
+      for (j = 0; j < frontlink->len() && share != 3; j++) {
+        checkface = (triface *) frontlink->getnitem(j + 1);
+        // Skip if it is one of the neighbors. 
+        if ((checkface == neigh[0]) || (checkface == neigh[1]) ||
+            (checkface == neigh[2])) continue; 
+        isneighbor = false;
+        for (k = 0; k < 3; k++) {
+          workpt[0] = org(*checkface);
+          workpt[1] = dest(*checkface);
+          if (workpt[0] == forg) {
+            if (workpt[1] == fdest) isneighbor = true;
+          } else if (workpt[0] == fdest) {
+            if (workpt[1] == forg) isneighbor = true;
+          }
+          if (isneighbor) {
+            if (apex(*checkface) == shareoppo) {
+              // Find! Change the old neighbor at this side be this one. 
+              neigh[i] = checkface;
+              share = 3;
+              break;
+            }
+          }
+          enextself(*checkface);
+        }
+      }
+    }
+
+    isconvex = true;
+    if (share == 2 || share == 3) {
+      // It is possible to reduce the cavity by constructing a tetrahedron
+      //   from the face 'front' and 'shareoppo'. However, we have to make
+      //   sure that this tetrahedron is valid, i.e., shareoppo should lie
+      //   above front. 
+      workpt[0] = org(front);
+      workpt[1] = dest(front);
+      workpt[2] = apex(front);
+      sign = orient3d(workpt[0], workpt[1], workpt[2], shareoppo);
+      if (sign > 0.0) {
+        // It is not a valid tetrahedron, skip to create it.
+        isconvex = false;
+      } else if (sign == 0.0) {
+        // These four points are coplanar. If there are only three faces
+        //   left, we should stop here. Create a degenerate tetrahedron
+        //   on these four faces and return. It will be repaired later.
+        if (frontlink->len() > 3) {
+          isconvex = false;
+        }
+      }
+    }
+    if (share == 2 && isconvex) {
+      // Check if we can reduce the tetrahedron formed by the front and the
+      //   shareoppo. The condition is no vertex is inside the tetrahedron.
+      for (i = 0; i < ptlink->len() && isconvex; i++) {
+        ploop = (point *) ptlink->getnitem(i + 1);
+        if (*ploop == workpt[0] || *ploop == workpt[1] || *ploop == workpt[2]
+            || *ploop == shareoppo) continue;
+        sign = orient3d(workpt[0], workpt[1], workpt[2], *ploop);
+        isconvex = sign > 0.0;
+        if (isconvex) continue;
+        sign = orient3d(workpt[1], workpt[0], shareoppo, *ploop);
+        isconvex = sign > 0.0;
+        if (isconvex) continue;
+        sign = orient3d(workpt[2], workpt[1], shareoppo, *ploop);
+        isconvex = sign > 0.0;
+        if (isconvex) continue;
+        sign = orient3d(workpt[0], workpt[2], shareoppo, *ploop);
+        isconvex = sign > 0.0;
+      }
+    } 
+
+    if (share == 1 || !isconvex) {
+      // Put 'front' back into 'frontlink'.
+      frontlink->add(&front);
+      loopcount++; // Increase the loop counter.
+      continue;
+    } else {
+      // Find a reducable tetrahedron. Reset the loop counter.
+      loopcount = 0;
+    }
+
+    if (b->verbose > 2) {
+      for (i = 0; i < 3; i++) {
+        if (apex(*neigh[i]) == shareoppo) {
+          printf("      (%d, %d, %d).\n", pointmark(org(*neigh[i])),
+                 pointmark(dest(*neigh[i])), pointmark(apex(*neigh[i])));
+	}
+      }
+    }
+
+    // The front will be finished by two or three faces.
+    maketetrahedron(&newtet);
+    setorg(newtet, org(front));
+    setdest(newtet, dest(front));
+    setapex(newtet, apex(front));
+    setoppo(newtet, shareoppo);
+    // 'front' may be a 'fake' tet.
+    tspivot(front, checksh);
+    if (oppo(front) == (point) NULL) {
+      // Dealloc the 'fake' tet.
+      tetrahedrondealloc(front.tet);
+      // This side (newtet) is a boundary face, let 'dummytet' bond to it.
+      //   Otherwise, 'dummytet' may point to a dead tetrahedron after the
+      //   old cavity tets are removed.
+      dummytet[0] = encode(newtet);
+    } else {
+      // Bond two tetrahedra, also dissolve the old bond at 'front'.
+      bond(newtet, front);
+      // 'front' becomes an interior face, add it to 'flipqueue'.
+      if (flipqueue != (queue *) NULL) {
+        enqueueflipface(front, flipqueue);
+      }
+    }
+    if (checksh.sh != dummysh) {
+      if (oppo(front) == (point) NULL) {
+        stdissolve(checksh);
+      }
+      sesymself(checksh);
+      tsbond(newtet, checksh);
+    }
+    // Bond the neighbor faces to 'newtet'.
+    for (i = 0; i < 3; i++) {
+      fnext(newtet, newface);
+      if (apex(*neigh[i]) == shareoppo) {
+        // This side is finished. 'neigh[i]' may be a 'fake' tet.
+        tspivot(*neigh[i], checksh);
+        if (oppo(*neigh[i]) == (point) NULL) {
+          // Dealloc the 'fake' tet.
+          tetrahedrondealloc(neigh[i]->tet);
+          // This side (newface) is a boundary face, let 'dummytet' bond to
+          //   it. Otherwise, 'dummytet' may point to a dead tetrahedron
+          //   after the old cavity tets are removed.
+          dummytet[0] = encode(newface);
+        } else {
+          // Bond two tetrahedra, also dissolve the old bond at 'neigh[i]'.
+          bond(newface, *neigh[i]);
+          // 'neigh[i]' becomes an interior face, add it to 'flipqueue'.
+          if (flipqueue != (queue *) NULL) {
+            enqueueflipface(*neigh[i], flipqueue);
+          }
+        }
+        if (checksh.sh != dummysh) {
+          if (oppo(*neigh[i]) == (point) NULL) {
+            stdissolve(checksh);
+          }
+          sesymself(checksh);
+          tsbond(newface, checksh);
+        }
+        // Remove it from the link.
+        frontlink->del(neigh[i]);
+      } else {
+        // This side is unfinished. Add 'newface' into 'frontlink'.
+        frontlink->add(&newface);
+      }
+      // Get the face in 'newtet' corresponding to 'neigh[i]'.
+      enextself(newtet);
+    }
+  } // End of while loop.
+
+  return frontlink->len() == 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// reducecavity1()    Reduce the cavity by forming a new tetrahedron from a  //
+//                    cavity face to a visible point. As a result, create    //
+//                    one or more new edges inside the cavity.               //
+//                                                                           //
+// We know that the cavity is not simply reducable, we have to create new    //
+// faces inside the cavity in order to reduce the cavity. This routine finds //
+// the most suitable edge we can create in the cavity.                       //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::reducecavity1(link* frontlink, queue* flipqueue)
+{
+  list *edgelist;
+  triface front, *neigh[3], *checkface;
+  triface newtet, newface;
+  face checksh;
+  point forg, fdest;
+  point workpt[3], *edgeends;
+  REAL sign;
+  bool isneighbor, isvalid;
+  bool isexist, isfind;
+  bool isreducable;
+  unsigned long count;
+  int loopcount;
+  int i, j, k;  
+
+  if (b->verbose > 2) {
+    printf("    Reducecavity1: %d faces.\n", (int) frontlink->len());
+  }
+
+  // Initialize 'edgelist'. Each edge has two endpoints and 1 counter.
+  edgelist = new list(sizeof(point) * 3, NULL);
+
+  loopcount = 0;
+  while (loopcount < frontlink->len()) {
+    front = * (triface *) frontlink->getnitem(loopcount + 1);
+    // Make the front point to the inside of the cavity.
+    adjustedgering(front, CW);
+    if (b->verbose > 2) {
+      printf("    Get front (%d, %d, %d).\n", pointmark(org(front)),
+             pointmark(dest(front)), pointmark(apex(front)));
+    }
+    // Find the three neighbors of 'front' in 'frontlink'.
+    for (i = 0; i < 3; i++) {
+      forg = org(front);
+      fdest = dest(front);
+      isneighbor = false;
+      for (j = 0; j < frontlink->len() && !isneighbor; j++) {
+        if (j == loopcount) continue;
+        checkface = (triface *) frontlink->getnitem(j + 1);
+        for (k = 0; k < 3; k++) {
+          workpt[0] = org(*checkface);
+          workpt[1] = dest(*checkface);
+          if (workpt[0] == forg) {
+            if (workpt[1] == fdest) isneighbor = true;
+          } else if (workpt[0] == fdest) {
+            if (workpt[1] == forg) isneighbor = true;
+          }
+          if (isneighbor) {
+            neigh[i] = checkface;
+            break;
+          }
+          enextself(*checkface);
+        }
+      }
+      assert(isneighbor);
+      enextself(front);
+    }
+    // Check the three edges which are possibly created in cavity.
+    for (i = 0; i < 3; i++) {
+      // 'forg', 'fdest' is the edge.
+      forg = apex(front);
+      fdest = apex(*neigh[i]);
+      // Two face vertices.
+      workpt[0] = org(front);
+      workpt[1] = dest(front);
+      // Only do check if these four points form a positive volume. Allow
+      //   the case that they are coplanar.
+      sign = orient3d(workpt[0], workpt[1], forg, fdest);
+      if (sign <= 0.0) {
+        // Check the face (forg, fdest, workpt[i]) is valid or not.
+        isvalid = true;
+        for (j = 0; j < 2 && isvalid; j++) {
+          // Skip the following tests if the face is (nearly) degenerate.
+          if (iscollinear(forg, fdest, workpt[j], b->epsilon)) {
+            isvalid = false;
+          }
+          for (k = 0; k < frontlink->len() && isvalid; k++) {
+            if (k == loopcount) continue;
+            checkface = (triface *) frontlink->getnitem(k + 1);
+            if (checkface == neigh[i]) continue;
+            isvalid = !tritritest(checkface, forg, fdest, workpt[j]);
+          }
+        }
+        if (isvalid) {
+          // This edge can be created. Check in 'edgelist', if it is not
+          //   in there, add it, if it exists, increase its counter.
+          isexist = false;
+          for (j = 0; j < edgelist->len() && !isexist; j++) {
+            edgeends = (point *)(* edgelist)[j];
+            if (edgeends[0] == forg) {
+              if (edgeends[1] == fdest) isexist = true;
+            } else if (edgeends[0] == fdest) {
+              if (edgeends[1] == forg) isexist = true;
+            }
+          }
+          if (!isexist) {
+            // Not exist, add it into 'edgelist'.
+            if (b->verbose > 2) {
+              printf("      Add edge (%d, %d).\n", pointmark(forg),
+                     pointmark(fdest));
+            }
+            edgeends = (point *) edgelist->append(NULL);
+            edgeends[0] = forg;
+            edgeends[1] = fdest;
+            edgeends[2] = (point) 1;
+          } else {
+            // Exist, only increase its counter.
+            if (b->verbose > 2) {
+              printf("      Increase edge (%d, %d)'s counter.\n",
+                     pointmark(forg), pointmark(fdest));
+            }
+            count = (unsigned long)(edgeends[2]);
+            edgeends[2] = (point) (++count);
+          }
+        }
+      }
+      enextself(front);
+    }
+    loopcount++;
+  }
+
+  isreducable = edgelist->len() > 0;
+
+  if (edgelist->len() > 0) {
+    // Get the edge which has the largest counter.
+    k = 0;
+    for (i = 0; i < edgelist->len(); i++) {
+      edgeends = (point *)(* edgelist)[i];
+      count = (unsigned long)(edgeends[2]);
+      if (k < (int) count) {
+        k = (int) count;
+        j = i;
+      }
+    }
+    // Get the edge we want to create.
+    edgeends = (point *)(* edgelist)[j];
+    if (b->verbose > 2) {
+      printf("    Create new edge (%d, %d).\n", pointmark(edgeends[0]),
+             pointmark(edgeends[1]));
+    }
+    // Find two adjacent faces in 'frontlink' conatining this edge's ends.
+    neigh[0] = neigh[1] = (triface *) NULL;
+    isfind = false;
+    for (i = 0; i < frontlink->len() && !isfind; i++) {
+      checkface = (triface *) frontlink->getnitem(i + 1);
+      for (j = 0; j < 3; j++) {
+        if (apex(*checkface) == edgeends[0]) {
+          neigh[0] = checkface;
+          break;
+        }
+        enextself(*checkface);
+      }
+      if (neigh[0] != (triface *) NULL) {
+        forg = org(*neigh[0]);
+        fdest = dest(*neigh[0]);
+        for (k = 0; k < frontlink->len(); k++) {
+          if (k == i) continue;
+          checkface = (triface *) frontlink->getnitem(k + 1);
+          isneighbor = false;
+          for (j = 0; j < 3; j++) {
+            workpt[0] = org(*checkface);
+            workpt[1] = dest(*checkface);
+            if (workpt[0] == forg) {
+              if (workpt[1] == fdest) isneighbor = true;
+            } else if (workpt[0] == fdest) {
+              if (workpt[1] == forg) isneighbor = true;
+            }
+            if (isneighbor) break;
+            enextself(*checkface);
+          }
+          if (isneighbor) {
+            if (apex(*checkface) == edgeends[1]) {
+              neigh[1] = checkface;
+              isfind = true;
+              break;
+            }
+          }
+        }
+        if (neigh[1] == (triface *) NULL) {
+          neigh[0] = (triface *) NULL;
+        }
+      }
+    }
+    assert(isfind);
+    if (b->verbose > 2) {
+      for (i = 0; i < 2; i++) {
+        printf("    Finish face (%d, %d, %d).\n", pointmark(org(*neigh[i])),
+               pointmark(dest(*neigh[i])), pointmark(apex(*neigh[i])));
+      }
+    }
+    // Make the front point inside the cavity.
+    front = *neigh[0];
+    adjustedgering(front, CW);
+    maketetrahedron(&newtet);
+    setorg(newtet, org(front));
+    setdest(newtet, dest(front));
+    setapex(newtet, apex(front));
+    setoppo(newtet, edgeends[1]);
+    // 'front' may be a 'fake' tet.
+    tspivot(front, checksh);
+    if (oppo(front) == (point) NULL) {
+      // Dealloc the 'fake' tet.
+      tetrahedrondealloc(front.tet);
+      // This side (newtet) is a boundary face, let 'dummytet' bond to it.
+      //   Otherwise, 'dummytet' may point to a dead tetrahedron after the
+      //   old cavity tets are removed.
+      dummytet[0] = encode(newtet);
+    } else {
+      // Bond two tetrahedra, also dissolve the old bond at 'front'.
+      bond(newtet, front);
+      // 'front' becomes an interior face, add it to 'flipqueue'.
+      if (flipqueue != (queue *) NULL) {
+        enqueueflipface(front, flipqueue);
+      }
+    }
+    if (checksh.sh != dummysh) {
+      if (oppo(front) == (point) NULL) {
+        stdissolve(checksh);
+      }
+      sesymself(checksh);
+      tsbond(newtet, checksh);
+    }
+    fnext(newtet, newface);
+    // 'neigh[1]' may be a 'fake' tet.
+    tspivot(*neigh[1], checksh);
+    if (oppo(*neigh[1]) == (point) NULL) {
+      // Dealloc the 'fake' tet.
+      tetrahedrondealloc(neigh[1]->tet);
+      // This side (newface) is a boundary face, let 'dummytet' bond to it.
+      //   Otherwise, 'dummytet' may point to a dead tetrahedron after the
+      //   old cavity tets are removed.
+      dummytet[0] = encode(newface);
+    } else {
+      // Bond two tetrahedra, also dissolve the old bond at 'newface'.
+      bond(*neigh[1], newface);
+      // 'neigh[0]' becomes an interior face, add it to 'flipqueue'.
+      if (flipqueue != (queue *) NULL) {
+        enqueueflipface(*neigh[1], flipqueue);
+      }
+    }
+    if (checksh.sh != dummysh) {
+      if (oppo(*neigh[1]) == (point) NULL) {
+        stdissolve(checksh);
+      }
+      sesymself(checksh);
+      tsbond(newface, checksh);
+    }
+    // Remove 'neigh[0]', 'neigh[1]' from 'frontlink'.
+    frontlink->del(neigh[0]);
+    frontlink->del(neigh[1]);
+    // Add two new faces into 'frontlink'.
+    enextfnext(newtet, newface);
+    frontlink->add(&newface);
+    enext2fnext(newtet, newface);
+    frontlink->add(&newface);
+  }
+
+  delete edgelist;
+  return isreducable;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// triangulatecavity()    Triangulate a cavity by filling a set of Delaunay  //
+//                        tetrahedra inside.                                 //
+//                                                                           //
+// The boundary of the cavity is consisted of two list of triangular faces.  //
+// 'floorlist' is a list of coplanar subfaces.  All subfaces are oriented in //
+// the same direction so that the cavity is in the above part of each face.  //
+// 'ceilinglist' is a list of faces of tetrahedra which are crossing the     //
+// cavity, they form the rest part of the boundary of the cavity.            //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::
+triangulatecavity(list* floorlist, list* ceillist, list* floorptlist,
+                  list* ceilptlist)
+{
+  link *frontlink;
+  link *ptlink;
+  queue *flipqueue;
+
+  if (b->verbose > 1) {
+    printf("    Triangulate cavity %d floors, %d ceilings.\n",
+           floorlist->len(), ceillist->len());
+  }
+  
+  // Initialize flipqueue;
+  flipqueue = new queue(sizeof(badface));
+  // Initialize 'frontlink', 'ptlink'.
+  frontlink = new link(sizeof(triface), NULL, 256);
+  ptlink = new link(sizeof(point), NULL, 256);
+  
+  initializecavity(floorlist, ceillist, floorptlist, ceilptlist, frontlink,
+                   ptlink);
+
+  // Loop until 'frontlink' is empty.
+  while (frontlink->len() > 0) {
+    // Shrink the cavity by finishing easy connected fronts.
+    if (!reducecavity(frontlink, ptlink, flipqueue)) {
+      // Create new fronts inside the cavity (may insert point(s)).
+      if (!reducecavity1(frontlink, flipqueue)) {
+        // reducecavity2(frontlink, flipqueue);
+        assert(0);
+      }
+    }
+  }
+  // Some inner faces may need be flipped.
+  flip(flipqueue, NULL);
+
+  delete frontlink;
+  delete ptlink;
+  delete flipqueue;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// formmissingregion()    Form the missing region from a given missing face. //
+//                                                                           //
+// 'missingsh' is a missing subface.  Start from it, we can find the missing //
+// adjoinging subfaces. More detail, remember that all missing subfaces have //
+// been infected, and missing region of a facet is bounded by facet segments.//
+// All missing subfaces of the region can be found by checking the neighbors //
+// of 'missingsh', and the neighbors of the neighbors, and so on.            //
+//                                                                           //
+// 'missingshlist' returns all missing subfaces of this region, furthermore, //
+// the edge rings of these subfaces are oriented in the same direction.      //
+// 'equatptlist' returns the vertices of the missing subfaces. Both lists    //
+// should be empty on input.  'worklist' is used for marking vertices of the //
+// missing region.                                                           //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::
+formmissingregion(face* missingsh, list* missingshlist, list* equatptlist,
+                  int* worklist)
+{
+  face neighsh, worksh, workseg;
+  point workpt[3];
+  int idx, i, j;
+
+  // Add 'missingsh' into 'missingshlist'.
+  missingshlist->append(missingsh);
+  // Save and mark its three vertices.
+  workpt[0] = sorg(*missingsh);
+  workpt[1] = sdest(*missingsh);
+  workpt[2] = sapex(*missingsh);
+  for (i = 0; i < 3; i++) {
+    idx = pointmark(workpt[i]) - in->firstnumber;
+    worklist[idx] = 1;
+    equatptlist->append(&workpt[i]);
+  }
+  // Temporarily uninfect it (avoid to save it again).
+  suninfect(*missingsh);
+  
+  // Find other missing subfaces.
+  for (i = 0; i < missingshlist->len(); i++) {
+    // Get a missing subface.
+    worksh = * (face *)(* missingshlist)[i];
+    // Check three neighbors of this face.
+    for (j = 0; j < 3; j++) {
+      sspivot(worksh, workseg);
+      if (workseg.sh == dummysh) {
+        spivot(worksh, neighsh);
+        if (sinfected(neighsh)) {
+          // Find a missing subface, adjust the edge ring.
+          if (sorg(neighsh) != sdest(worksh)) {
+            sesymself(neighsh);
+          }
+          if (b->verbose > 2) {
+            printf("    Add missing subface (%d, %d, %d).\n", 
+                   pointmark(sorg(neighsh)), pointmark(sdest(neighsh)),
+                   pointmark(sapex(neighsh)));
+          }
+          missingshlist->append(&neighsh);
+          // Save and mark its apex.
+          workpt[0] = sapex(neighsh);
+          idx = pointmark(workpt[0]) - in->firstnumber;
+          worklist[idx] = 1;
+          equatptlist->append(&workpt[0]);
+          // Temporarily uninfect it (avoid to save it again).
+          suninfect(neighsh);
+        } 
+      } 
+      senextself(worksh);
+    }
+  }
+
+  // The missing region has been formed. Infect missing subfaces again.
+  for (i = 0; i < missingshlist->len(); i++) {
+    worksh = * (face *)(* missingshlist)[i];
+    sinfect(worksh);
+  } 
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// scoutcrossingedge()    Search an edge crossing the missing region.        //
+//                                                                           //
+// 'missingshlist' contains all subfaces of the missing region. This routine //
+// first form a 'boundedgelist' consists of all boundary edges of the region,//
+// which are existing in DT (because they are either edges of existing faces //
+// or segments of the facet).  A crossing edge is found by rotating faces of //
+// DT around one of the boundary edges. It is possible that there is no edge //
+// crosses the missing region (e.g. the region has a degenerate point set).  //
+//                                                                           //
+// If find a croosing edge, return TRUE, and 'crossedgelist' contains this   //
+// edge.  Otherwise, return FALSE.  Both 'boundedgelist' and 'crossedgelist' //
+// should be empty on input.                                                 //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::
+scoutcrossingedge(list* missingshlist, list* boundedgelist,
+                  list* crossedgelist, int* worklist)
+{
+  triface starttet, spintet, worktet;
+  face startsh, neighsh, worksh, workseg;
+  point torg, tdest, tapex, workpt[3];
+  enum finddirectionresult collinear;
+  bool crossflag, inlistflag;
+  int hitbdry, i, j, k;
+
+  // Form the 'boundedgelist'.  Loop through 'missingshlist', check edges
+  //   of these subfaces. If an edge is a subsegment, or the neighbor
+  //   subface is uninfected, add it to 'boundedgelist'.
+  for (i = 0; i < missingshlist->len(); i++) {
+    worksh = * (face *)(* missingshlist)[i];
+    for (j = 0; j < 3; j++) {
+      sspivot(worksh, workseg);
+      if (workseg.sh == dummysh) {
+        spivot(worksh, neighsh);
+        if (!sinfected(neighsh)) {
+          boundedgelist->append(&worksh);
+        }
+      } else {
+        boundedgelist->append(&worksh);
+      }
+      senextself(worksh);
+    }
+  }
+
+  crossflag = false;
+  // Find a crossing edge. It is possible there is no such edge. We need to
+  //   loop through all edges of 'boundedgelist' for sure we don't miss any.
+  for (i = 0; i < boundedgelist->len() && !crossflag; i++) {
+    startsh = * (face *)(* boundedgelist)[i];
+    // 'startsh' holds an existing edge of the DT, find it.
+    torg = sorg(startsh);
+    tdest = sdest(startsh);
+    tapex = sapex(startsh);  
+    getsearchtet(torg, tdest, &starttet, &workpt[0]);
+    collinear = finddirection(&starttet, workpt[0]);
+    if (collinear == LEFTCOLLINEAR) {
+      enext2self(starttet);
+      esymself(starttet);
+    } else if (collinear == TOPCOLLINEAR) {
+      fnextself(starttet);
+      enext2self(starttet);
+      esymself(starttet);
+    }
+    assert(dest(starttet) == workpt[0]);
+    // Find the crossing edge by rotating faces around 'starttet'.
+    spintet = starttet;
+    hitbdry = 0;
+    do {
+      if (fnextself(spintet)) {
+        // Check if the opposite edge of 'spintet' crosses the region.
+        workpt[1] = apex(spintet);
+        workpt[2] = oppo(spintet);
+        j = pointmark(workpt[1]) - in->firstnumber;
+        k = pointmark(workpt[2]) - in->firstnumber;
+        if (worklist[j] == 1 || worklist[k] == 1) {
+          // One of the two points is a vertex of the missing region. This
+          //   edge can not cross this region.
+          inlistflag = false;
+        } else {
+          // Check if the edge crosses the region by performing a triangle
+          //   triangle intersection test. ('workpt[0]' is the dest of the
+          //   rotating edge 'spintet').
+          inlistflag = (triangle_triangle_inter(torg, tdest, tapex, workpt[0],
+                        workpt[1], workpt[2]) == INTERSECT);
+        }
+        if (inlistflag) {
+          // Find an edge crossing the missing region. Save it.
+          worktet = spintet;
+          adjustedgering(worktet, CCW);
+          enextfnextself(worktet);
+          enextself(worktet);
+          // Add this edge (worktet) into 'crossedgelist'.
+          crossedgelist->append(&worktet);
+          break;
+        }
+        if (apex(spintet) == apex(starttet)) break;
+      } else {
+        hitbdry++;
+        // It is only possible to hit boundary once.
+        if (hitbdry < 2) {
+          esym(starttet, spintet);
+        }
+      }
+    } while (hitbdry < 2);
+    crossflag = (crossedgelist->len() == 1);
+  }
+
+  return crossflag;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// rearrangesubfaces()    Rearrange the set of subfaces of a missing region  //
+//                        so that they conform to the faces of DT.           //
+//                                                                           //
+// The missing region formed by subfaces of 'missingshlist' contains a set   //
+// of degenerate vertices, hence the set of subfaces don't match the set of  //
+// faces in DT.  Instead of forcing them to present in DT, we re-arrange the //
+// connection of them so that the new subfaces conform to the faces of DT.   //
+//                                                                           //
+// 'boundedgelist' is a set of boundary edges of the region, these edges(may //
+// be subsegments) must exist in DT.  The process of rearrangement can be    //
+// described as follows:                                                     //
+//   - Form an inital link of boundary egdes;                                //
+//   - For each boundary edge (it must exist in DT),                         //
+//     - Remove it from the link;                                            //
+//     - Look for a face in DT which contains this edge and has its apex     //
+//       be a region vertex; Such face should exist (otherwise, an edge      //
+//       will cross the region).                                             //
+//     - Create a new subface on this face;  insert it into the surface      //
+//       mesh (the old subface connected at this edge will automatically     //
+//       be disconnecte;                                                     //
+//     - Check the other two edges of this new created subface,  if one      //
+//       matches a boundary edge, remove the edge from the link (it is       //
+//       finished) and bond them together.  Otherwise, add a new boundary    //
+//       to the link.                                                        //
+//     - Loop until the link of boundary edge is empty.                      //
+//                                                                           //
+// On completion, we have created and inserted a set of new subfaces which   //
+// conform to faces of DT. The set of old subfaces in 'missingshlist' are    //
+// deleted. The region vertices in 'equatptlist' are unmarked.               //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::
+rearrangesubfaces(list* missingshlist, list* boundedgelist, list* equatptlist,
+                  int* worklist)
+{
+  link *boundedgelink;
+  triface starttet, spintet, neightet, worktet;
+  face shloop, newsh, neighsh, spinsh, worksh;
+  face workseg, casingin, casingout;
+  point torg, tdest, workpt;
+  point liftpoint;
+  enum finddirectionresult collinear;
+  REAL area, ori1, ori2;
+  bool matchflag, finishflag;
+  int shmark, idx, hitbdry;
+  int i, j;
+
+  // Initialize the boundary edge link.
+  boundedgelink = new link(sizeof(face), NULL, 256);
+
+  // Create an initial boundary link.
+  for (i = 0; i < boundedgelist->len(); i++) {
+    shloop = * (face *)(* boundedgelist)[i];
+    if (i == 0) {
+      if (b->quality) {
+        // area will be copied to all new created subfaces.
+        area = areabound(shloop);
+      }
+      // 'shmark' will be set to all new created subfaces.
+      shmark = shellmark(shloop);
+      // Get the liftpoint of this facet for later checking.
+      liftpoint = getliftpoint(shmark);
+    }
+    sspivot(shloop, workseg);
+    if (workseg.sh == dummysh) {
+      // This edge is an interior edge.
+      spivot(shloop, neighsh);
+      boundedgelink->add(&neighsh);
+    } else {
+      // This side has a segment, the edge exists. 
+      boundedgelink->add(&shloop);
+    }
+  }
+
+  // Loop until the link is empty.  Each boundary edge will be finished by a
+  //   new subface. After a new subface is created, it will be inserted into
+  //   both the surface mesh and the DT, and new boundary edge will be added
+  //   into the link.
+  while (boundedgelink->len() > 0) {
+    // Remove the top boundary edge from the link.
+    shloop = * (face *) boundedgelink->del(1);
+    sspivot(shloop, workseg); // 'workseg' indicates it is a segment or not.
+    torg = sorg(shloop);
+    tdest = sdest(shloop);
+    // Find a tetrahedron containing edge (torg, tdest).
+    getsearchtet(torg, tdest, &starttet, &workpt);
+    collinear = finddirection(&starttet, workpt);
+    if (collinear == LEFTCOLLINEAR) {
+      enext2self(starttet);
+      esymself(starttet);
+    } else if (collinear == TOPCOLLINEAR) {
+      fnextself(starttet);
+      enext2self(starttet);
+      esymself(starttet);
+    }
+    assert(dest(starttet) == workpt);
+    // Spinning faces around this edge, find the one lies on the facet AND
+    //   is not a subface yet.
+    matchflag = false;
+    spintet = starttet;
+    hitbdry = 0;
+    do {
+      workpt = apex(spintet);
+      idx = pointmark(workpt) - in->firstnumber;
+      if (worklist[idx] == 1) {
+        // This face is on the facet.
+        if (workseg.sh != dummysh) {
+          // 'shloop' is a segment.
+          if (workpt != sapex(shloop)) {
+            //  Be careful that 'workpt' may not be the vertex that we're
+            //   looking for. Because the missing region may be non-convex.
+            //   However, the right vertex should be at the same side of
+            //   the apex of 'shloop'.
+            ori1 = orient3d(torg, tdest, liftpoint, sapex(shloop));
+            ori2 = orient3d(torg, tdest, liftpoint, workpt);
+            assert(ori1 != 0.0 && ori2 != 0.0);
+            if ((ori1 > 0.0 && ori2 > 0.0) || (ori1 < 0.0 && ori2 < 0.0)) {
+              matchflag = true;
+              break;
+            }
+          } else {
+            // This face is already exist! It is possible. Created by
+            //   previous recovering procedures.
+            matchflag = true;
+            break;
+          }
+        } else {
+          // 'shloop' is not a segment. Only insert a subface when there
+          //   does not already exist a subface.
+          tspivot(spintet, neighsh);
+          if (neighsh.sh == dummysh) {
+            // This face is not a subface yet.
+            matchflag = true;
+            break;
+          }
+        }
+      }
+      if (!fnextself(spintet)) {
+        hitbdry ++;
+        if (hitbdry < 2) {
+          esym(starttet, spintet);
+          if (!fnextself(spintet)) {
+            hitbdry ++;
+          }
+        }
+      }
+    } while (hitbdry < 2 && apex(spintet) != apex(starttet));
+    assert(matchflag == true);
+    tspivot(spintet, neighsh);
+    if (neighsh.sh != dummysh) {
+      printf("Error:  Invalid PLC.\n");
+      printf("  Facet #%d and facet #%d overlap each other.\n",
+             shellmark(neighsh), shellmark(shloop));
+      printf("  It might be caused by a facet is defined more than once.\n");
+      printf("  Hint:  Use -d switch to find all overlapping facets.\n");
+      exit(1);
+    }
+    // The side of 'spintet' is at which a new subface will be attached.
+    adjustedgering(spintet, CCW);
+    // Create the new subface.
+    makeshellface(subfaces, &newsh);
+    setsorg(newsh, org(spintet));
+    setsdest(newsh, dest(spintet));
+    setsapex(newsh, apex(spintet));
+    if (b->quality) {
+      // Copy the areabound into the new subface.
+      setareabound(newsh, area);
+    }
+    setshellmark(newsh, shmark);
+    // Insert it into the current mesh.
+    tsbond(spintet, newsh);
+    sym(spintet, neightet);
+    if (neightet.tet != dummytet) {
+      sesym(newsh, neighsh);
+      tsbond(neightet, neighsh);
+    }
+    // Insert it into the surface mesh.
+    sspivot(shloop, workseg);
+    if (workseg.sh == dummysh) {
+      sbond(shloop, newsh);
+    } else {
+      // There is a subsegment, 'shloop' is the subface which is going to
+      //   die. Insert the 'newsh' at the place of 'shloop' into its face
+      //   link, so as to dettach 'shloop'.   The original connection is:
+      //   -> casingin -> shloop -> casingout ->, it will be changed with:
+      //   -> casingin ->  newsh -> casingout ->.  Pay attention to the
+      //   case when this subsegment is dangling in the mesh, i.e., 'shloop'
+      //   is bonded to itself.
+      spivot(shloop, casingout);
+      if (shloop.sh != casingout.sh) {
+        // 'shloop' is not bonded to itself.
+        spinsh = casingout;
+        do {
+          casingin = spinsh;
+          spivotself(spinsh);
+        } while (sapex(spinsh) != sapex(shloop));
+        assert(casingin.sh != shloop.sh); 
+        // Bond casingin -> newsh -> casingout.
+        sbond1(casingin, newsh);
+        sbond1(newsh, casingout);
+      } else {
+        // Bond newsh -> newsh.
+        sbond(newsh, newsh);
+      }
+      // Bond the segment.
+      ssbond(newsh, workseg);
+    }
+    // Check other two sides of this new subface.  If a side is not bonded
+    //   to any edge in the link, it will be added to the link.
+    for (i = 0; i < 2; i++) {
+      if (i == 0) {
+        senext(newsh, worksh);
+      } else {
+        senext2(newsh, worksh);
+      }
+      torg = sorg(worksh);
+      tdest = sdest(worksh);
+      finishflag = false;
+      for (j = 0; j < boundedgelink->len() && !finishflag; j++) {
+        neighsh = * (face *) boundedgelink->getnitem(j + 1);
+        if ((sorg(neighsh) == torg && sdest(neighsh) == tdest) ||
+            (sorg(neighsh) == tdest && sdest(neighsh) == torg)) {
+          // Find a boundary edge.  Bond them and exit the loop.
+          sspivot(neighsh, workseg);
+          if (workseg.sh == dummysh) {
+            sbond(neighsh, worksh);
+          } else {
+            // There is a subsegment, 'neighsh' is the subface which is
+            //   going to die. Do the same as above for 'worksh'.
+            spivot(neighsh, casingout);
+            if (neighsh.sh != casingout.sh) {
+              // 'neighsh' is not bonded to itself.
+              spinsh = casingout;
+              do {
+                casingin = spinsh;
+                spivotself(spinsh);
+              } while (sapex(spinsh) != sapex(neighsh));
+              assert(casingin.sh != neighsh.sh); 
+              // Bond casingin -> worksh -> casingout.
+              sbond1(casingin, worksh);
+              sbond1(worksh, casingout);
+            } else {
+              // Bond worksh -> worksh.
+              sbond(worksh, worksh);
+            }
+            // Bond the segment.
+            ssbond(worksh, workseg);
+          }
+          // Remove this boundary edge from the link.
+          boundedgelink->del(j + 1);
+          finishflag = true;
+        }
+      }
+      if (!finishflag) {
+        // It's a new boundary edge, add it to link.
+        boundedgelink->add(&worksh);
+      }
+    }
+  }
+
+  // Deallocate the set of old missing subfaces.
+  for (i = 0; i < missingshlist->len(); i++) {
+    worksh = * (face *)(* missingshlist)[i];
+    shellfacedealloc(subfaces, worksh.sh);
+  }
+  // Unmark region vertices in 'worklist'.
+  for (i = 0; i < equatptlist->len(); i++) {
+    workpt = * (point *)(* equatptlist)[i];
+    idx = pointmark(workpt) - in->firstnumber;
+    worklist[idx] = 0;
+  }
+
+  delete boundedgelink;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// recoversubfaces()    Recover the set of subfaces of a missing region so   //
+//                      that they become faces of the DT.                    //
+//                                                                           //
+// 'missingshlist' contains a set of missing subfaces which form the missing //
+// region. A cavity retriangulation method is used to recover these subfaces //
+// in the DT.  To do so, first find all the tetrahedra in DT that intersect  //
+// the relative interior of the missing region. Then delete them from the DT,//
+// this will form a cavity C inside the DT. Now we want to retriangulate the //
+// C and want the missing subfaces will appear after the retriangulation. To //
+// complete this, we first insert the missing subfaces into the C, so as to  //
+// split it into two disjointed cavity. Then retriangulate them separately.  //
+// (See the intoduction of the routine triangulatecavity() for the cavity    //
+// retriangulation method.)                                                  //
+//                                                                           //
+// On input, 'crossedgelist' contains an edge which is crossing the missing  //
+// region.  All tetrahedra containing this edge must cross the region. It is //
+// possible there are other crossing edges as well.  They can be found by    //
+// checking the edges of the discovered crossing tetrahedra.  Through this   //
+// way, other crossing tetrahedra of the region can be found incrementally.  //
+// However, it doesn't guarantee we can get all crossing tetrahedra of this  //
+// region.  The discovered tetrahedra are connected each other. There may    //
+// exist other tetrahedra which are crossing the region but disjoint with    //
+// the set of discovered tetrahedra.  Due to this fact, we need to check the //
+// missing subfaces once more. Only recover those which are crossed by the   //
+// set of discovered tetrahedra. The other subfaces remain missing and will  //
+// be recovered later.                                                       //
+//                                                                           //
+// On completion, we have modified the DT to incorporate a set of subfaces.  //
+// The recovered subfaces of 'missingshlist' are uninfected.  The region     //
+// vertices in 'equatptlist' are unmarked.                                   //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::
+recoversubfaces(list* missingshlist, list* crossedgelist, list* equatptlist,
+                int* worklist)
+{
+  list *crossshlist, *crosstetlist;
+  list *belowfacelist, *abovefacelist;
+  list *belowptlist, *aboveptlist;
+  triface starttet, spintet, neightet, worktet;
+  face startsh, neighsh, worksh, workseg;
+  point torg, tdest, tapex, workpt[3];
+  REAL checksign, orgori, destori;
+  bool crossflag, inlistflag;
+  bool belowflag, aboveflag;
+  int idx, share;
+  int i, j, k;
+
+  // Initialize the working lists.
+  crossshlist = new list(sizeof(face), NULL);
+  crosstetlist = new list(sizeof(triface), NULL);
+  belowfacelist = new list(sizeof(triface), NULL);
+  abovefacelist = new list(sizeof(triface), NULL);
+  belowptlist = new list("point *");
+  aboveptlist = new list("point *");
+
+  // Get a face as horizon.
+  startsh = * (face *)(* missingshlist)[0];
+  torg = sorg(startsh);
+  tdest = sdest(startsh);
+  tapex = sapex(startsh);
+
+  // Collect the set of crossing tetrahedra by rotating crossing edges. At
+  //   the beginning, 'crossedgelist' contains one crossing edge,  others
+  //   will be discovered after newly crossing tetrahedra are found.
+  for (i = 0; i < crossedgelist->len(); i++) {
+    starttet = * (triface *)(* crossedgelist)[i];
+    adjustedgering(starttet, CCW);
+    if (b->verbose > 2) {
+      printf("    Collect tets containing edge (%d, %d).\n",
+             pointmark(org(starttet)), pointmark(dest(starttet)));
+    }
+    orgori = orient3d(torg, tdest, tapex, org(starttet));
+    destori = orient3d(torg, tdest, tapex, dest(starttet));
+    assert(orgori * destori < 0.0); 
+    spintet = starttet;
+    do {
+      // The face rotation should not meet boundary.
+      fnextself(spintet); 
+      // Check the validity of the PLC.
+      tspivot(spintet, worksh);
+      if (worksh.sh != dummysh) {
+        printf("Error:  Invalid PLC.\n");
+        printf("  Two subfaces (%d, %d, %d) and (%d, %d, %d)\n",
+               pointmark(torg), pointmark(tdest), pointmark(tapex),
+               pointmark(sorg(worksh)), pointmark(sdest(worksh)),
+               pointmark(sapex(worksh)));
+        printf("  are found intersecting each other.\n");
+        printf("  Hint:  Use -d switch to find all intersecting facets.\n");
+        exit(1);
+      }
+      if (!infected(spintet)) {
+        if (b->verbose > 2) {
+          printf("      Add crossing tet (%d, %d, %d, %d).\n",
+                 pointmark(org(spintet)), pointmark(dest(spintet)),
+                 pointmark(apex(spintet)), pointmark(oppo(spintet)));
+        }
+        infect(spintet);
+        crosstetlist->append(&spintet);
+      }
+      // Check whether other two edges of 'spintet' is a crossing edge.
+      //   It can be quickly checked from the apex of 'spintet', if it is
+      //   not on the facet, then there exists a crossing edge.
+      workpt[0] = apex(spintet);
+      idx = pointmark(workpt[0]) - in->firstnumber;
+      if (worklist[idx] != 1) {
+        // Either edge (dest, apex) or edge (apex, org) crosses.
+        checksign = orient3d(torg, tdest, tapex, workpt[0]);
+        assert(checksign != 0.0);
+        if (checksign * orgori < 0.0) {
+          enext2(spintet, worktet); // edge (apex, org).
+          workpt[1] = org(spintet);
+        } else {
+          assert(checksign * destori < 0.0);
+          enext(spintet, worktet);  // edge (dest, apex).
+          workpt[1] = dest(spintet);
+        }
+        // 'worktet' represents the crossing edge. Add it into list only
+        //   it doesn't exist in 'crossedgelist'.
+        inlistflag = false;
+        for (j = 0; j < crossedgelist->len() && !inlistflag; j++) {
+          neightet = * (triface *)(* crossedgelist)[j];
+          if (org(neightet) == workpt[0]) {
+            if (dest(neightet) == workpt[1]) inlistflag = true;
+          } else if (org(neightet) == workpt[1]) {
+            if (dest(neightet) == workpt[0]) inlistflag = true;
+          }
+        }
+        if (!inlistflag) {
+          crossedgelist->append(&worktet);
+        }
+      }
+    } while (apex(spintet) != apex(starttet));
+  }
+
+  // Identifying the boundary faces of the cavity.
+  for (i = 0; i < crosstetlist->len(); i++) {
+    starttet = * (triface *)(* crosstetlist)[i];
+    assert(infected(starttet));
+    adjustedgering(starttet, CCW);
+    // Only need to check two sides of starttet. Current side and the side
+    //   of fnext() are sharing the crossing edge, the two neighbors must
+    //   be crossing tetrahedra. Hence these two sides can't be boundaries
+    //   of the cavity. 
+    for (j = 0; j < 2; j++) {
+      if (j == 0) {
+        enextfnext(starttet, worktet);
+      } else {
+        enext2fnext(starttet, worktet);
+      } 
+      sym(worktet, neightet);
+      // If the neighbor doesn't exist or exists but doesn't be infected,
+      //   it's a boundary face of the cavity, save it.
+      if (neightet.tet == dummytet || !infected(neightet)) {
+        workpt[0] = org(worktet);
+        workpt[1] = dest(worktet);
+        workpt[2] = apex(worktet);
+        belowflag = aboveflag = false;
+        share = 0;
+        for (k = 0; k < 3; k++) {
+          idx = pointmark(workpt[k]) - in->firstnumber;
+          if (worklist[idx] == 0) {
+            // It's not a vertices of facet, find which side it lies.
+            checksign = orient3d(torg, tdest, tapex, workpt[k]);
+            assert(checksign != 0.0);
+            if (checksign > 0.0) {
+              // It lies "below" the facet wrt. 'startsh'.
+              worklist[idx] = 2;
+              belowptlist->append(&workpt[k]);
+            } else if (checksign < 0.0) {
+              // It lies "above" the facet wrt. 'startsh'.
+              worklist[idx] = 3;
+              aboveptlist->append(&workpt[k]);
+            }
+          }
+          if (worklist[idx] == 2) {
+            // This face lies "below" the facet wrt. 'startsh'.
+            belowflag = true;
+          } else if (worklist[idx] == 3) {
+            // This face lies "above" the facet wrt. 'startsh'.
+            aboveflag = true;
+          } else {
+            // In degenerate case, this face may just be the equator.
+            assert(worklist[idx] == 1);
+            share++;
+          }
+        }
+        // The degenerate case has been ruled out.
+        assert(share < 3);
+        // Only one flag is possible for a cavity face.
+        assert(belowflag ^ aboveflag); 
+        if (belowflag) {
+          belowfacelist->append(&worktet);
+        } else if (aboveflag) {
+          abovefacelist->append(&worktet);
+        }
+      }
+    }
+  }
+
+  // Form the set of missing subfaces which are crossed by tetrahedra of
+  //   'crosstetlist'.  It is a subset of 'missingshlist'.  These faces
+  //   need be recovered (using cavity filling algorithm). Other subfaces
+  //   remain infected and will be recovered later.
+  for (i = 0; i < missingshlist->len(); i++) {
+    worksh = * (face *)(* missingshlist)[i];
+    assert(sinfected(worksh));
+    torg = sorg(worksh);
+    tdest = sdest(worksh);
+    tapex = sapex(worksh);
+    crossflag = false;
+    for (j = 0; j < crosstetlist->len() && !crossflag; j++) {
+      starttet = * (triface *)(* crosstetlist)[j];
+      adjustedgering(starttet, CCW);
+      // Only need to check two sides of worktet.
+      for (k = 0; k < 2 && !crossflag; k++) {
+        if (k == 0) {
+          worktet = starttet;
+        } else {
+          fnext(starttet, worktet);
+        }
+        // torg, tdest and tapex SHOULD be non-collinear.
+        crossflag = tritritest(&worktet, torg, tdest, tapex);
+      }
+    }
+    if (crossflag) {
+      // 'worksh' is crossed by 'worktet', uninfect it.
+      suninfect(worksh);
+      crossshlist->append(&worksh);
+    } 
+  }
+
+  // Clear flags set in 'worklist'.
+  for (i = 0; i < equatptlist->len(); i++) {
+    workpt[0] = * (point *)(* equatptlist)[i];
+    idx = pointmark(workpt[0]) - in->firstnumber;
+    // assert(worklist[idx] == 1);
+    worklist[idx] = 0;
+  }
+  for (i = 0; i < belowptlist->len(); i++) {
+    workpt[0] = * (point *)(* belowptlist)[i];
+    idx = pointmark(workpt[0]) - in->firstnumber;
+    // assert(worklist[idx] == 2);
+    worklist[idx] = 0;
+  }
+  for (i = 0; i < aboveptlist->len(); i++) {
+    workpt[0] = * (point *)(* aboveptlist)[i];
+    idx = pointmark(workpt[0]) - in->firstnumber;
+    // assert(worklist[idx] == 3);
+    worklist[idx] = 0;
+  }
+
+  assert (aboveptlist->len() > 0);
+  // Retriangulate the upper part of the cavity.
+  triangulatecavity(crossshlist, abovefacelist, equatptlist, aboveptlist);
+
+  // Inverse the direction of faces in 'missingshlist'.
+  for (i = 0; i < crossshlist->len(); i++) {
+    worksh = * (face *)(* crossshlist)[i];
+    sesymself(worksh);
+    * (face *)(* crossshlist)[i] = worksh;
+  }
+
+  assert(belowptlist->len() > 0);
+  // Retriangulate the lower part of the cavity.
+  triangulatecavity(crossshlist, belowfacelist, equatptlist, belowptlist);
+
+  // Delete old tetrahedra of this cavity.
+  for (i = 0; i < crosstetlist->len(); i++) {
+    worktet = * (triface *)(* crosstetlist)[i];
+    tetrahedrondealloc(worktet.tet);
+  }
+
+  delete crossshlist;
+  delete crosstetlist;
+  delete belowfacelist;
+  delete abovefacelist;
+  delete belowptlist;
+  delete aboveptlist;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// constrainedfacets()    Insert PLC facets into the Delaunay tetrahedraliz- //
+//                        ation of the PLC vertices.                         //
+//                                                                           //
+// This is the last step of our CDT algorithm, which transforms a Delaunay   //
+// tetrahedralization DT into a constrained Delaunay tetrahedralization by   //
+// forcing all subfaces of the surface mesh F of the PLC into the DT. It is  //
+// important that all PLC segments have been previously recovered in DT, so  //
+// the existence of a CDT is guaranteed (by our CDT theorem).  Hence, all    //
+// subfaces of F can be recovered in the DT without inserting vertices.      //
+//                                                                           //
+// The process of constrained facets can be though of "merging" the surface  //
+// mesh F completely into the Delaunay tetrahedralization DT.  Recover the   //
+// subfaces in DT if they are not matching.  Hence, the process is divided   //
+// into two steps: first insert all existing subfaces of F into DT, that is, //
+// each subface already appears as a face of DT; at the same time, queue all //
+// missing subfaces. Then recover missing subfaces (explained below).  The   //
+// second step changes a DT into a CDT.                                      //
+//                                                                           //
+// When a subface s of a facet f is found missing in DT, most probably, some //
+// other subfaces near to s and belong to f are also missing.  The set of    //
+// adjoining missing subfaces of f forms a missing region. It is obvious to  //
+// see that this region is closed and is bounded by the edges of existing    //
+// subfaces or the segments of f. Instead of recovering missing subfaces of  //
+// this region one by one, they are recovered together, i.e., each time a    //
+// closed missing region will be recovered.                                  //
+//                                                                           //
+// There are two possibilities can from a mssing region R: (1) Some edges of //
+// DT intersect subfaces in R; (2) No edge of DT cross R, but another set of //
+// faces of DT spans R, this is because the existence of degeneracies (five  //
+// or more vertices of R are cospherical).  If it is case (1), we modify DT  //
+// so that its faces spans R.  A cavity retriangulation algorithm is used to //
+// recover the region. If it is case (2), F is modified so that the set of   //
+// subfaces of F matches faces in DT. A face rearrangment algorithm is used. //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::constrainedfacets()
+{
+  queue *missingshqueue;
+  list *missingshlist;
+  list *boundedgelist;
+  list *crossedgelist;
+  list *equatptlist;
+  triface searchtet;
+  face subloop;
+  int *worklist;
+  int i;
+
+  if (!b->quiet) {
+    printf("Constraining facets.\n");
+  }
+
+  // Compute a mapping from points to tetrahedra.
+  makepoint2tetmap();
+  // Initialize the queue to store the set of missing subfaces.
+  missingshqueue = new queue(sizeof(face));
+  // Initialize the working lists.
+  missingshlist = new list(sizeof(face), NULL);
+  boundedgelist = new list(sizeof(face), NULL);
+  crossedgelist = new list(sizeof(triface), NULL);
+  equatptlist = new list("point *");
+  // Initialize the list for matching vertices.
+  worklist = new int[points->items];
+  for (i = 0; i < points->items; i++) {
+    worklist[i] = 0;
+  }
+
+  // Step 1, go through all subfaces, insert existing subfaces into DT.
+  //   Missing subfaces are queued. Moreover, they are infected so that
+  //   can be distinguished from existing ones. 
+  searchtet.tet = (tetrahedron *) NULL;
+  subfaces->traversalinit();
+  subloop.sh = shellfacetraverse(subfaces);
+  while (subloop.sh != (shellface *) NULL) {
+    if (!insertsubface(&subloop, &searchtet)) {
+      if (b->verbose > 1) {
+        printf("    Queuing missing subface (%d, %d, %d).\n",
+               pointmark(sorg(subloop)), pointmark(sdest(subloop)),
+               pointmark(sapex(subloop)));
+      }
+      sinfect(subloop);
+      missingshqueue->push(&subloop);
+    }
+    subloop.sh = shellfacetraverse(subfaces);
+  }
+
+  // Step 2, recover all missing subfaces.
+  while (!missingshqueue->empty()) {
+    subloop = * (face *) missingshqueue->pop();
+    if (!isdead(&subloop) && sinfected(subloop)) {
+      if (b->verbose > 1) {
+        printf("    Recovering subface (%d, %d, %d).\n",
+               pointmark(sorg(subloop)), pointmark(sdest(subloop)),
+               pointmark(sapex(subloop)));
+      }
+      // Other operation may have recovered this subface.
+      if (!insertsubface(&subloop, &searchtet)) {
+        // First form the missing region.
+        formmissingregion(&subloop, missingshlist, equatptlist, worklist);
+        // Are there crossing tetrahedra?
+        if (scoutcrossingedge(missingshlist, boundedgelist, crossedgelist,
+                              worklist)) {
+          // There are! Recover by retriangulating cavities.
+          recoversubfaces(missingshlist, crossedgelist, equatptlist, worklist);
+          // There may remain some un-recovered subfaces. Don't ignore them.
+          for (i = 0; i < missingshlist->len(); i++) {
+            subloop = * (face *)(* missingshlist)[i];
+            if (sinfected(subloop)) {
+              // Put it back into queue.
+              missingshqueue->push(&subloop);
+            }
+          }
+        } else {
+          // No crossing tetrahedra. Rearrange subfaces in surface mesh.
+          rearrangesubfaces(missingshlist, boundedgelist, equatptlist,
+                            worklist);
+        }
+        // Clear all working lists.
+        missingshlist->clear();
+        boundedgelist->clear();
+        crossedgelist->clear();
+        equatptlist->clear();
+      } else {
+        // This subface has been recovered. Only uninfect it.
+        suninfect(subloop);
+      }
+    }
+  }
+
+  delete missingshqueue;
+  delete missingshlist;
+  delete boundedgelist;
+  delete crossedgelist;
+  delete equatptlist;
+  delete [] worklist;
+}
+
+//
+// End of constrained Delaunay triangulation routines
+//
+
+//
+// Begin of carving out holes and concavities routines
+//
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// indenthull()    Remove redundant tetrahedra on the convex hull.           //
+//                                                                           //
+// All tetrahedra on the hull which are not protected by subfaces will be    //
+// removed.  As a result, the convex tetrahedralization becomes concave, and //
+// the new hull is formed by subfaces.                                       //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::indenthull()
+{
+  memorypool *viri;
+  link *hulllink;
+  tetrahedron **virusloop;
+  triface tetloop, hullface;
+  triface checkface, neightet;
+  face checksh;
+  point p1, p2, p3;
+  bool indentflag;;
+  int i;
+
+  if (b->verbose) {
+    printf("  Indenting hulls.\n");
+  }
+
+  // Initialize a pool of viri.
+  viri = new memorypool(sizeof(tetrahedron *), 1024, POINTER, 0);
+  // Initialize the hulllink.
+  hulllink = new link(sizeof(triface), NULL, 1024);
+
+  // Find out all hull faces which are not protected by subfaces.  At the
+  //   same time, infect all tetrahedra which has faces on the hull and
+  //   are protected by subfaces.
+  tetrahedrons->traversalinit();
+  tetloop.tet = tetrahedrontraverse();
+  while (tetloop.tet != (tetrahedron *) NULL) {    
+    indentflag = true;
+    for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) {
+      // Is this face on the hull?
+      sym(tetloop, neightet);
+      if (neightet.tet == dummytet) {
+        // Is the face protected by a subface?
+        tspivot(tetloop, checksh);
+        if (checksh.sh == dummysh) {
+          // Add this face into hulllink.
+          hulllink->add(&tetloop);
+        } else {
+          // It is protected by a subface. 
+          indentflag = false;
+        }
+      }
+    }
+    if (!indentflag) {
+      // Infect it to indicate it is at interior space.
+      virusloop = (tetrahedron **) viri->alloc();
+      *virusloop = tetloop.tet;
+    }
+    tetloop.tet = tetrahedrontraverse();
+  }
+
+  // Loop until the hulllink is empty.
+  while (hulllink->len() > 0) {
+    // Remove a hullface from the link.
+    hullface = * (triface *) hulllink->del(1);
+    // The tet may already be removed.
+    if (isdead(&hullface)) continue;
+    if (b->verbose > 1) {
+      printf("  Indenting face (%d, %d, %d).\n", pointmark(org(hullface)),
+             pointmark(dest(hullface)), pointmark(apex(hullface)));
+    }
+    // The tet may be an interior one (if it is infected).
+    indentflag = !infected(hullface);
+    // Check if hullface can be indented.
+    adjustedgering(hullface, CCW);
+    for (i = 0; i < 3 && indentflag; i++) {
+      fnext(hullface, checkface);
+      sym(checkface, neightet);
+      tspivot(checkface, checksh);
+      if (neightet.tet != dummytet) {
+        // The neighbor exists.
+        if (checksh.sh == dummysh) {
+          // This side is not protected by a subface. If the neighbor is
+          //   marked as an interior tet, hullface survives.
+          indentflag = !infected(neightet);  
+        } else {
+          // It is protected by a subface.
+          if (!infected(neightet)) {
+            // This is a new discovered interior tet. Infect it.
+            infect(neightet);
+            virusloop = (tetrahedron **) viri->alloc();
+            *virusloop = neightet.tet;
+          }
+        }
+      }
+      enextself(hullface);
+    }
+    if (!indentflag) {
+      // hullface survives.
+      p1 = org(hullface);
+      p2 = dest(hullface);
+      p3 = apex(hullface);
+      printf("Warning:  Face (%d, %d, %d) is open.\n", pointmark(p1),
+             pointmark(p2), pointmark(p3));
+      if (!infected(hullface)) {
+        // Infect it and add it into viris.
+        infect(hullface);
+        virusloop = (tetrahedron **) viri->alloc();
+        *virusloop = hullface.tet;
+      }
+      continue;
+    }
+    // Indent hullface. That is, remove it from the hull.  The hullsize is
+    //   changed, new discoved open hullfaces will be added to hulllink.
+    for (i = 0; i < 3; i++) {
+      fnext(hullface, checkface);
+      sym(checkface, neightet);
+      tspivot(checkface, checksh);
+      if (neightet.tet != dummytet) {
+        // The neighbor exists. 
+        if (checksh.sh == dummysh) {
+          // This side is not protected.
+          assert(!infected(neightet));
+          hulllink->add(&neightet);
+        } else {
+          // It is protected.
+          assert(infected(neightet));
+          stdissolve(checksh);
+        }
+        // It becomes a new hull face.
+        dissolve(neightet);
+        hullsize++;
+      } else {
+        // This side is hull face also.
+        if (checksh.sh != dummysh) {
+          // A dangling subface. It will be isolated.
+          stdissolve(checksh);
+        }
+        // Decrease the hullsize.
+        hullsize--;
+      }
+      enextself(hullface);
+    } 
+    // Delete the tetrahedron.
+    tetrahedrondealloc(hullface.tet);
+    // Decrease the hullsize.
+    hullsize--;
+  }
+
+  // Uninfect infected tetrahedra.
+  viri->traversalinit();
+  virusloop = (tetrahedron **) viri->traverse();
+  while (virusloop != (tetrahedron **) NULL) {
+    neightet.tet = *virusloop;
+    uninfect(neightet);    
+    virusloop = (tetrahedron **) viri->traverse();
+  }
+
+  delete viri;
+  delete hulllink;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// infecthull()    Virally infect all of the tetrahedra of the convex hull   //
+//                 that are not protected by subfaces.  Where there are      //
+//                 subfaces, set boundary markers as appropriate.            //
+//                                                                           //
+// Memorypool 'viri' is used to return all the infected tetrahedra.          //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::infecthull(memorypool *viri)
+{
+  triface tetloop, tsymtet;
+  tetrahedron **deadtet;
+  face hullface;
+  // point horg, hdest, hapex;
+
+  if (b->verbose) {
+    printf("  Marking concavities for elimination.\n");
+  }
+  tetrahedrons->traversalinit();
+  tetloop.tet = tetrahedrontraverse();
+  while (tetloop.tet != (tetrahedron *) NULL) {
+    // Is this tetrahedron on the hull?
+    for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) {
+      sym(tetloop, tsymtet);
+      if (tsymtet.tet == dummytet) {
+        // Is the tetrahedron protected by a subface?
+        tspivot(tetloop, hullface);
+        if (hullface.sh == dummysh) {
+          // The tetrahedron is not protected; infect it.
+          if (!infected(tetloop)) {
+            infect(tetloop);
+            deadtet = (tetrahedron **) viri->alloc();
+            *deadtet = tetloop.tet;
+            break;  // Go and get next tet.
+          }
+        } else {
+          // The tetrahedron is protected; set boundary markers if appropriate.
+          if (shellmark(hullface) == 0) {
+            setshellmark(hullface, 1);
+            /*
+            horg = sorg(hullface);
+            hdest = sdest(hullface);
+            hapex = sapex(hullface);
+            if (pointmark(horg) == 0) {
+              setpointmark(horg, 1);
+            }
+            if (pointmark(hdest) == 0) {
+              setpointmark(hdest, 1);
+            }
+            if (pointmark(hapex) == 0) {
+              setpointmark(hapex, 1);
+            }
+            */
+          }
+        }
+      }
+    }
+    tetloop.tet = tetrahedrontraverse();
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// plague()    Spread the virus from all infected tetrahedra to any          //
+//             neighbors not protected by subfaces.  Delete all infected     //
+//             tetrahedra.                                                   //
+//                                                                           //
+// This is the procedure that actually creates holes and concavities.        //
+//                                                                           //
+// This procedure operates in two phases.  The first phase identifies all    //
+// the tetrahedra that will die, and marks them as infected.  They are       //
+// marked to ensure that each tetrahedron is added to the virus pool only    //
+// once, so the procedure will terminate.                                    //
+//                                                                           //
+// The second phase actually eliminates the infected tetrahedra.  It also    //
+// eliminates orphaned segments and points(not done now).                    //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::plague(memorypool *viri)
+{
+  tetrahedron **virusloop;
+  tetrahedron **deadtet;
+  triface testtet, neighbor;
+  face neighsh, testseg;
+  face spinsh, casingin, casingout;
+  point checkpt;
+  int *tetspernodelist;
+  int i, j;
+
+  if (b->verbose) {
+    printf("  Marking neighbors of marked tetrahedra.\n");
+  }
+  // Loop through all the infected tetrahedra, spreading the virus to
+  //   their neighbors, then to their neighbors' neighbors.
+  viri->traversalinit();
+  virusloop = (tetrahedron **) viri->traverse();
+  while (virusloop != (tetrahedron **) NULL) {
+    testtet.tet = *virusloop;
+    // Temporarily uninfect this tetrahedron, not necessary.
+    uninfect(testtet);
+    // Check each of the tetrahedron's four neighbors.
+    for (testtet.loc = 0; testtet.loc < 4; testtet.loc++) {
+      // Find the neighbor.
+      sym(testtet, neighbor);
+      // Check for a shell between the tetrahedron and its neighbor.
+      tspivot(testtet, neighsh);
+      // Check if the neighbor is nonexistent or already infected.
+      if ((neighbor.tet == dummytet) || infected(neighbor)) {
+        if (neighsh.sh != dummysh) {
+          // There is a subface separating the tetrahedron from its neighbor,
+          //   but both tetrahedra are dying, so the subface dies too.
+          // Before deallocte this subface, dissolve the connections between
+          //   other subfaces, subsegments and tetrahedra.
+          neighsh.shver = 0;
+          // For keep the same enext() direction.
+          findedge(&testtet, sorg(neighsh), sdest(neighsh));
+          for (i = 0; i < 3; i++) {
+            sspivot(neighsh, testseg);
+            if (testseg.sh != dummysh) {
+              // A subsegment is found at this side, dissolve this subface
+              //   from the face link of this subsegment.
+              testseg.shver = 0;
+              spinsh = neighsh;
+              if (sorg(spinsh) != sorg(testseg)) {
+                sesymself(spinsh);
+              }
+              spivot(spinsh, casingout);
+              if (casingout.sh == spinsh.sh) {
+                // This is a trivial face link, only 'neighsh' itself,
+                //   the subsegment at this side is also died.
+                shellfacedealloc(subsegs, testseg.sh);
+              } else {
+                spinsh = casingout;
+                do {
+                  casingin = spinsh;
+                  spivotself(spinsh);
+                } while (spinsh.sh != neighsh.sh);
+                // Set the link casingin->casingout.
+                sbond1(casingin, casingout);
+                // Bond the subsegment anyway.
+                ssbond(casingin, testseg);
+              }
+            }
+            senextself(neighsh);
+            enextself(testtet);
+          }
+          shellfacedealloc(subfaces, neighsh.sh);
+          if (neighbor.tet != dummytet) {
+            // Make sure the subface doesn't get deallocated again later
+            //  when the infected neighbor is visited.
+            tsdissolve(neighbor);
+          }
+        }
+      } else {                   // The neighbor exists and is not infected.
+        if (neighsh.sh == dummysh) {
+          // There is no subface protecting the neighbor, infect it.
+          infect(neighbor);
+          // Ensure that the neighbor's neighbors will be infected.
+          deadtet = (tetrahedron **) viri->alloc();
+          *deadtet = neighbor.tet;
+        } else {               // The neighbor is protected by a subface.
+          // Remove this tetrahedron from the subface.
+          stdissolve(neighsh);
+          // The subface becomes a boundary.  Set markers accordingly.
+          if (shellmark(neighsh) == 0) {
+            setshellmark(neighsh, 1);
+          }
+        }
+      }
+    }
+    // Remark the tetrahedron as infected, so it doesn't get added to the
+    //   virus pool again.
+    infect(testtet);
+    virusloop = (tetrahedron **) viri->traverse();
+  }
+
+  if (b->verbose) {
+    printf("  Deleting marked tetrahedra.\n");
+  }
+
+  // Create and initialize 'segspernodelist'.
+  tetspernodelist = new int[points->items + 1];
+  for (i = 0; i < points->items + 1; i++) tetspernodelist[i] = 0;
+  
+  // Loop the tetrahedra list, counter the number of tets sharing each node.
+  tetrahedrons->traversalinit();
+  testtet.tet = tetrahedrontraverse();
+  while (testtet.tet != (tetrahedron *) NULL) {
+    // Increment the number of sharing tets for each endpoint.
+    for (i = 0; i < 4; i++) {
+      j = pointmark((point) testtet.tet[4 + i]);
+      tetspernodelist[j]++;
+    }
+    testtet.tet = tetrahedrontraverse();
+  }
+
+  viri->traversalinit();
+  virusloop = (tetrahedron **) viri->traverse();
+  while (virusloop != (tetrahedron **) NULL) {
+    testtet.tet = *virusloop;
+    // Record changes in the number of boundary faces, and disconnect
+    //   dead tetrahedra from their neighbors.
+    for (testtet.loc = 0; testtet.loc < 4; testtet.loc++) {
+      sym(testtet, neighbor);
+      if (neighbor.tet == dummytet) {
+        // There is no neighboring tetrahedron on this face, so this face
+        //   is a boundary face.  This tetrahedron is being deleted, so this
+        //   boundary face is deleted.
+        hullsize--;
+      } else {
+        // Disconnect the tetrahedron from its neighbor.
+        dissolve(neighbor);
+        // There is a neighboring tetrahedron on this face, so this face
+        //   becomes a boundary face when this tetrahedron is deleted.
+        hullsize++;
+      }
+    }
+    // Check the four corners of this tet if they're isolated.
+    for (i = 0; i < 4; i++) {
+      checkpt = (point) testtet.tet[4 + i];
+      j = pointmark(checkpt);
+      tetspernodelist[j]--;
+      if (tetspernodelist[j] == 0) {
+        setpointtype(checkpt, UNUSEDVERTEX);
+      }
+    }
+    // Return the dead tetrahedron to the pool of tetrahedra.
+    tetrahedrondealloc(testtet.tet);
+    virusloop = (tetrahedron **) viri->traverse();
+  }
+  
+  delete [] tetspernodelist;
+  // Empty the virus pool.
+  viri->restart();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// regionplague()    Spread regional attributes and/or volume constraints    //
+//                   (from a .poly file) throughout the mesh.                //
+//                                                                           //
+// This procedure operates in two phases.  The first phase spreads an        //
+// attribute and/or an volume constraint through a (segment-bounded) region. //
+// The tetrahedra are marked to ensure that each tetrahedra is added to the  //
+// virus pool only once, so the procedure will terminate.                    //
+//                                                                           //
+// The second phase uninfects all infected tetrahedra, returning them to     //
+// normal.                                                                   //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::
+regionplague(memorypool *viri, REAL attribute, REAL volume)
+{
+  tetrahedron **virusloop;
+  tetrahedron **regiontet;
+  triface testtet, neighbor;
+  face neighsh;
+
+  if (b->verbose > 1) {
+    printf("  Marking neighbors of marked tetrahedra.\n");
+  }
+  // Loop through all the infected tetrahedra, spreading the attribute
+  //   and/or volume constraint to their neighbors, then to their neighbors'
+  //   neighbors.
+  viri->traversalinit();
+  virusloop = (tetrahedron **) viri->traverse();
+  while (virusloop != (tetrahedron **) NULL) {
+    testtet.tet = *virusloop;
+    // Temporarily uninfect this tetrahedron, not necessary.
+    uninfect(testtet);
+    if (b->regionattrib) {
+      // Set an attribute.
+      setelemattribute(testtet.tet, in->numberoftetrahedronattributes,
+                       attribute);
+    }
+    if (b->varvolume) {
+      // Set an volume constraint.
+      setvolumebound(testtet.tet, volume);
+    }
+    // Check each of the tetrahedron's four neighbors.
+    for (testtet.loc = 0; testtet.loc < 4; testtet.loc++) {
+      // Find the neighbor.
+      sym(testtet, neighbor);
+      // Check for a subface between the tetrahedron and its neighbor.
+      tspivot(testtet, neighsh);
+      // Make sure the neighbor exists, is not already infected, and
+      //   isn't protected by a subface, or is protected by a nonsolid
+      //   subface.
+      if ((neighbor.tet != dummytet) && !infected(neighbor)
+          && (neighsh.sh == dummysh)) {
+        // Infect the neighbor.
+        infect(neighbor);
+        // Ensure that the neighbor's neighbors will be infected.
+        regiontet = (tetrahedron **) viri->alloc();
+        *regiontet = neighbor.tet;
+      }
+    }
+    // Remark the tetrahedron as infected, so it doesn't get added to the
+    //   virus pool again.
+    infect(testtet);
+    virusloop = (tetrahedron **) viri->traverse();
+  }
+
+  // Uninfect all tetrahedra.
+  if (b->verbose > 1) {
+    printf("  Unmarking marked tetrahedra.\n");
+  }
+  viri->traversalinit();
+  virusloop = (tetrahedron **) viri->traverse();
+  while (virusloop != (tetrahedron **) NULL) {
+    testtet.tet = *virusloop;
+    uninfect(testtet);
+    virusloop = (tetrahedron **) viri->traverse();
+  }
+  // Empty the virus pool.
+  viri->restart();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// carveholes()    Find the holes and infect them.  Find the volume          //
+//                 constraints and infect them.  Infect the convex hull.     //
+//                 Spread the infection and kill tetrahedra.  Spread the     //
+//                 volume constraints.                                       //
+//                                                                           //
+// This routine mainly calls other routines to carry out all these functions.//
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::carveholes()
+{
+  memorypool *viri;
+  triface searchtet;
+  triface *holetets;
+  triface *regiontets;
+  tetrahedron *tptr;
+  tetrahedron **holetet;
+  tetrahedron **regiontet;
+  enum locateresult intersect;
+  int i;
+
+  if (!b->quiet) {
+    printf("Removing unwanted tetrahedra.\n");
+    if (b->verbose && (in->numberofholes > 0)) {
+      printf("  Marking holes for elimination.\n");
+    }
+  }
+
+  if (in->numberofholes > 0) {
+    // Allocate storage for the tetrahedra in which hole points fall.
+    holetets = (triface *) new triface[in->numberofholes];
+  }
+  if (in->numberofregions > 0) {
+    // Allocate storage for the tetrahedra in which region points fall.
+    regiontets = (triface *) new triface[in->numberofregions];
+  }
+
+  // Now, we have to find all the holes and regions BEFORE we infect hull
+  //   and carve the holes, because locate() won't work when there exist
+  //   infect tetrahedra and the tetrahedronlization is no longer convex.
+
+  if (in->numberofholes > 0) {
+    // Infect each tetrahedron in which a hole lies.
+    for (i = 0; i < 3 * in->numberofholes; i += 3) {
+      // Ignore holes that aren't within the bounds of the mesh.
+      if ((in->holelist[i] >= xmin) && (in->holelist[i] <= xmax)
+          && (in->holelist[i + 1] >= ymin)
+          && (in->holelist[i + 1] <= ymax)
+          && (in->holelist[i + 2] >= zmin)
+          && (in->holelist[i + 2] <= zmax)) {
+        searchtet.tet = dummytet;
+        // Find a tetrahedron that contains the hole.
+        intersect = locate(&in->holelist[i], &searchtet);
+        if ((intersect != OUTSIDE) && (!infected(searchtet))) {
+          // Record the tetrahedron for processing carve hole.
+          holetets[i / 3] = searchtet;
+        }
+      }
+    }
+  }
+
+  if (in->numberofregions > 0) {
+    // Find the starting tetrahedron for each region.
+    for (i = 0; i < in->numberofregions; i++) {
+      regiontets[i].tet = dummytet;
+      // Ignore region points that aren't within the bounds of the mesh.
+      if ((in->regionlist[5 * i] >= xmin)
+           && (in->regionlist[5 * i] <= xmax)
+           && (in->regionlist[5 * i + 1] >= ymin)
+           && (in->regionlist[5 * i + 1] <= ymax)
+           && (in->regionlist[5 * i + 2] >= zmin)
+           && (in->regionlist[5 * i + 2] <= zmax)) {
+        searchtet.tet = dummytet;
+        // Find a tetrahedron that contains the region point.
+        intersect = locate(&in->regionlist[5 * i], &searchtet);
+        if ((intersect != OUTSIDE) && (!infected(searchtet))) {
+          // Record the tetrahedron for processing after the
+          //   holes have been carved.
+          regiontets[i] = searchtet;
+        }
+      }
+    }
+  }
+
+  // Initialize a pool of viri to be used for holes, concavities,
+  //   regional attributes, and/or regional volume constraints.
+  viri = new memorypool(sizeof(tetrahedron *), 1024, POINTER, 0);
+  // Mark as infected any unprotected tetrahedra on the boundary.
+  //   This is one way by which concavities are created.
+  infecthull(viri);
+
+  if (in->numberofholes > 0) {
+    // Infect the hole tetrahedron.  This is done by marking the
+    //  tetrahedron as infect and including the tetrahedron in
+    //  the virus pool.
+    for (i = 0; i < in->numberofholes; i++) {
+      infect(holetets[i]);
+      holetet = (tetrahedron **) viri->alloc();
+      *holetet = holetets[i].tet;
+    }
+  }
+
+  if (viri->items > 0) {
+    // Carve the holes and concavities.
+    plague(viri);
+  }
+  // The virus pool should be empty now.
+
+  if (in->numberofregions > 0) {
+    if (!b->quiet) {
+      if (b->regionattrib) {
+        if (b->varvolume) {
+          printf("Spreading regional attributes and volume constraints.\n");
+        } else {
+          printf("Spreading regional attributes.\n");
+        }
+      } else {
+        printf("Spreading regional volume constraints.\n");
+      }
+    }
+    if (b->regionattrib && !b->refine) {
+      // Assign every tetrahedron a regional attribute of zero.
+      tetrahedrons->traversalinit();
+      tptr = tetrahedrontraverse();
+      while (tptr != (tetrahedron *) NULL) {
+        setelemattribute(tptr, in->numberoftetrahedronattributes, 0.0);
+        tptr = tetrahedrontraverse();
+      }
+    }
+    for (i = 0; i < in->numberofregions; i++) {
+      if (regiontets[i].tet != dummytet) {
+        // Make sure the tetrahedron under consideration still exists.
+        //   It may have been eaten by the virus.
+        if (!isdead(&(regiontets[i]))) {
+          // Put one tetrahedron in the virus pool.
+          infect(regiontets[i]);
+          regiontet = (tetrahedron **) viri->alloc();
+          *regiontet = regiontets[i].tet;
+          // Apply one region's attribute and/or volume constraint.
+          regionplague(viri, in->regionlist[5 * i + 3],
+                       in->regionlist[5 * i + 4]);
+          // The virus pool should be empty now.
+        }
+      }
+    }
+    if (b->regionattrib && !b->refine) {
+      // Note the fact that each tetrahedron has an additional attribute.
+      in->numberoftetrahedronattributes++;
+    }
+  }
+
+  // Free up memory.
+  delete viri;
+  if (in->numberofholes > 0) {
+    delete [] holetets;
+  }
+  if (in->numberofregions > 0) {
+    delete [] regiontets;
+  }
+}
+
+//
+// End of carving out holes and concavities routines
+//
+
+//
+// Begin of mesh update routines
+//
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// reconstructmesh()    Reconstruct a tetrahedral mesh from a list of        //
+//                      tetrahedra and possibly a list of boundary faces.    //
+//                                                                           //
+// The list of tetrahedra is stored in 'in->tetrahedronlist',  the list of   //
+// boundary faces is stored in 'in->trifacelist'.  The tetrahedral mesh is   //
+// reconstructed in memorypool 'tetrahedrons', its boundary faces (subfaces) //
+// are reconstructed in 'subfaces', its boundary edges (subsegments) are     //
+// reconstructed in 'subsegs'. If the -a switch is used, this procedure will //
+// also read a list of REALs from 'in->tetrahedronvolumelist' and set a      //
+// maximum volume constraint on each tetrahedron.                            //
+//                                                                           //
+// If the user has provided the boundary faces in 'in->trifacelist', they    //
+// will be inserted the mesh. Otherwise subfaces will be identified from the //
+// mesh.  All hull faces (including faces of the internal holes) will be     //
+// recognized as subfaces, internal faces between two tetrahedra which have  //
+// different attributes will also be recognized as subfaces.                 //
+//                                                                           //
+// Subsegments will be identified after subfaces are reconstructed. Edges at //
+// the intersections of non-coplanar subfaces are recognized as subsegments. //
+// Edges between two coplanar subfaces with different boundary markers are   //
+// also recognized as subsegments.                                           //
+//                                                                           //
+// The facet index of each subface will be set automatically after we have   //
+// recovered subfaces and subsegments.  That is, the set of subfaces, which  //
+// are coplanar and have the same boundary marker will be recognized as a    //
+// facet and has a unique index, stored as the facet marker in each subface  //
+// of the set, the real boundary marker of each subface will be found in     //
+// 'in->facetmarkerlist' by the index.  Facet index will be used in Delaunay //
+// refinement for detecting two incident facets.                             //
+//                                                                           //
+// Points which are not corners of tetrahedra will be inserted into the mesh.//
+// Return the number of faces on the hull after the reconstruction.          //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+long tetgenmesh::reconstructmesh()
+{
+  tetrahedron **tetsperverlist;
+  shellface **facesperverlist;
+  triface tetloop, neightet, neineightet, spintet;
+  face subloop, neighsh, neineighsh, subseg;
+  face sface1, sface2;
+  point *idx2verlist;
+  point torg, tdest, tapex, toppo;
+  point norg, ndest, napex;
+  list *neighshlist, *markerlist;
+  REAL sign, attrib, volume;
+  REAL da1, da2;
+  bool bondflag, insertsegflag;
+  int *idx2tetlist;
+  int *idx2facelist;
+  int *worklist;
+  int facetidx, marker;
+  int iorg, idest, iapex, ioppo;
+  int inorg, indest, inapex;
+  int index, i, j;
+
+  if (!b->quiet) {
+    printf("Reconstructing mesh.\n");
+  }
+
+  // Create a map from index to points.
+  makeindex2pointmap(idx2verlist);
+
+  // Create the tetrahedra.
+  for (i = 0; i < in->numberoftetrahedra; i++) {
+    // Create a new tetrahedron and set its four corners, make sure that
+    //   four corners form a positive orientation.
+    maketetrahedron(&tetloop);
+    index = i * in->numberofcorners;
+    // Although there may be 10 nodes, we only read the first 4.
+    iorg = in->tetrahedronlist[index] - in->firstnumber;
+    idest = in->tetrahedronlist[index + 1] - in->firstnumber;
+    iapex = in->tetrahedronlist[index + 2] - in->firstnumber;
+    ioppo = in->tetrahedronlist[index + 3] - in->firstnumber;
+    torg = idx2verlist[iorg];
+    tdest = idx2verlist[idest];
+    tapex = idx2verlist[iapex];
+    toppo = idx2verlist[ioppo];
+    sign = orient3d(torg, tdest, tapex, toppo);
+    if (sign > 0.0) {
+      norg = torg; torg = tdest; tdest = norg;
+    } else if (sign == 0.0) {
+      printf("Warning:  Tetrahedron %d is degenerate.\n", i + in->firstnumber);
+    }
+    setorg(tetloop, torg);
+    setdest(tetloop, tdest);
+    setapex(tetloop, tapex);
+    setoppo(tetloop, toppo);
+    // Temporarily set the vertices be type FREEVOLVERTEX, to indicate that
+    //   they belong to the mesh.  These types may be changed later.
+    setpointtype(torg, FREEVOLVERTEX);
+    setpointtype(tdest, FREEVOLVERTEX);
+    setpointtype(tapex, FREEVOLVERTEX);
+    setpointtype(toppo, FREEVOLVERTEX);
+    // Set element attributes if they exist.
+    for (j = 0; j < in->numberoftetrahedronattributes; j++) {
+      index = i * in->numberoftetrahedronattributes;
+      attrib = in->tetrahedronattributelist[index + j];
+      setelemattribute(tetloop.tet, j, attrib);
+    }
+    // If -a switch is used (with no number follows) Set a volume
+    //   constraint if it exists.
+    if (b->varvolume) {
+      if (in->tetrahedronvolumelist != (REAL *) NULL) {
+        volume = in->tetrahedronvolumelist[i];
+      } else {
+        volume = -1.0;
+      }
+      setvolumebound(tetloop.tet, volume);
+    }
+  }
+
+  // Set the connection between tetrahedra.
+  hullsize = 0l;
+  // Create a map from nodes to tetrahedra.
+  maketetrahedronmap(idx2tetlist, tetsperverlist);
+  // Initialize the worklist.
+  worklist = new int[points->items];
+  for (i = 0; i < points->items; i++) {
+    worklist[i] = 0;
+  }
+
+  // Loop all tetrahedra, bond two tetrahedra if they share a common face.
+  tetrahedrons->traversalinit();
+  tetloop.tet = tetrahedrontraverse();
+  while (tetloop.tet != (tetrahedron *) NULL) {
+    // Loop the four sides of the tetrahedron.
+    for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) {
+      sym(tetloop, neightet);
+      if (neightet.tet != dummytet) continue; // This side has finished.
+      torg = org(tetloop);
+      tdest = dest(tetloop);
+      tapex = apex(tetloop);
+      iorg = pointmark(torg) - in->firstnumber;
+      idest = pointmark(tdest) - in->firstnumber;
+      iapex = pointmark(tapex) - in->firstnumber;
+      worklist[iorg] = 1;
+      worklist[idest] = 1;
+      worklist[iapex] = 1;
+      bondflag = false;
+      // Search its neighbor in the adjacent tets of torg.
+      for (j = idx2tetlist[iorg]; j < idx2tetlist[iorg + 1] && !bondflag; 
+           j++) {
+        if (tetsperverlist[j] == tetloop.tet) continue; // Skip myself.
+        neightet.tet = tetsperverlist[j];
+        for (neightet.loc = 0; neightet.loc < 4; neightet.loc++) {
+          sym(neightet, neineightet);
+          if (neineightet.tet == dummytet) {
+            norg = org(neightet);
+            ndest = dest(neightet);
+            napex = apex(neightet);
+            inorg = pointmark(norg) - in->firstnumber;
+            indest = pointmark(ndest) - in->firstnumber;
+            inapex = pointmark(napex) - in->firstnumber;
+            if ((worklist[inorg] + worklist[indest] + worklist[inapex]) == 3) {
+              // Find! Bond them together and break the loop.
+              bond(tetloop, neightet);
+              bondflag = true;
+              break;
+            }
+          }
+        }
+      }
+      if (!bondflag) {
+        hullsize++;  // It's a hull face.
+        // Bond this side to outer space.
+        dummytet[0] = encode(tetloop);
+        if (in->pointmarkerlist != (int *) NULL) {
+          // Set its three corners's markers be boundary (hull) vertices.
+          if (in->pointmarkerlist[iorg] == 0) {
+            in->pointmarkerlist[iorg] = 1;
+          }
+          if (in->pointmarkerlist[idest] == 0) {
+            in->pointmarkerlist[idest] = 1;
+          }
+          if (in->pointmarkerlist[iapex] == 0) {
+            in->pointmarkerlist[iapex] = 1;
+          }
+        }
+      }
+      worklist[iorg] = 0;
+      worklist[idest] = 0;
+      worklist[iapex] = 0;
+    }
+    tetloop.tet = tetrahedrontraverse();
+  }
+
+  // Subfaces will be inserted into the mesh.
+  if (in->trifacelist != (int *) NULL) {
+    // Recover subfaces from 'in->trifacelist'.
+    for (i = 0; i < in->numberoftrifaces; i++) {
+      index = i * 3;
+      iorg = in->trifacelist[index] - in->firstnumber;
+      idest = in->trifacelist[index + 1] - in->firstnumber;
+      iapex = in->trifacelist[index + 2] - in->firstnumber;
+      // Look for the location of this subface.
+      worklist[iorg] = 1;
+      worklist[idest] = 1;
+      worklist[iapex] = 1;
+      bondflag = false;
+      // Search its neighbor in the adjacent tets of torg.
+      for (j = idx2tetlist[iorg]; j < idx2tetlist[iorg + 1] && !bondflag; 
+           j++) {
+        neightet.tet = tetsperverlist[j];
+        for (neightet.loc = 0; neightet.loc < 4; neightet.loc++) {
+          norg = org(neightet);
+          ndest = dest(neightet);
+          napex = apex(neightet);
+          inorg = pointmark(norg) - in->firstnumber;
+          indest = pointmark(ndest) - in->firstnumber;
+          inapex = pointmark(napex) - in->firstnumber;
+          if ((worklist[inorg] + worklist[indest] + worklist[inapex]) == 3) {
+            bondflag = true;  // Find!
+            break;
+          }
+        }
+      }
+      if (bondflag) {
+        // Create a new subface and insert it into the mesh.
+        makeshellface(subfaces, &subloop);
+        torg = idx2verlist[iorg];
+        tdest = idx2verlist[idest];
+        tapex = idx2verlist[iapex];
+        setsorg(subloop, torg);
+        setsdest(subloop, tdest);
+        setsapex(subloop, tapex);
+        // Set the vertices be FREESUBVERTEX to indicate they belong to a
+        //   facet of the domain.  They may be changed later.
+        setpointtype(torg, FREESUBVERTEX);
+        setpointtype(tdest, FREESUBVERTEX);
+        setpointtype(tapex, FREESUBVERTEX);
+        if (in->trifacemarkerlist != (int *) NULL) {
+          setshellmark(subloop, in->trifacemarkerlist[i]);
+        }
+        adjustedgering(neightet, CCW);
+        findedge(&subloop, org(neightet), dest(neightet));
+        tsbond(neightet, subloop);
+        sym(neightet, neineightet);
+        if (neineightet.tet != dummytet) {
+          sesymself(subloop);
+          tsbond(neineightet, subloop);
+        }
+      } else {
+        printf("Warning:  Subface %d is discarded.\n", i + in->firstnumber);
+      }
+      worklist[iorg] = 0;
+      worklist[idest] = 0;
+      worklist[iapex] = 0;
+    }
+  } else {
+    // Indentify subfaces from the mesh.
+    tetrahedrons->traversalinit();
+    tetloop.tet = tetrahedrontraverse();
+    while (tetloop.tet != (tetrahedron *) NULL) {
+      // Loop the four sides of the tetrahedron.
+      for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) {
+        tspivot(tetloop, subloop);
+        if (subloop.sh != dummysh) continue;
+        bondflag = false;
+        sym(tetloop, neightet);
+        if (neightet.tet == dummytet) {
+          // It's a hull face. Insert a subface at here.
+          bondflag = true;
+        } else {
+          // It's an interior face. Insert a subface if two tetrahedra have
+          //   different attributes (i.e., they belong to two regions).
+          if (in->numberoftetrahedronattributes > 0) {
+            if (elemattribute(neightet.tet,
+                in->numberoftetrahedronattributes - 1) != 
+                elemattribute(tetloop.tet,
+                in->numberoftetrahedronattributes - 1)) {
+              bondflag = true;
+            }
+          }
+        }
+        if (bondflag) {
+          adjustedgering(tetloop, CCW);
+          makeshellface(subfaces, &subloop);
+          torg = org(tetloop);
+          tdest = dest(tetloop);
+          tapex = apex(tetloop);
+          setsorg(subloop, torg);
+          setsdest(subloop, tdest);
+          setsapex(subloop, tapex);
+          // Set the vertices be FACETVERTEX to indicate they belong to a
+          //   facet of the domain.  They may be changed later.
+          setpointtype(torg, FACETVERTEX);
+          setpointtype(tdest, FACETVERTEX);
+          setpointtype(tapex, FACETVERTEX);
+          tsbond(tetloop, subloop);
+          if (neightet.tet != dummytet) {
+            sesymself(subloop);
+            tsbond(neightet, subloop);
+          }
+        }
+      }
+      tetloop.tet = tetrahedrontraverse();
+    }
+  }
+
+  // Set the connection between subfaces. An subsegment may have more than
+  //   two subfaces sharing it, 'neighshlist' stores all subfaces sharing
+  //   one edge.
+  neighshlist = new list(sizeof(face), NULL);
+  // Create a map from nodes to subfaces.
+  makesubfacemap(idx2facelist, facesperverlist);
+
+  // Loop over the set of subfaces, setup the connection between subfaces.
+  subfaces->traversalinit();
+  subloop.sh = shellfacetraverse(subfaces);
+  while (subloop.sh != (shellface *) NULL) {
+    for (i = 0; i < 3; i++) {
+      spivot(subloop, neighsh);
+      if (neighsh.sh == dummysh) {
+        // This side is 'empty', operate on it.
+        torg = sorg(subloop);
+        tdest = sdest(subloop);
+        tapex = sapex(subloop);
+        neighshlist->append(&subloop);
+        iorg = pointmark(torg) - in->firstnumber;
+        // Search its neighbor in the adjacent list of torg.
+        for (j = idx2facelist[iorg]; j < idx2facelist[iorg + 1]; j++) {
+          neighsh.sh = facesperverlist[j];
+          if (neighsh.sh == subloop.sh) continue;
+          neighsh.shver = 0;
+          if (isfacehasedge(&neighsh, torg, tdest)) {
+            findedge(&neighsh, torg, tdest);
+            // Insert 'neighsh' into 'neighshlist'.
+            if (neighshlist->len() < 2) {
+              neighshlist->append(&neighsh);
+            } else {
+              for (index = 0; index < neighshlist->len() - 1; index++) {
+                sface1 = * (face *)(* neighshlist)[index];
+                sface2 = * (face *)(* neighshlist)[index + 1];
+                da1 = facedihedral(torg, tdest, sapex(sface1), sapex(neighsh));
+                da2 = facedihedral(torg, tdest, sapex(sface1), sapex(sface2));
+                if (da1 < da2) {
+                  break;  // Insert it after index.
+                }
+              }
+              neighshlist->insert(index + 1, &neighsh);
+            }
+          }
+        }
+        // Bond the subfaces in 'neighshlist'. 
+        if (neighshlist->len() > 1) {
+          neighsh = * (face *)(* neighshlist)[0];
+          for (j = 1; j <= neighshlist->len(); j++) {
+            if (j < neighshlist->len()) {
+              neineighsh = * (face *)(* neighshlist)[j];
+            } else {
+              neineighsh = * (face *)(* neighshlist)[0];
+            }
+            sbond1(neighsh, neineighsh);
+            neighsh = neineighsh;
+          }
+        } else {
+          // No neighbor subface be found, bond 'subloop' to itself.
+          sbond(subloop, subloop);
+        }
+        neighshlist->clear();
+      }
+      senextself(subloop);
+    }
+    subloop.sh = shellfacetraverse(subfaces);
+  }
+
+  // Subsegments will be introudced.
+  subfaces->traversalinit();
+  subloop.sh = shellfacetraverse(subfaces);
+  while (subloop.sh != (shellface *) NULL) {
+    for (i = 0; i < 3; i++) {
+      sspivot(subloop, subseg);
+      if (subseg.sh == dummysh) {
+        // This side has no subsegment bonded, check it.
+        torg = sorg(subloop);
+        tdest = sdest(subloop);
+        tapex = sapex(subloop);
+        spivot(subloop, neighsh);
+        spivot(neighsh, neineighsh);
+        insertsegflag = false;
+        if (subloop.sh == neighsh.sh || subloop.sh != neineighsh.sh) {
+          // This side is either self-bonded or more than two subfaces,
+          //   insert a subsegment at this side.
+          insertsegflag = true;
+        } else {
+          // Only two subfaces case.
+          assert(subloop.sh != neighsh.sh);
+          napex = sapex(neighsh);
+          sign = orient3d(torg, tdest, tapex, napex);
+          if (iscoplanar(torg, tdest, tapex, napex, sign, b->epsilon)) {
+            // Although they are coplanar, we still need to check if they
+            //   have the same boundary marker.
+            insertsegflag = (shellmark(subloop) != shellmark(neighsh));
+          } else {
+            // Non-coplanar.
+            insertsegflag = true;
+          }
+        }
+        if (insertsegflag) {
+          // Create a subsegment at this side.
+          makeshellface(subsegs, &subseg);
+          setsorg(subseg, torg);
+          setsdest(subseg, tdest);
+          // At the moment, all segment vertices have type FACETVERTEX.
+          //   They will be set to type ACUTEVERTEX or NONACUTEVERTEX by
+          //   routine markacutevertices() later.
+          // setpointtype(torg, SEGMENTVERTEX);
+          // setpointtype(tdest, SEGMENTVERTEX);
+          // Bond all subfaces to this subsegment.
+          neighsh = subloop;
+          do {
+            ssbond(neighsh, subseg);
+            spivotself(neighsh);
+          } while (neighsh.sh != subloop.sh);
+        }
+      }
+      senextself(subloop);
+    }
+    subloop.sh = shellfacetraverse(subfaces);
+  }
+  // Remember the number of input segments.
+  insegment = subsegs->items;
+  // Find the acute vertices and set them be type ACUTEVERTEX.
+
+  // Indentify the facet and set facet index for each subface.
+  markerlist = new list("int");
+  
+  subfaces->traversalinit();
+  subloop.sh = shellfacetraverse(subfaces);
+  while (subloop.sh != (shellface *) NULL) {
+    // Only operate on uninfected subface, after operating, infect it.
+    if (!sinfected(subloop)) {
+      // A new facet has found.
+      marker = shellmark(subloop);
+      markerlist->append(&marker);
+      facetidx = markerlist->len(); // 'facetidx' starts from 1.
+      setshellmark(subloop, facetidx);
+      sinfect(subloop);
+      neighshlist->append(&subloop);
+      // Find out all subfaces of this facet (bounded by subsegments).
+      for (i = 0; i < neighshlist->len(); i++) {
+        neighsh = * (face *) (* neighshlist)[i];
+        for (j = 0; j < 3; j++) {
+          sspivot(neighsh, subseg);
+          if (subseg.sh == dummysh) {
+            spivot(neighsh, neineighsh);
+            if (!sinfected(neineighsh)) {
+              // 'neineighsh' is in the same facet as 'subloop'.
+              assert(shellmark(neineighsh) == marker);
+              setshellmark(neineighsh, facetidx);
+              sinfect(neineighsh);
+              neighshlist->append(&neineighsh);
+            }
+          }
+          senextself(neighsh);
+        }
+      }
+      neighshlist->clear();
+    }
+    subloop.sh = shellfacetraverse(subfaces);
+  }
+  // Save the facet markers in 'in->facetmarkerlist'.
+  in->numberoffacets = markerlist->len();
+  in->facetmarkerlist = new int[in->numberoffacets];
+  for (i = 0; i < in->numberoffacets; i++) {
+    marker = * (int *) (* markerlist)[i];
+    in->facetmarkerlist[i] = marker;
+  }
+  // Uninfect all subfaces.
+  subfaces->traversalinit();
+  subloop.sh = shellfacetraverse(subfaces);
+  while (subloop.sh != (shellface *) NULL) {
+    assert(sinfected(subloop));
+    suninfect(subloop);
+    subloop.sh = shellfacetraverse(subfaces);
+  }
+
+  // The mesh contains boundary now.
+  checksubfaces = 1;
+
+  if (b->quality) {
+    // Check and recover the Delaunay property.
+    queue* flipqueue = new queue(sizeof(badface)); 
+    checkdelaunay(flipqueue);
+    if (!flipqueue->empty()) {
+      // Call flip algorithm to recover Delaunayness.
+      flip(flipqueue, NULL); 
+    }
+    delete flipqueue;
+  }
+
+  delete markerlist;
+  delete neighshlist;
+  delete [] worklist;
+  delete [] idx2tetlist;
+  delete [] tetsperverlist;
+  delete [] idx2facelist;
+  delete [] facesperverlist;
+  delete [] idx2verlist;
+  
+  return hullsize;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// insertaddpoints()    Insert additional points in 'in->addpointlist'.      //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::insertaddpoints()
+{
+  queue *flipqueue;
+  triface searchtet;
+  face checksh, checkseg;
+  point newpoint;
+  point p1, p2, p3, p4;
+  enum locateresult loc;
+  REAL ori;
+  int ptmark;
+  int index;
+  int i, j;
+  
+  if (!b->quiet) {
+    printf("Insert additional points into mesh.\n");
+  }
+  // Initialize 'flipqueue'.
+  flipqueue = new queue(sizeof(badface));
+  recenttet.tet = dummytet;
+
+  index = 0;
+  for (i = 0; i < in->numberofaddpoints; i++) {
+    // Create a newpoint.
+    newpoint = (point) points->alloc();
+    newpoint[0] = in->addpointlist[index++];
+    newpoint[1] = in->addpointlist[index++];
+    newpoint[2] = in->addpointlist[index++];
+    for (j = 0; j < in->numberofpointattributes; j++) {
+      newpoint[3 + j] = 0.0;
+    }
+    // Remember the point index (starts from 'in->firstnumber').
+    ptmark = (int) points->items - (in->firstnumber == 1 ? 0 : 1);
+    setpointmark(newpoint, ptmark);
+    // Find the location of the inserted point.
+    searchtet = recenttet;
+    loc = locate(newpoint, &searchtet);
+    if (loc != OUTSIDE) {
+      if (loc != ONVERTEX) {
+        loc = adjustlocate(newpoint, &searchtet, loc, b->epsilon);
+      }
+    }
+    if (loc == OUTSIDE) {
+      // Perform a brute-force search.
+      tetrahedrons->traversalinit();
+      searchtet.tet = tetrahedrontraverse();
+      while (searchtet.tet != (tetrahedron *) NULL) {
+        p1 = (point) searchtet.tet[4];
+        p2 = (point) searchtet.tet[5];
+        p3 = (point) searchtet.tet[6];
+        p4 = (point) searchtet.tet[7];
+        ori = orient3d(p2, p1, p3, newpoint);
+        if (ori >= 0) {
+          ori = orient3d(p1, p2, p4, newpoint);
+          if (ori >= 0) {
+            ori = orient3d(p2, p3, p4, newpoint);
+            if (ori >= 0) {
+              ori = orient3d(p3, p1, p4, newpoint);
+              if (ori >= 0) {
+                // 'newpoint' lies inside, or on a face, or on an edge, or
+                //   a vertex of 'searchtet'.
+                loc = adjustlocate(newpoint, &searchtet, OUTSIDE, b->epsilon);
+                if (loc != OUTSIDE) break;
+              }
+            }
+          }
+        }
+        searchtet.tet = tetrahedrontraverse();
+      }
+    }
+    // Insert the point if it not lies outside or on a vertex.
+    switch (loc) {
+    case INTETRAHEDRON:
+      setpointtype(newpoint, FREEVOLVERTEX);
+      splittetrahedron(newpoint, &searchtet, flipqueue);
+      break;
+    case ONFACE:
+      tspivot(searchtet, checksh);
+      if (checksh.sh != dummysh) {
+        setpointtype(newpoint, FREESUBVERTEX);
+      } else {
+        setpointtype(newpoint, FREEVOLVERTEX);
+      }
+      splittetface(newpoint, &searchtet, flipqueue);
+      break;
+    case ONEDGE:
+      tsspivot(&searchtet, &checkseg);
+      if (checkseg.sh != dummysh) {
+        setpointtype(newpoint, FREESEGVERTEX);
+      } else {
+        tspivot(searchtet, checksh);
+        if (checksh.sh != dummysh) {
+          setpointtype(newpoint, FREESUBVERTEX);
+        } else {
+          setpointtype(newpoint, FREEVOLVERTEX);
+        }
+      }
+      splittetedge(newpoint, &searchtet, flipqueue);
+      break;
+    case ONVERTEX:
+      if (b->verbose) {
+        printf("Warning: Point (%.17g, %.17g, %.17g) falls on a vertex.\n",
+               newpoint[0], newpoint[1], newpoint[2]);
+      }
+      break;
+    case OUTSIDE:
+      if (b->verbose) {
+        printf("Warning: Point (%.17g, %.17g, %.17g) lies outside the mesh.\n",
+               newpoint[0], newpoint[1], newpoint[2]);
+      }
+      break;
+    }
+    // Remember the tetrahedron for next point searching.
+    recenttet = searchtet;
+    if (loc == ONVERTEX || loc == OUTSIDE) {
+      pointdealloc(newpoint);
+    } else {
+      flip(flipqueue, NULL);
+    }
+  }
+
+  delete flipqueue;
+}
+
+//
+// End of mesh update routines
+//
+
+//
+// Begin of Delaunay refinement routines
+//
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// initializerpsarray()    Calculate the initial radii of protecting spheres //
+//                         of all acute vertices, save in 'rpsarray'.        //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::initializerpsarray(REAL* rpsarray)
+{
+  list *neightetlist;
+  tetrahedron tetptr;
+  triface starttet, neightet;
+  point pointloop, workpt[3];
+  REAL rps, len;
+  int index, i, j;  
+
+  if (b->verbose) {
+    printf("  Initializing protecting spheres.\n");
+  }
+
+  // Initialize the point2tet field of each point.
+  points->traversalinit();
+  pointloop = pointtraverse();
+  while (pointloop != (point) NULL) {
+    setpoint2tet(pointloop, (tetrahedron) NULL);
+    pointloop = pointtraverse();
+  }
+  // Construct a map from points to tetrahedra.
+  makepoint2tetmap();
+  // Initialize 'neightetlist'.
+  neightetlist = new list(sizeof(triface), NULL, 256);
+  
+  points->traversalinit();
+  pointloop = pointtraverse();
+  while (pointloop != (point) NULL) {
+    tetptr = point2tet(pointloop);
+    // Only calculate lfs(p) if it is acute and is not dangling.
+    if ((pointtype(pointloop) == ACUTEVERTEX) &&
+        (tetptr != (tetrahedron) NULL)) {
+      decode(tetptr, starttet);
+      assert((starttet.tet != NULL) && (starttet.tet != dummytet));
+      // Find all tetrahedra sharing 'pointloop'.
+      findorg(&starttet, pointloop);
+      infect(starttet);
+      neightetlist->append(&starttet);
+      for (i = 0; i < neightetlist->len(); i++) {
+        starttet = * (triface *)(* neightetlist)[i];
+        assert(infected(starttet));
+        // The origin of 'starttet' should be 'pointloop'.
+        adjustedgering(starttet, CCW);
+        if (org(starttet) != pointloop) {
+          enextself(starttet);
+        }
+        assert(org(starttet) == pointloop);
+        // Let 'starttet' be the opposite face of 'pointloop'.
+        enextfnextself(starttet);
+        assert(oppo(starttet) == pointloop);
+        // Get three neighbors of faces having 'pointloop'.
+        adjustedgering(starttet, CCW);
+        for (j = 0; j < 3; j++) {
+          fnext(starttet, neightet);
+          symself(neightet);
+          // Add it into list if is is not outer space and not infected.
+          if ((neightet.tet != dummytet) && !infected(neightet)) {
+            findorg(&neightet, pointloop);
+            infect(neightet);
+            neightetlist->append(&neightet);
+          }
+          enextself(starttet);
+        }
+      }
+      // 'neightetlist' contains all tetrahedra sharing at 'pointloop'. Get
+      //   the shortest edge length of edges sharing at 'pointloop'.
+      rps = longest;
+      for (i = 0; i < neightetlist->len(); i++) {
+        starttet = * (triface *)(* neightetlist)[i];
+        assert(org(starttet) == pointloop);
+        workpt[0] = dest(starttet);
+        workpt[1] = apex(starttet);
+        workpt[2] = oppo(starttet);
+        for (j = 0; j < 3; j++) {
+          len = distance(workpt[j], pointloop);
+          if (pointtype(workpt[j]) == ACUTEVERTEX) {
+            len /= 3.0;
+          } else {
+            len /= 2.0;
+          }
+          if (len < rps) rps = len;
+        }
+      }
+      // Uninfect tetrahedra and clear 'neightetlist'.
+      for (i = 0; i < neightetlist->len(); i++) {
+        starttet = * (triface *)(* neightetlist)[i];
+        uninfect(starttet);
+      }
+      neightetlist->clear();
+    } else {
+      // A non-acute or dangling vertex.
+      rps = 0.0;
+    }
+    // Return the local feature size of pointloop.
+    index = pointmark(pointloop) - in->firstnumber;
+    rpsarray[index] = rps;
+    pointloop = pointtraverse();
+  }
+
+  delete neightetlist;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// marksharpfacets()    Make a map of facets which form sharp corners.       //
+//                                                                           //
+// A sharp corner between two facets has a dihedral angle smaller than the   //
+// 'dihedbound' (in degrees).  The map is returned in an integer array       //
+// 'idx2facetlist'.  If idx2facetlist[facetidx - 1] is '1', it means that    //
+// facet form a sharp corner with other facet.                               //
+//                                                                           //
+// NOTE: idx2facetlist is created inside this routine, don't forget to free  //
+// it after using.                                                           //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::marksharpfacets(int*& idx2facetlist, REAL dihedbound)
+{
+  list *incishlist;
+  triface adjtet; 
+  face segloop, prevseg, checkseg;
+  face subloop, parentsh, spinsh;
+  face neighsh, checksh;
+  point eorg, edest; 
+  REAL anglebound, angle;
+  int facetidx;
+  int i, j;
+
+  if (b->verbose) {
+    printf("  Marking facets have sharp corners.\n");
+  }
+
+  anglebound = dihedbound * 3.1415926535897932 / 180.;
+  // Create and initialize 'idx2facetlist'.
+  idx2facetlist = new int[in->numberoffacets + 1];
+  for (i = 0; i < in->numberoffacets + 1; i++) idx2facetlist[i] = 0;
+  // A list keeps incident and not co-facet subfaces around a subsegment.
+  incishlist = new list(sizeof(face), NULL);
+
+  // Loop the set of subsegments once, counter the number of incident
+  //   facets of each facet.
+  subsegs->traversalinit();
+  segloop.sh = shellfacetraverse(subsegs);
+  while (segloop.sh != (shellface *) NULL) {
+    // A subsegment may be split into many pieces, we only need one piece
+    //   for getting the incident facets.  Only operate on the one which
+    //   contains the origin of the unsplit subsegment.
+    segloop.shver = 0;
+    senext2(segloop, prevseg);
+    spivotself(prevseg);
+    if (prevseg.sh == dummysh) {
+      // Operate on this subsegment.
+      segloop.shver = 0;
+      spivot(segloop, parentsh);
+      assert(parentsh.sh != dummysh);
+      spivot(parentsh, spinsh);
+      if (spinsh.sh != parentsh.sh) {
+        // This subface is not self-bonded.
+        eorg = sorg(segloop);
+        edest = sdest(segloop);
+        // Get all incident subfaces around 'segloop'.
+        spinsh = parentsh;
+        do {
+          if (sorg(spinsh) != eorg) {
+            sesymself(spinsh);
+          }
+          incishlist->append(&spinsh);  
+          spivotself(spinsh);
+        } while (spinsh.sh != parentsh.sh);
+        // Check the pair of adjacent subfaces for small angle.
+        spinsh = * (face *)(* incishlist)[0];
+        for (i = 1; i <= incishlist->len(); i++) {
+          if (i == incishlist->len()) {
+            neighsh = * (face *)(* incishlist)[0];
+          } else {
+            neighsh = * (face *)(* incishlist)[i];
+          }
+          // Only do test when the side spinsh is faceing inward.
+          stpivot(spinsh, adjtet);
+          if (adjtet.tet != dummytet) {
+            angle = facedihedral(eorg, edest, sapex(spinsh), sapex(neighsh));
+            if (angle < anglebound) {
+              facetidx = shellmark(spinsh);
+              idx2facetlist[facetidx - 1] = 1;
+              facetidx = shellmark(neighsh);
+              idx2facetlist[facetidx - 1] = 1;
+            }
+          }
+          spinsh = neighsh;
+        }
+        incishlist->clear();
+      }
+    }
+    segloop.sh = shellfacetraverse(subsegs);
+  }
+
+  // Ensure all the sharp facets are marked.  The mergefacet() operation
+  //   may leave several facets having different markers merged.
+  incishlist->clear();
+  subfaces->traversalinit();
+  subloop.sh = shellfacetraverse(subfaces);
+  while (subloop.sh != (shellface *) NULL) {
+    // Only operate on sharp and unmarked subfaces.
+    facetidx = shellmark(subloop);
+    if (!sinfected(subloop) && (idx2facetlist[facetidx - 1] == 1)) {
+      sinfect(subloop);
+      incishlist->append(&subloop);
+      // Find out all subfaces of this facet (bounded by subsegments).
+      for (i = 0; i < incishlist->len(); i++) {
+        neighsh = * (face *) (* incishlist)[i];
+        for (j = 0; j < 3; j++) {
+          sspivot(neighsh, checkseg);
+          if (checkseg.sh == dummysh) {
+            spivot(neighsh, checksh);
+            if (!sinfected(checksh)) {
+              // 'checksh' is in the same facet as 'subloop'.
+              sinfect(checksh);
+              // Check if it is marked.
+              facetidx = shellmark(checksh);
+              if (idx2facetlist[facetidx - 1] == 0) {
+                idx2facetlist[facetidx - 1] = 1;
+              }
+              incishlist->append(&checksh);
+            }
+          }
+          senextself(neighsh);
+        }
+      }
+      incishlist->clear();
+    }
+    subloop.sh = shellfacetraverse(subfaces);
+  }
+  // Uninfect all sharp subfaces.
+  subfaces->traversalinit();
+  subloop.sh = shellfacetraverse(subfaces);
+  while (subloop.sh != (shellface *) NULL) {
+    if (sinfected(subloop)) {
+      facetidx = shellmark(subloop);
+      assert(idx2facetlist[facetidx - 1] == 1);
+      suninfect(subloop);
+    }
+    subloop.sh = shellfacetraverse(subfaces);
+  }
+
+  delete incishlist;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// enqueuebadtet()    Add a bad tetrahedron to the end of a queue.           //
+//                                                                           //
+// The queue is actually a set of 64 queues.                                 //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::
+enqueuebadtet(triface *instet, REAL ratio, point insorg, point insdest,
+              point insapex, point insoppo, point inscent)
+{
+  badtetrahedron *newtet;
+  int queuenumber;
+
+  // Allocate space for the bad tetrahedron.
+  newtet = (badtetrahedron *) badtetrahedrons->alloc();
+  newtet->tet = *instet;
+  newtet->key = ratio;
+  newtet->cent[0] = inscent[0];
+  newtet->cent[1] = inscent[1];
+  newtet->cent[2] = inscent[2];
+  newtet->tetorg = insorg;
+  newtet->tetdest = insdest;
+  newtet->tetapex = insapex;
+  newtet->tetoppo = insoppo;
+  newtet->nexttet = (badtetrahedron *) NULL;
+  // Determine the appropriate queue to put the bad tetrahedron into.
+  if (ratio > b->goodratio) {
+    queuenumber = (int) ((ratio - b->goodratio) / 0.5);
+    // 'queuenumber' may overflow (negative) caused by a very large ratio.
+    if ((queuenumber > 63) || (queuenumber < 0)) {
+      queuenumber = 63;
+    }
+  } else {
+    // It's not a bad ratio; put the tet in the lowest-priority queue.
+    queuenumber = 0;
+  }
+  // Add the tetrahedron to the end of a queue.
+  *tetquetail[queuenumber] = newtet;
+  // Maintain a pointer to the NULL pointer at the end of the queue.
+  tetquetail[queuenumber] = &newtet->nexttet;
+
+  if (b->verbose > 2) {
+    printf("    Queueing bad tet: (%d, %d, %d, %d), ratio %g, qnum %d.\n",
+           pointmark(insorg), pointmark(insdest), pointmark(insapex),
+           pointmark(insoppo), sqrt(ratio), queuenumber);
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// dequeuebadtet()    Remove a tetrahedron from the front of the queue.      //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+tetgenmesh::badtetrahedron* tetgenmesh::dequeuebadtet()
+{
+  badtetrahedron *result;
+  int queuenumber;
+
+  // Look for a nonempty queue.
+  for (queuenumber = 63; queuenumber >= 0; queuenumber--) {
+    result = tetquefront[queuenumber];
+    if (result != (badtetrahedron *) NULL) {
+      // Remove the tetrahedron from the queue.
+      tetquefront[queuenumber] = result->nexttet;
+      // Maintain a pointer to the NULL pointer at the end of the queue.
+      if (tetquefront[queuenumber] == (badtetrahedron *) NULL) {
+        tetquetail[queuenumber] = &tetquefront[queuenumber];
+      }
+      return result;
+    }
+  }
+  return (badtetrahedron *) NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// checkseg4encroach()    Check a subsegment to see if it is encroached.     //
+//                                                                           //
+// A subsegment is encroached if there is a vertex in its diametral circle   //
+// (that is, the subsegment faces an angle greater than 90 degrees).         //
+//                                                                           //
+// If 'testpt' is not NULL, only check whether 'testsubseg' is encroached by //
+// it or not. Otherwise, check all apexes of faces containing 'testsubseg',  //
+// to see if there is one encroaches it.                                     //
+//                                                                           //
+// If 'enqueueflag' is TRUE, add 'testsubseg' to queue 'badsubsegs' if it is //
+// encroached.                                                               //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::
+checkseg4encroach(face* testsubseg, point testpt, bool enqueueflag)
+{
+  badface *encsubseg;
+  triface starttet, spintet;
+  point eorg, edest, eapex, encpt;
+  REAL cent[3], radius, dist, diff;
+  bool enq;
+  int hitbdry;
+
+  eorg = sorg(*testsubseg);
+  edest = sdest(*testsubseg);
+  cent[0] = 0.5 * (eorg[0] + edest[0]);
+  cent[1] = 0.5 * (eorg[1] + edest[1]);
+  cent[2] = 0.5 * (eorg[2] + edest[2]);
+  radius = distance(cent, eorg);
+
+  enq = false;
+  encpt = (point) NULL;
+  if (testpt == (point) NULL) {
+    // Check if it is encroached by traversing all faces containing it.
+    sstpivot(testsubseg, &starttet);
+    eapex = apex(starttet);
+    spintet = starttet;
+    hitbdry = 0;
+    do {
+      dist = distance(cent, apex(spintet));
+      diff = dist - radius;
+      if (fabs(diff) / radius <= b->epsilon) diff = 0.0; // Rounding.
+      if (diff < 0.0) {
+        enq = true;
+        encpt = apex(spintet);
+        break;
+      }
+      if (!fnextself(spintet)) {
+        hitbdry++;
+        if (hitbdry < 2) {
+          esym(starttet, spintet);
+          if (!fnextself(spintet)) {
+            hitbdry++;
+          } 
+        }
+      }
+    } while (apex(spintet) != eapex && (hitbdry < 2));
+  } else {
+    // Only check if 'testsubseg' is encroached by 'testpt'.
+    dist = distance(cent, testpt);
+    diff = dist - radius;
+    if (fabs(diff) / radius <= b->epsilon) diff = 0.0; // Rounding.
+    if (diff < 0.0) {
+      enq = true;
+    }
+  }
+
+  if (enq && enqueueflag) {
+    if (b->verbose > 2) {
+      printf("    Queuing encroaching subsegment (%d, %d).\n",
+             pointmark(eorg), pointmark(edest));
+    }
+    encsubseg = (badface *) badsubsegs->alloc();
+    encsubseg->ss = *testsubseg;
+    encsubseg->forg = eorg;
+    encsubseg->fdest = edest;
+    encsubseg->foppo = encpt;
+    // Set the pointer of 'encsubseg' into 'testseg'.  It has two purposes:
+    //   (1) We can regonize it is encroached; (2) It is uniquely queued.
+    setshell2badface(encsubseg->ss, encsubseg);
+  }
+  
+  return enq;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// checksub4encroach()    Check a subface to see if it is encroached. If so, //
+//                        add it to the list.                                //
+//                                                                           //
+// A subface is encroached if there is a vertex in its diametral sphere. If  //
+// 'testpt != NULL', only test if 'testsub' is encroached by it.  Otherwise, //
+// test the opposites of the adjoining tetrahedra of 'testsub' at both side  //
+// to see whether it is encroached or not.  If 'enqueueflag = TRUE', add     //
+// 'testsub' into pool 'badsubfaces'. Return TRUE if 'testsub' is encroached,//
+// return FALSE if it is not.                                                //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::
+checksub4encroach(face* testsub, point testpt, bool enqueueflag)
+{
+  badface *encsub;
+  triface abuttet;
+  point forg, fdest, fapex, encpt;
+  REAL cent[3], radius, dist, diff;
+  bool enq, bqual, ncollinear;
+  int quenumber, i;
+
+  enq = false;
+  encpt = (point) NULL;
+  bqual = checksub4badqual(testsub);
+
+  if (!bqual) {
+    forg = sorg(*testsub);
+    fdest = sdest(*testsub);
+    fapex = sapex(*testsub);
+    ncollinear = circumsphere(forg, fdest, fapex, NULL, cent, &radius);
+    assert(ncollinear == true);
+    
+    if (testpt == (point) NULL) {
+      stpivot(*testsub, abuttet);
+      if (abuttet.tet != dummytet) {
+        dist = distance(cent, oppo(abuttet));
+        diff = dist - radius;
+        if (fabs(diff) / radius <= b->epsilon) diff = 0.0; // Rounding.
+        enq = diff < 0.0;
+        if (enq) encpt = oppo(abuttet);
+      }
+      if (!enq) {
+        sesymself(*testsub);
+        stpivot(*testsub, abuttet);
+        if (abuttet.tet != dummytet) {
+          dist = distance(cent, oppo(abuttet));
+          diff = dist - radius;
+          if (fabs(diff) / radius <= b->epsilon) diff = 0.0; // Rounding.
+          enq = diff < 0.0;
+          if (enq) encpt = oppo(abuttet);
+        }
+      }
+    } else {
+      // Only do test when 'testpt' is one of its corners.
+      if (testpt != forg && testpt != fdest && testpt != fapex) {
+        dist = distance(cent, testpt);
+        diff = dist - radius;
+        if (fabs(diff) / radius <= b->epsilon) diff = 0.0; // Rounding.
+        enq = diff < 0.0;
+      }
+    }
+  }
+
+  if ((enq || bqual) && enqueueflag) {    
+    encsub = (badface *) badsubfaces->alloc();
+    encsub->ss = *testsub;
+    encsub->forg = sorg(*testsub);
+    encsub->fdest = sdest(*testsub);
+    encsub->fapex = sapex(*testsub);
+    encsub->foppo = encpt;
+    if (enq) {
+      for (i = 0; i < 3; i++) encsub->cent[i] = cent[i];
+    } else {
+      for (i = 0; i < 3; i++) encsub->cent[i] = 0.0;
+    }
+    encsub->nextface = (badface *) NULL;
+    // Set the pointer of 'encsubseg' into 'testsub'.  It has two purposes:
+    //   (1) We can regonize it is encroached; (2) It is uniquely queued.
+    setshell2badface(encsub->ss, encsub);
+    quenumber = bqual ? 1 : 0;
+    // Add the subface to the end of a queue.
+    *subquetail[quenumber] = encsub;
+    // Maintain a pointer to the NULL pointer at the end of the queue.
+    subquetail[quenumber] = &encsub->nextface;
+    if (b->verbose > 2) {
+      printf("    Queuing %s subface (%d, %d, %d).\n", 
+             enq ? "encroached" : "badqual", pointmark(encsub->forg),
+             pointmark(encsub->fdest), pointmark(encsub->fapex));
+    }
+  }
+
+  return enq || bqual;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// checksub4badquality()    Test if the quality of a subface is bad.         //
+//                                                                           //
+// A subface has bad quality if: (1) its minimum internal angle is smaller   //
+// than 20 degree; or (2) its area is larger than a maximum area condition.  //
+// Return TRUE if it is bad.                                                 //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::checksub4badqual(face* testsub)
+{
+  face sametestsub;
+  face subseg1, subseg2;
+  point torg, tdest, tapex;
+  point anglevertex;
+  REAL dxod, dyod, dzod;
+  REAL dxda, dyda, dzda;
+  REAL dxao, dyao, dzao;
+  REAL dxod2, dyod2, dzod2;
+  REAL dxda2, dyda2, dzda2;
+  REAL dxao2, dyao2, dzao2;
+  REAL apexlen, orglen, destlen;
+  REAL angle, area;
+  bool enq;
+
+  enq = false;
+  torg = sorg(*testsub);
+  tdest = sdest(*testsub);
+  tapex = sapex(*testsub);
+  dxod = torg[0] - tdest[0];
+  dyod = torg[1] - tdest[1];
+  dzod = torg[2] - tdest[2];
+  dxda = tdest[0] - tapex[0];
+  dyda = tdest[1] - tapex[1];
+  dzda = tdest[2] - tapex[2];
+  dxao = tapex[0] - torg[0];
+  dyao = tapex[1] - torg[1];
+  dzao = tapex[2] - torg[2];
+  dxod2 = dxod * dxod;
+  dyod2 = dyod * dyod;
+  dzod2 = dzod * dzod;
+  dxda2 = dxda * dxda;
+  dyda2 = dyda * dyda;
+  dzda2 = dzda * dzda;
+  dxao2 = dxao * dxao;
+  dyao2 = dyao * dyao;
+  dzao2 = dzao * dzao;
+  // Find the lengths of the triangle's three edges.
+  apexlen = dxod2 + dyod2 + dzod2;
+  orglen = dxda2 + dyda2 + dzda2;
+  destlen = dxao2 + dyao2 + dzao2;
+  if ((apexlen < orglen) && (apexlen < destlen)) {
+    // The edge opposite the apex is shortest.
+    // Find the square of the cosine of the angle at the apex.
+    angle = dxda * dxao + dyda * dyao + dzda * dzao;
+    angle = angle * angle / (orglen * destlen);
+    anglevertex = tapex;
+    senext(*testsub, sametestsub);
+    sspivot(sametestsub, subseg1);
+    senext2(*testsub, sametestsub);
+    sspivot(sametestsub, subseg2);
+  } else if (orglen < destlen) {
+    // The edge opposite the origin is shortest.
+    // Find the square of the cosine of the angle at the origin.
+    angle = dxod * dxao + dyod * dyao + dzod * dzao;
+    angle = angle * angle / (apexlen * destlen);
+    anglevertex = torg;
+    sspivot(*testsub, subseg1);
+    senext2(*testsub, sametestsub);
+    sspivot(sametestsub, subseg2);
+  } else {
+    // The edge opposite the destination is shortest.
+    // Find the square of the cosine of the angle at the destination.
+    angle = dxod * dxda + dyod * dyda + dzod * dzda;
+    angle = angle * angle / (apexlen * orglen);
+    anglevertex = tdest;
+    sspivot(*testsub, subseg1);
+    senext(*testsub, sametestsub);
+    sspivot(sametestsub, subseg2);
+  }
+
+  // Check if both edges that form the angle are segments.
+  if ((subseg1.sh != dummysh) && (subseg2.sh != dummysh)) {
+    // The angle is a segment intersection.  Don't add this bad subface to
+    //   the list; there's nothing that can be done about a small angle
+    //   between two segments.
+    angle = 0.0;
+  } else if (pointtype(anglevertex) == ACUTEVERTEX) {
+    // If the small angle vertex is acute, do not refine this face.
+    angle = 0.0;
+  }
+
+  // Check whether the angle is smaller than permitted.
+  if (angle > b->goodangle) {
+    enq = true;
+  }
+
+  if (!enq && areabound(*testsub) > 0.0) {
+    // Check whether the area is larger than desired.  A variation form of
+    //   Heron's formula which only uses the squares of the edge lengthes
+    //   is used to calculated the area of a 3D triangle.
+    area = apexlen + orglen - destlen;
+    area = area * area;
+    area = 4 * apexlen * orglen - area;
+    area = 0.25 * sqrt(fabs(area));
+    if (area > areabound(*testsub)) {
+      enq = true;
+    }
+  }
+
+  return enq;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// checktet4badqual()    Test a tetrahedron for quality measures.            //
+//                                                                           //
+// Tests a tetrahedron to see if it satisfies the minimum ratio condition    //
+// and the maximum volume condition. Tetrahedra that aren't upto spec are    //
+// added to the bad tetrahedron queue.                                       //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::checktet4badqual(triface* testtet)
+{
+  point torg, tdest, tapex, toppo;
+  REAL dxod, dyod, dzod, dxda, dyda, dzda, dxao, dyao, dzao;
+  REAL dxop, dyop, dzop, dxdp, dydp, dzdp, dxap, dyap, dzap;
+  REAL dxod2, dyod2, dzod2, dxda2, dyda2, dzda2, dxao2, dyao2, dzao2;
+  REAL dxop2, dyop2, dzop2, dxdp2, dydp2, dzdp2, dxap2, dyap2, dzap2;
+  REAL dxoc, dyoc, dzoc, dxoc2, dyoc2, dzoc2;
+  REAL edgelen[6], cent[3];
+  REAL smedgelen, averlen, volume;
+  REAL radius, ratio2;
+  int i;
+
+  torg = org(*testtet);
+  tdest = dest(*testtet);
+  tapex = apex(*testtet);
+  toppo = oppo(*testtet);
+
+  dxod = torg[0] - tdest[0];
+  dyod = torg[1] - tdest[1];
+  dzod = torg[2] - tdest[2];
+  dxda = tdest[0] - tapex[0];
+  dyda = tdest[1] - tapex[1];
+  dzda = tdest[2] - tapex[2];
+  dxao = tapex[0] - torg[0];
+  dyao = tapex[1] - torg[1];
+  dzao = tapex[2] - torg[2];
+
+  dxop = torg[0] - toppo[0];
+  dyop = torg[1] - toppo[1];
+  dzop = torg[2] - toppo[2];
+  dxdp = tdest[0] - toppo[0];
+  dydp = tdest[1] - toppo[1];
+  dzdp = tdest[2] - toppo[2];
+  dxap = tapex[0] - toppo[0];
+  dyap = tapex[1] - toppo[1];
+  dzap = tapex[2] - toppo[2];
+
+  dxod2 = dxod * dxod;
+  dyod2 = dyod * dyod;
+  dzod2 = dzod * dzod;
+  dxda2 = dxda * dxda;
+  dyda2 = dyda * dyda;
+  dzda2 = dzda * dzda;
+  dxao2 = dxao * dxao;
+  dyao2 = dyao * dyao;
+  dzao2 = dzao * dzao;
+
+  dxop2 = dxop * dxop;
+  dyop2 = dyop * dyop;
+  dzop2 = dzop * dzop;
+  dxdp2 = dxdp * dxdp;
+  dydp2 = dydp * dydp;
+  dzdp2 = dzdp * dzdp;
+  dxap2 = dxap * dxap;
+  dyap2 = dyap * dyap;
+  dzap2 = dzap * dzap;
+
+  // Find the smallest edge length of 'testtet'.
+  edgelen[0] = dxod2 + dyod2 + dzod2;
+  edgelen[1] = dxda2 + dyda2 + dzda2;
+  edgelen[2] = dxao2 + dyao2 + dzao2;
+  edgelen[3] = dxop2 + dyop2 + dzop2;
+  edgelen[4] = dxdp2 + dydp2 + dzdp2;
+  edgelen[5] = dxap2 + dyap2 + dzap2;
+  smedgelen = averlen = edgelen[0];
+  for (i = 1; i < 6; i++) {
+    averlen += sqrt(edgelen[i]);
+    if (smedgelen > edgelen[i]) {
+      smedgelen = edgelen[i];
+    }
+  }
+  averlen /= 6.0;
+
+  // Find the circumcenter and circumradius of 'testtet'.
+  circumsphere(torg, tdest, tapex, toppo, cent, NULL);
+  dxoc = torg[0] - cent[0];
+  dyoc = torg[1] - cent[1];
+  dzoc = torg[2] - cent[2];
+  dxoc2 = dxoc * dxoc;
+  dyoc2 = dyoc * dyoc;
+  dzoc2 = dzoc * dzoc;
+  radius = dxoc2 + dyoc2 + dzoc2;
+  
+  // Calculate the square of radius-edge ratio.
+  ratio2 = radius / smedgelen;
+
+  // Check whether the ratio is smaller than permitted.
+  if (ratio2 > b->goodratio) {
+    // Add this tet to the list of bad tetrahedra.
+    enqueuebadtet(testtet, ratio2, torg, tdest, tapex, toppo, cent);
+    return true;
+  }
+  if (b->varvolume || b->fixedvolume) {
+    volume = orient3d(torg, tdest, tapex, toppo);
+    if (volume < 0) volume = -volume;
+    volume /= 6.0;
+    // Check whether the volume is larger than permitted.
+    if (b->fixedvolume && (volume > b->maxvolume)) {
+      // Add this tetrahedron to the list of bad tetrahedra.
+      enqueuebadtet(testtet, 0, torg, tdest, tapex, toppo, cent);
+      return true;
+    } else if (b->varvolume) {
+      // Nonpositive volume constraints are treated as unconstrained.
+      if ((volume > volumebound(testtet->tet)) &&
+          (volumebound(testtet->tet) > 0.0)) {
+        // Add this tetrahedron to the list of bad tetrahedron.
+        enqueuebadtet(testtet, 0, torg, tdest, tapex, toppo, cent);
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// checktet4illtet()    Test a tetrahedron to see if it is illegal.          //
+//                                                                           //
+// A tetrahedron is assumed as illegal if its four corners are defined on    //
+// one facet. A tetrahedron has zero volume is illegal.  Add it into queue   //
+// if it is illegal.                                                         //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::checktet4illtet(triface* testtet, list* illtetlist)
+{
+  badtetrahedron *badtet;
+  triface neighface;
+  face testsh, neighsh, testseg;
+  bool isill;
+  int i;
+
+  isill = false;
+  testtet->loc = 0;
+  testtet->ver = 0;
+  tspivot(*testtet, testsh); // edge ab
+  if (testsh.sh != dummysh) {
+    // Check subfaces at edges ab, bc, ca.
+    findedge(&testsh, org(*testtet), dest(*testtet));
+    for (i = 0; i < 3; i++) {
+      sspivot(testsh, testseg);
+      if (testseg.sh == dummysh) {
+        fnext(*testtet, neighface);
+        tspivot(neighface, neighsh);
+        if (neighsh.sh != dummysh) {
+          isill = true;
+          break;
+        }
+      }
+      enextself(*testtet);
+      senextself(testsh);
+    }
+  }
+  if (!isill) {
+    testtet->loc = 0;
+    testtet->ver = 0;
+    fnextself(*testtet);
+    esymself(*testtet);
+    tspivot(*testtet, testsh);
+    if (testsh.sh != dummysh) {
+      // Check subfaces at edges ad, db.
+      findedge(&testsh, org(*testtet), dest(*testtet));
+      for (i = 0; i < 2; i++) {
+        enextself(*testtet);
+        senextself(testsh);
+        sspivot(testsh, testseg);
+        if (testseg.sh == dummysh) {
+          fnext(*testtet, neighface);
+          tspivot(neighface, neighsh);
+          if (neighsh.sh != dummysh) {
+            isill = true;
+            break;
+          }
+        }
+      }
+    }
+  }
+  if (!isill) {
+    testtet->loc = 0;
+    testtet->ver = 0;
+    enextfnextself(*testtet);
+    esymself(*testtet);
+    enext2self(*testtet);  // edge cd.
+    tspivot(*testtet, testsh);
+    if (testsh.sh != dummysh) {
+      findedge(&testsh, org(*testtet), dest(*testtet));
+      sspivot(testsh, testseg);
+      if (testseg.sh == dummysh) {
+        fnext(*testtet, neighface);
+        tspivot(neighface, neighsh);
+        if (neighsh.sh != dummysh) {
+          isill = true;
+        }
+      }
+    }
+  }
+  if (isill) {
+    badtet = (badtetrahedron *) illtetlist->append(NULL);
+    badtet->tet = *testtet;
+    badtet->tetorg = org(*testtet);
+    badtet->tetdest = dest(*testtet);
+    badtet->tetapex = apex(*testtet);
+    badtet->tetoppo = oppo(*testtet);
+    if (b->verbose > 2) {
+      printf("    Queuing illtet (%d, %d, %d, %d).\n",
+             pointmark(badtet->tetorg), pointmark(badtet->tetdest),
+             pointmark(badtet->tetapex), pointmark(badtet->tetoppo));
+    }
+  }
+  return isill;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// checktet4sliver()    Test a tetrahedron for large dihedral angle.         //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::checktet4sliver(triface* testtet, list* illtetlist)
+{
+  badtetrahedron *badtet;
+  point pa, pb, pc, pd;
+  REAL dihed[6];
+  bool issliver;
+  
+  pa = (point) testtet->tet[4];
+  pb = (point) testtet->tet[5];
+  pc = (point) testtet->tet[6];
+  pd = (point) testtet->tet[7];
+  tetalldihedral(pa, pb, pc, pd, dihed);
+  
+  issliver = false;
+  testtet->loc = 0;
+  testtet->ver = 0;
+  if (dihed[0] > b->maxdihedral) { // Edge ab
+    issliver = true;
+  } 
+  if (!issliver && (dihed[1] > b->maxdihedral)) { // Edge ac
+    enext2self(*testtet);
+    issliver = true;
+  } 
+  if (!issliver && (dihed[2] > b->maxdihedral)) { // Edge ad
+    fnextself(*testtet);
+    enext2self(*testtet);
+    esymself(*testtet);
+    issliver = true;
+  } 
+  if (!issliver && (dihed[3] > b->maxdihedral)) { // Edge bc
+    enextself(*testtet);
+    issliver = true;
+  } 
+  if (!issliver && (dihed[4] > b->maxdihedral)) { // Edge bd
+    fnextself(*testtet);
+    enextself(*testtet);
+    esymself(*testtet);
+    issliver = true;
+  } 
+  if (!issliver && (dihed[5] > b->maxdihedral)) { // Edge cd
+    enextfnextself(*testtet);
+    enextself(*testtet);
+    esymself(*testtet);
+    issliver = true;
+  }
+
+  if (issliver) {
+    badtet = (badtetrahedron *) illtetlist->append(NULL);
+    badtet->tet = *testtet;
+    badtet->tetorg = org(*testtet);
+    badtet->tetdest = dest(*testtet);
+    badtet->tetapex = apex(*testtet);
+    badtet->tetoppo = oppo(*testtet);
+    if (b->verbose > 2) {
+      printf("    Queuing sliver (%d, %d, %d, %d).\n",
+             pointmark(badtet->tetorg), pointmark(badtet->tetdest),
+             pointmark(badtet->tetapex), pointmark(badtet->tetoppo));
+    }
+  }
+  return issliver;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// checkseg4splitting()    Check an encroached subsegment to see if it is    //
+//                         suitable to be split.                             //
+//                                                                           //
+// If a subsegment is one of edges of a subface which is on the sharp corner,//
+// it is not suitable to be split. If the volume constraint is set, it is    //
+// still suitable to be split if there is a tetrahedron around it which has  //
+// volume larger than volumebound. To avoid resulting too skinny tetrahedron,//
+// we compare the longest edge length to the cubic root of volumebound.      //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::checkseg4splitting(face* testseg, REAL* rpsarray, bool bqual)
+{
+  triface spintet;
+  face parentsh, spinsh;
+  point eorg, edest, fapex;
+  bool acuteorg, acutedest;
+  REAL rpslimit;
+  REAL L, L3;
+  int ptidx;
+
+  eorg = sorg(*testseg);
+  edest = sdest(*testseg);
+  acuteorg = pointtype(eorg) == ACUTEVERTEX;
+  acutedest = pointtype(edest) == ACUTEVERTEX;
+  if ((acuteorg && acutedest) || (!acuteorg && !acutedest)) {
+    // Can be split.
+    return true;
+  }
+  // Now exactly one vertex is acute.
+  assert(acuteorg || acutedest);
+  if (!bqual) {
+    // We're not forced to split it. However, if it is encroached by an
+    //   existing vertex, we must split it, otherwise, not split it.
+    return checkseg4encroach(testseg, NULL, false);
+  }
+
+  L = distance(eorg, edest);
+  if (acuteorg) {
+    ptidx = pointmark(eorg) - in->firstnumber;
+  } else {
+    assert(acutedest);
+    ptidx = pointmark(edest) - in->firstnumber;
+  }
+  rpslimit = rpsarray[ptidx] / 4.0;
+  if (L > (rpslimit * 1.1)) {
+    // The edge is not too small, can be split.
+    return true;
+  }
+  // L <= rpslimit.  We should not split it. However, it may still be
+  //   split if its length is too long wrt. the volume constraints.
+  if (b->varvolume || b->fixedvolume) {
+    L3 = L * L * L / 6.0;
+    if (b->fixedvolume && (L3 > b->maxvolume)) {
+      // This edge is too long wrt. the maximum volume bound. Split it.
+      return true; 
+    } 
+    if (b->varvolume) {
+      spivot(*testseg, parentsh);
+      if (sorg(parentsh) != eorg) sesymself(parentsh);
+      stpivot(parentsh, spintet);
+      if (spintet.tet == dummytet) {
+        sesymself(parentsh);
+        stpivot(parentsh, spintet);
+        assert(spintet.tet != dummytet);
+      }
+      findedge(&spintet, eorg, edest);
+      fapex = apex(spintet);
+      while (true) {
+        if (!fnextself(spintet)) {
+          // Meet a boundary, walk through it.
+          tspivot(spintet, spinsh);
+          assert(spinsh.sh != dummysh);
+          findedge(&spinsh, eorg, edest);
+          sfnextself(spinsh);
+          stpivot(spinsh, spintet);
+          assert(spintet.tet != dummytet);
+          findedge(&spintet, eorg, edest);
+        }
+        if ((L3 > volumebound(spintet.tet)) && 
+            (volumebound(spintet.tet) > 0.0)) {
+          // This edge is too long wrt. the maximum volume bound. Split it.
+          return true; 
+        }
+        if (apex(spintet) == fapex) break;
+      }
+    }
+  }
+
+  // Not split it.
+  return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// checksub4splitting()    Check an encroached subface to see if it is       //
+//                         suitable to be split.                             //
+//                                                                           //
+// If a subface is on the sharp corner, it is not suitable to be split. If   //
+// the volume constraint is set, it is still suitable to be split if there   //
+// is a tetrahedron around it which has volume larger than volumebound. To   //
+// avoid resulting too skinny tet, we compare the longest edge length to the //
+// cubic root of volumebound.                                                //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::checksub4splitting(face* testsh)
+{
+  triface testtet;
+  point p[3];
+  REAL L, L3;
+  int i;
+
+  if (b->varvolume || b->fixedvolume) {
+    // Check if all the tetrahedra having this subface are conforming to
+    //   the volume bound specified in b.maxvolume. Here we don't use each
+    //   tetrahedron's volume for comparsion, instead is an approximate
+    //   volume (one sixth of the cubic of its longest edge length). We
+    //   hope this way can find skinny tetrahedra and split them.
+    p[0] = sorg(*testsh);
+    p[1] = sdest(*testsh);
+    p[2] = sapex(*testsh);
+    // Get the longest edge length of testsh = L.
+    L = distance(p[0], p[1]);
+    L3 = distance(p[1], p[2]);
+    L = (L >= L3 ? L : L3);
+    L3 = distance(p[2], p[0]);
+    L = (L >= L3 ? L : L3);
+
+    L3 = L * L * L / 6.0;
+    if (b->fixedvolume && (L3 > b->maxvolume)) {
+      // This face is too large wrt. the maximum volume bound. Split it.
+      return true; 
+    }
+    if (b->varvolume) {
+      for (i = 0; i < 2; i ++) {
+        stpivot(*testsh, testtet);
+        if (testtet.tet != dummytet) {
+          if ((L3 > volumebound(testtet.tet)) && 
+              (volumebound(testtet.tet) > 0.0)) {
+            // This face is too large wrt. the maximum volume bound.
+            return true;
+          }
+        }
+        sesymself(*testsh);
+      }
+    }
+  }
+  return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// doqualchecktetlist()    Put bad-quality tetrahedra in 'qualchecktetlist'  //
+//                         into queue and clear it.                          //
+//                                                                           //
+// 'qualchecktetlist' stores a list of tetrahedra which are possibly bad-    //
+// quality, furthermore, one tetrahedron may appear many times in it.  For   //
+// testing and queuing each bad-quality tetrahedron only once, infect it     //
+// after testing, later on, only test the one which is not infected.  On     //
+// finish, uninfect them.                                                    //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::doqualchecktetlist()
+{
+  triface testtet;
+  int i;
+
+  for (i = 0; i < qualchecktetlist->len(); i++) {
+    testtet = * (triface *) (* qualchecktetlist)[i];
+    if (!isdead(&testtet) && !infected(testtet)) {
+      checktet4badqual(&testtet);
+      infect(testtet);
+    }
+  }
+  for (i = 0; i < qualchecktetlist->len(); i++) {
+    testtet = * (triface *) (* qualchecktetlist)[i];
+    if (!isdead(&testtet) && infected(testtet)) {
+      uninfect(testtet);
+    }
+  }
+  qualchecktetlist->clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// tallencsegs()    Check for encroached segments, save them in list.        //
+//                                                                           //
+// If both 'testpt' and 'cavtetlist' are not NULLs, then check the segments  //
+// in 'cavtetlist' to see if they're encroached by 'testpt'.  Otherwise,     //
+// check the entire list of segments to see if they're encroached by any of  //
+// mesh vertices.                                                            //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::tallencsegs(point testpt, list *cavtetlist)
+{
+  triface starttet, neightet;
+  face checkseg;
+  long oldencnum;
+  int i, j;
+  
+  // Remember the current number of encroached segments.
+  oldencnum = badsubsegs->items;
+
+  if (cavtetlist != (list *) NULL) {
+    assert(testpt != (point) NULL);
+    // Check segments in the list of tetrahedra.
+    for (i = 0; i < cavtetlist->len(); i++) {
+      starttet = * (triface *)(* cavtetlist)[i];
+      infect(starttet); // Indicate it has been tested.
+      sym(starttet, neightet);
+      if (!infected(neightet)) {
+        // Test all three edges of this face.
+        for (j = 0; j < 3; j++) {
+          tsspivot(&starttet, &checkseg);
+          if (checkseg.sh != dummysh) {
+            if (!shell2badface(checkseg)) {
+              checkseg4encroach(&checkseg, testpt, true);
+            }
+          }
+          enextself(starttet);
+        }
+      }
+      adjustedgering(starttet, CCW);
+      fnext(starttet, neightet);
+      symself(neightet);
+      if ((neightet.tet == dummytet) || !infected(neightet)) {
+        fnext(starttet, neightet);
+        // Test the tow other edges of this face.
+        for (j = 0; j < 2; j++) {
+          enextself(neightet);
+          tsspivot(&neightet, &checkseg);
+          if (checkseg.sh != dummysh) {
+            if (!shell2badface(checkseg)) {
+              checkseg4encroach(&checkseg, testpt, true);
+            }
+          }
+        }
+      }
+      enextfnext(starttet, neightet);
+      symself(neightet);
+      if ((neightet.tet == dummytet) || !infected(neightet)) {
+        enextfnext(starttet, neightet);
+        // Only test the next edge of this face.
+        enextself(neightet);
+        tsspivot(&neightet, &checkseg);
+        if (checkseg.sh != dummysh) {
+          if (!shell2badface(checkseg)) {
+            checkseg4encroach(&checkseg, testpt, true);
+          }
+        }
+      }
+    }
+    // Uninfect all tetrahedra in the list.
+    for (i = 0; i < cavtetlist->len(); i++) {
+      starttet = * (triface *)(* cavtetlist)[i];
+      assert(infected(starttet));
+      uninfect(starttet);
+    }
+  } else {
+    // Check the entire list of segments.
+    subsegs->traversalinit();
+    checkseg.sh = shellfacetraverse(subsegs);
+    while (checkseg.sh != (shellface *) NULL) {
+      checkseg4encroach(&checkseg, NULL, true);
+      checkseg.sh = shellfacetraverse(subsegs);
+    }
+  }
+
+  return (badsubsegs->items > oldencnum);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// tallencsubs()    Find all encroached subfaces and save them in list.      //
+//                                                                           //
+// If 'cavtetlist' and 'testpt' are not NULL, only check subfaces which are  //
+// in cavtetlist. Otherwise check all subfaces in current mesh. If 'protonly'//
+// is TRUE, only check subfaces which are in protecting cylinders & spheres. //
+// Return TRUE if at least one encorached subface is found.                  //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+bool tetgenmesh::tallencsubs(point testpt, list* cavtetlist)
+{
+  triface starttet, neightet;
+  face checksh;
+  long oldencnum;
+  int i, j;
+  
+  // Remember the current number of encroached segments.
+  oldencnum = badsubfaces->items;
+
+  if (cavtetlist != (list *) NULL) {
+    assert(testpt != (point) NULL);
+    // Check subfaces in the list of tetrahedra.
+    for (i = 0; i < cavtetlist->len(); i++) {
+      starttet = * (triface *)(* cavtetlist)[i];
+      infect(starttet); // Indicate it has been tested.
+      sym(starttet, neightet);
+      if (!infected(neightet)) {
+        // Test if this face is encroached.
+        tspivot(starttet, checksh);
+        if (checksh.sh != dummysh) {
+          // If it is not encroached, test it.
+          if (shell2badface(checksh) == NULL) {
+            checksub4encroach(&checksh, testpt, true);
+          }
+        }
+      }
+      adjustedgering(starttet, CCW);
+      // Check the other three sides of this tet.
+      for (j = 0; j < 3; j++) {
+        fnext(starttet, neightet);
+        symself(neightet);
+        if ((neightet.tet == dummytet) || !infected(neightet)) {
+          fnext(starttet, neightet);
+          // Test if this face is encroached.
+          tspivot(neightet, checksh);
+          if (checksh.sh != dummysh) {
+            // If it is not encroached, test it.
+            if (shell2badface(checksh) == NULL) {
+              checksub4encroach(&checksh, testpt, true);
+            }
+          }
+        }
+        enextself(starttet);
+      }
+    }
+    // Uninfect all tetrahedra in the list.
+    for (i = 0; i < cavtetlist->len(); i++) {
+      starttet = * (triface *)(* cavtetlist)[i];
+      assert(infected(starttet));
+      uninfect(starttet);
+    }
+  } else {
+    // Check the entire list of subfaces.
+    subfaces->traversalinit();
+    checksh.sh = shellfacetraverse(subfaces);
+    while (checksh.sh != (shellface *) NULL) {
+      // If it is not encroached, test it.
+      checksub4encroach(&checksh, NULL, true);
+      checksh.sh = shellfacetraverse(subfaces);
+    }
+  }
+
+  return (badsubfaces->items > oldencnum);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// tallbadtetrahedrons()    Test every tetrahedron in the mesh for quality   //
+//                          measures.                                        //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::tallbadtetrahedrons()
+{
+  triface tetloop;
+
+  tetrahedrons->traversalinit();
+  tetloop.tet = tetrahedrontraverse();
+  while (tetloop.tet != (tetrahedron *) NULL) {
+    checktet4badqual(&tetloop);
+    tetloop.tet = tetrahedrontraverse();
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// tallilltets()    Test every tetrahedron in the mesh for illegal tet.      //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::tallilltets(list* illtetlist)
+{
+  triface tetloop;
+  REAL bakdihedral;
+
+  bakdihedral = b->maxdihedral;
+  b->maxdihedral = 3.1415926535897932 * (1.0 - b->epsilon);
+
+  tetrahedrons->traversalinit();
+  tetloop.tet = tetrahedrontraverse();
+  while (tetloop.tet != (tetrahedron *) NULL) {
+    if (!checktet4illtet(&tetloop, illtetlist)) {
+      checktet4sliver(&tetloop, illtetlist);
+    }
+    tetloop.tet = tetrahedrontraverse();
+  }
+
+  b->maxdihedral = bakdihedral;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// tallslivers()    Test every tetrahedron in the mesh for sliver checking.  //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::tallslivers(list* illtetlist)
+{
+  triface tetloop;
+
+  tetrahedrons->traversalinit();
+  tetloop.tet = tetrahedrontraverse();
+  while (tetloop.tet != (tetrahedron *) NULL) {
+    checktet4sliver(&tetloop, illtetlist);
+    tetloop.tet = tetrahedrontraverse();
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// removeilltets()    Repair mesh by removing illegal elements.              //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::removeilltets()
+{
+  badtetrahedron *badtet;
+  list *illtetlist;
+  queue *flipqueue;
+  int i;
+
+  if (!b->quiet) {
+    printf("Removing illegal tetrahedra.\n");
+  }
+  // Initialize the pool of bad tetrahedra.
+  illtetlist = new list(sizeof(badtetrahedron), NULL, 1024);
+  // Initialize 'flipqueue'.
+  flipqueue = new queue(sizeof(badface));  
+  // Initialize the pool of recently flipped faces.
+  flipstackers = new memorypool(sizeof(flipstacker), 1024, POINTER, 0);
+
+  // Test all tetrahedra to see if they're slivers.
+  tallilltets(illtetlist);
+  do {
+    for (i = 0; i < illtetlist->len(); i++) {
+      badtet = (badtetrahedron *)(* illtetlist)[i];
+      if (!isdead(&badtet->tet) && (org(badtet->tet) == badtet->tetorg) &&
+          (dest(badtet->tet) == badtet->tetdest) &&
+          (apex(badtet->tet) == badtet->tetapex) &&
+          (oppo(badtet->tet) == badtet->tetoppo)) {
+        removebadtet(ILLEGAL, &badtet->tet, flipqueue);
+      }
+    }
+    illtetlist->clear();
+    tallilltets(illtetlist);
+  } while (illtetlist->len() > 0);
+
+  delete flipstackers;
+  delete flipqueue;
+  delete illtetlist;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// removeslivers()    Repair mesh by removing slivers.                       //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::removeslivers()
+{
+  badtetrahedron *badtet;
+  list *illtetlist;
+  int i;
+
+  if (!b->quiet) {
+    printf("Removing slivers.\n");
+  }
+
+  // Initialize the pool of bad tetrahedra.
+  illtetlist = new list(sizeof(badtetrahedron), NULL, 1024);
+  // Initialize the pool of recently flipped faces.
+  flipstackers = new memorypool(sizeof(flipstacker), 1024, POINTER, 0);
+  
+  // Test all tetrahedra to see if they're slivers.
+  tallslivers(illtetlist);
+  for (i = 0; i < illtetlist->len(); i++) {
+    badtet = (badtetrahedron *)(* illtetlist)[i];
+    if (!isdead(&badtet->tet) && (org(badtet->tet) == badtet->tetorg) &&
+        (dest(badtet->tet) == badtet->tetdest) &&
+        (apex(badtet->tet) == badtet->tetapex) &&
+        (oppo(badtet->tet) == badtet->tetoppo)) {
+      // It's a sliver, remove it.
+      removebadtet(SLIVER, &badtet->tet, NULL);
+    }
+  }
+  illtetlist->clear();
+
+  delete flipstackers;
+  delete illtetlist;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// repairencsegs()    Repair all the encroached subsegments until no         //
+//                    subsegment is encroached.                              //
+//                                                                           //
+// At beginning, all encroached subsegments are stored in pool 'badsubsegs'. //
+// Each encroached subsegment is repaired by splitting it, i.e., inserting a //
+// point somewhere in it.  Newly inserted points may encroach upon other     //
+// subsegments, these are also repaired.                                     //
+//                                                                           //
+// After splitting a segment, the Delaunay property of the mesh is recovered //
+// by flip operations. 'flipqueue' returns a list of updated faces which may //
+// be non-locally Delaunay.                                                  //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::repairencsegs(REAL* rpsarray, bool bqual, queue* flipqueue)
+{
+  badface *encloop;
+  triface starttet;
+  face startsh, spinsh, checksh;
+  face splitseg, checkseg;
+  point eorg, edest;
+  point newpoint, ppt;
+  bool acuteorg, acutedest;
+  REAL rps, len, split;
+  int ptidx, i;
+
+  if (b->verbose > 1) {
+    printf("  Splitting encroached subsegments.\n");
+  }
+
+  // Note that steinerleft == -1 if an unlimited number of Steiner points 
+  //   is allowed.  Loop until 'badsubsegs' is empty.
+  while ((badsubsegs->items > 0) && (steinerleft != 0)) {
+    badsubsegs->traversalinit();
+    encloop = badfacetraverse(badsubsegs);
+    while ((encloop != (badface *) NULL) && (steinerleft != 0)) {
+      splitseg = encloop->ss;
+      // Every splitseg has a pointer to encloop, now clear it.
+      assert(shell2badface(splitseg) == encloop);
+      setshell2badface(splitseg, NULL);
+      eorg = sorg(splitseg);
+      edest = sdest(splitseg);
+      assert((eorg == encloop->forg) && (edest == encloop->fdest));
+      if (b->verbose > 1) {
+        printf("  Get encseg (%d, %d).\n", pointmark(eorg), pointmark(edest));
+      }
+      
+      if (checkseg4splitting(&splitseg, rpsarray, bqual)) {
+        // Decide the position to split the segment. Use the cutting sphere
+        //   if any of the endpoints is acute.
+        acuteorg = (pointtype(eorg) == ACUTEVERTEX);
+        acutedest = (pointtype(edest) == ACUTEVERTEX);
+        if (acuteorg || acutedest) {
+          if (!acuteorg) {
+            // eorg is not acute, but edest is. Exchange eorg, edest.
+            eorg = edest;
+            edest = sorg(splitseg);
+          }
+          // Now, eorg must be acute.
+          len = distance(eorg, edest);
+          // Get the radius of the current protecting sphere.
+          ptidx = pointmark(eorg) - in->firstnumber;
+          rps = rpsarray[ptidx];
+          // Calculate the suitable radius to split the segment. It should
+          //   be no larger than half of the segment length.
+          while (rps > 0.51 * len) {
+            rps *= 0.5;
+          }
+          assert((rps * 16.0) > rpsarray[ptidx]);
+          // Where to split the segment.
+          split = rps / len;
+          ppt = eorg;
+        } else {
+          split = 0.5;
+          ppt = (point) NULL;
+        }
+
+        // Create the new point.
+        makepoint(&newpoint);
+        // Set its coordinates.
+        for (i = 0; i < 3; i++) {
+          newpoint[i] = eorg[i] + split * (edest[i] - eorg[i]);
+        }
+        // Interpolate its attributes.
+        for (i = 0; i < in->numberofpointattributes; i++) {
+          newpoint[i + 3] = eorg[i + 3] + split * (edest[i + 3] - eorg[i + 3]);
+        }
+        // Set the parent point into the newpoint.
+        setpoint2ppt(newpoint, ppt);
+        // Set the type of the newpoint.
+        setpointtype(newpoint, FREESEGVERTEX);
+        // Set splitseg into the newpoint.
+        setpoint2sh(newpoint, sencode(splitseg));
+
+        // Insert new point into the mesh. It should be always success.
+        splitseg.shver = 0;
+        sstpivot(&splitseg, &starttet);
+        splittetedge(newpoint, &starttet, flipqueue);
+        if (steinerleft > 0) steinerleft--;
+
+        // Check the two new subsegments to see if they're encroached.
+        checkseg4encroach(&splitseg, NULL, true);
+        if (badsubfaces != (memorypool *) NULL) {
+          // Check the subfaces link of s to see if they're encroached.
+          spivot(splitseg, startsh);
+          spinsh = startsh;
+          do {
+            findedge(&spinsh, sorg(splitseg), sdest(splitseg));
+            // The next two lines are only for checking.
+            sspivot(spinsh, checkseg);
+            assert(checkseg.sh == splitseg.sh);
+            checksh = spinsh;
+            if (!shell2badface(checksh)) {
+              checksub4encroach(&checksh, NULL, true);
+            }
+            // The above operation may change the edge.
+            findedge(&spinsh, sorg(splitseg), sdest(splitseg));
+            spivotself(spinsh);
+          } while (spinsh.sh != startsh.sh);
+        }
+        senextself(splitseg);
+        spivotself(splitseg);
+        assert(splitseg.sh != (shellface *) NULL);
+        splitseg.shver = 0;
+        checkseg4encroach(&splitseg, NULL, true);
+        if (badsubfaces != (memorypool *) NULL) {
+          // Check the subfaces link of s to see if they're encroached.
+          spivot(splitseg, startsh);
+          spinsh = startsh;
+          do {
+            findedge(&spinsh, sorg(splitseg), sdest(splitseg));
+            // The next two lines are only for checking.
+            sspivot(spinsh, checkseg);
+            assert(checkseg.sh == splitseg.sh);
+            checksh = spinsh;
+            if (!shell2badface(checksh)) {
+              checksub4encroach(&checksh, NULL, true);
+            }
+            // The above operation may change the edge.
+            findedge(&spinsh, sorg(splitseg), sdest(splitseg));
+            spivotself(spinsh);
+          } while (spinsh.sh != startsh.sh);
+        }
+
+        // Recover Delaunay property by flipping. All existing segments which
+        //   are encroached by the new point will be discovered during flips
+        //   and be queued in list.
+        flip(flipqueue, NULL);
+        // Queuing bad-quality tetrahedra if need.
+        if (badtetrahedrons != (memorypool *) NULL) {
+          doqualchecktetlist();
+        }
+      }
+
+      // Remove this entry from list.
+      badfacedealloc(badsubsegs, encloop);  
+      // Get the next encroached segments.
+      encloop = badfacetraverse(badsubsegs);
+    }
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// repairencsubs()    Repair all the encroached subfaces until no subface is //
+//                    encroached.                                            //
+//                                                                           //
+// At beginning, all encroached subfaces are stored in pool 'badsubfaces'.   //
+// Each encroached subface is repaired by splitting it, i.e., inserting a    //
+// point at its circumcenter.  However, if this point encroaches upon one or //
+// more subsegments then we don not add it and instead split the subsegments.//
+// Newly inserted points may encroach upon other subfaces, these are also    //
+// repaired.                                                                 //
+//                                                                           //
+// After splitting a subface, the Delaunay property of the mesh is recovered //
+// by flip operations. 'flipqueue' returns a list of updated faces and may   //
+// be non-locally Delaunay.                                                  //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::repairencsubs(REAL* rpsarray, int* idx2facetlist,
+                               list* cavtetlist, queue* flipqueue)
+{
+  badface *encloop;
+  triface starttet;
+  face splitsub, neisplitsub;
+  face checksh, checkseg;
+  point newpoint, checkpt;
+  point pa, pb;
+  enum locateresult loc;
+  REAL epspp, dist;
+  bool enq, reject;
+  bool splitit, bqual;
+  int facetidx, quenumber;
+  int epscount;
+  int i;
+
+  if (b->verbose > 1) {
+    printf("  Splitting encroached subfaces.\n");
+  }
+
+  // Note that steinerleft == -1 if an unlimited number of Steiner points
+  //   is allowed.  Loop until the list 'badsubfaces' is empty.
+  while ((badsubfaces->items > 0) && (steinerleft != 0)) {
+    // Look for a nonempty queue.
+    encloop = (badface *) NULL;
+    for (quenumber = 1; quenumber >= 0; quenumber--) {
+      encloop = subquefront[quenumber];
+      if (encloop != (badface *) NULL) {
+        // Remove the badface from the queue.
+        subquefront[quenumber] = encloop->nextface;
+        // Maintain a pointer to the NULL pointer at the end of the queue.
+        if (subquefront[quenumber] == (badface *) NULL) {
+          subquetail[quenumber] = &subquefront[quenumber];
+        }
+        break;
+      }
+    }
+    assert(encloop != (badface *) NULL);
+    if (b->verbose > 2) {
+      printf("    Dequeuing ensub (%d, %d, %d) [%d].\n",
+             pointmark(encloop->forg), pointmark(encloop->fdest),
+             pointmark(encloop->fapex), quenumber);
+    }
+    
+    // Clear the pointer saved in encloop->ss. 
+    splitsub = encloop->ss;
+    setshell2badface(splitsub, NULL);
+    // The subface may be not the same one when it was determined to be
+    //   encroached.  If its adjacent encroached subface was split, the
+    //   consequent flips may change it into another subface.
+    enq = ((sorg(splitsub) == encloop->forg) &&
+           (sdest(splitsub) == encloop->fdest) &&
+           (sapex(splitsub) == encloop->fapex));
+    if (enq) {
+      // This subface is encroached or has bad quality.
+      bqual = (quenumber == 1);
+      facetidx = shellmark(splitsub);
+      // Split it if it is bad quality or is not sharp.
+      splitit = bqual || (idx2facetlist[facetidx - 1] != 1);
+      if (!splitit) {
+        // Split it if it's neighboring tets have too big volume.
+        bqual = checksub4splitting(&splitsub);
+        splitit = (bqual == true);
+      }
+      if (splitit) {
+        // We can or force to split this subface.
+        makepoint(&newpoint);
+        // If it is a bad quality face, calculate its circumcenter.
+        if (quenumber == 1) {
+          circumsphere(encloop->forg, encloop->fdest, encloop->fapex, NULL,
+                       encloop->cent, NULL);
+        } 
+        // Set the coordinates of newpoint.
+        for (i = 0; i < 3; i++) newpoint[i] = encloop->cent[i];
+        stpivot(splitsub, starttet);
+        if (starttet.tet == dummytet) {
+          sesymself(splitsub);
+          stpivot(splitsub, starttet);
+        }
+        assert(starttet.tet != dummytet);
+        // Locate the newpoint in facet (resulting in splitsub).
+        loc = locatesub(newpoint, &splitsub, oppo(starttet));
+        stpivot(splitsub, starttet);
+        if (starttet.tet == dummytet) {
+          sesymself(splitsub);
+          stpivot(splitsub, starttet);
+        }
+        assert(starttet.tet != dummytet);
+        // Look if the newpoint encroaches upon some segments.
+        recenttet = starttet;  // Used for the input of preciselocate().
+        collectcavtets(newpoint, cavtetlist);
+        assert(cavtetlist->len() > 0); 
+        reject = tallencsegs(newpoint, cavtetlist);
+        // Clear the list for the next use.
+        cavtetlist->clear();
+        if (!reject) {
+          // Remove the encroached subface by inserting the newpoint.
+          if (loc != ONVERTEX) {
+            // Adjust the location of newpoint wrt. starttet.
+            epspp = b->epsilon;
+            epscount = 0;
+            while (epscount < 16) {
+              loc = adjustlocate(newpoint, &starttet, ONFACE, epspp);
+              if (loc == ONVERTEX) {
+                checkpt = org(starttet);
+                dist = distance(checkpt, newpoint);
+                if ((dist / longest) > b->epsilon) {
+                  epspp *= 1e-2;
+                  epscount++;
+                  continue;
+                }
+              }
+              break;
+            }
+          }
+          pa = org(starttet);
+          pb = dest(starttet);
+          findedge(&splitsub, pa, pb);
+          // Let splitsub be face abc.  ab is the current edge.
+          if (loc == ONFACE) {
+            // Split the face abc into three faces abv, bcv, cav. 
+            splittetface(newpoint, &starttet, flipqueue);
+            // Adjust splitsub be abv.
+            findedge(&splitsub, pa, pb);
+            assert(sapex(splitsub) == newpoint);
+            // Check the three new subfaces to see if they're encroached.
+            //   splitsub may be queued (it exists before split).
+            checksh = splitsub;
+            if (!shell2badface(checksh)) {
+              checksub4encroach(&checksh, NULL, true); // abv
+            }
+            senext(splitsub, checksh);
+            spivotself(checksh);
+            // It is a new created face and should not be infected.
+            assert(checksh.sh != dummysh && !shell2badface(checksh));
+            checksub4encroach(&checksh, NULL, true); // bcv
+            senext2(splitsub, checksh);
+            spivotself(checksh);
+            // It is a new created face and should not be infected.
+            assert(checksh.sh != dummysh && !shell2badface(checksh));
+            checksub4encroach(&checksh, NULL, true); // cav
+          } else if (loc == ONEDGE) {
+            // Let the adjacent subface be bad.  ab is the spliting edge.
+            //   Split two faces abc, bad into 4 faces avc, vbc, avd, vbd.
+            sspivot(splitsub, checkseg);
+            assert(checkseg.sh == dummysh);
+            // Remember the neighbor subface abd (going to be split also).
+            spivot(splitsub, neisplitsub);
+            findedge(&neisplitsub, pa, pb);
+            // Split two faces abc, abd into four faces avc, vbc, avd, vbd.
+            splittetedge(newpoint, &starttet, flipqueue);
+            // Adjust splitsub be avc, neisplitsub be avd.
+            findedge(&splitsub, pa, newpoint);
+            findedge(&neisplitsub, pa, newpoint);
+            // Check the four new subfaces to see if they're encroached.
+            //   splitsub may be an infected one (it exists before split).
+            checksh = splitsub;
+            if (!shell2badface(checksh)) {
+              checksub4encroach(&checksh, NULL, true); // avc
+            }
+            //   Get vbc.
+            senext(splitsub, checksh);
+            spivotself(checksh);
+            //   vbc is newly created.
+            assert(checksh.sh != dummysh && !shell2badface(checksh));
+            checksub4encroach(&checksh, NULL, true); // vbc
+            //   neisplitsub may be an infected one (it exists before split).
+            checksh = neisplitsub;
+            if (!shell2badface(checksh)) {
+              checksub4encroach(&checksh, NULL, true); // avd
+            }
+            //   Get vbd.
+            senext(neisplitsub, checksh);
+            spivotself(checksh);
+            //   vbd is newly created.
+            assert(checksh.sh != dummysh && !shell2badface(checksh));
+            checksub4encroach(&checksh, NULL, true); // vbd
+          } else {
+            printf("Internal error in splitencsub():  Point %d locates %s.\n",
+              pointmark(newpoint), loc == ONVERTEX ? "on vertex" : "outside");
+            internalerror();
+          }
+          if (steinerleft > 0) steinerleft--;
+          // Recover Delaunay property by flipping. All existing subfaces
+          //   which are encroached by the new point will be discovered
+          //   during flips and be queued in list.
+          flip(flipqueue, NULL);
+          // There should be no encroached segments.
+          // assert(badsubsegs->items == 0);
+          // Queuing bad-quality tetrahedra if need.
+          if (badtetrahedrons != (memorypool *) NULL) {
+            doqualchecktetlist();
+          }
+        } else {
+          // newpoint encroaches upon some segments. Rejected.
+          /*
+          if (bqual) {
+            // Re-queue this face to process it later.
+            badface *splitsub = encloop->ss;
+            encsub = (badface *) badsubfaces->alloc();
+            encsub->ss = splitsub;
+            encsub->forg = sorg(splitsub);
+            encsub->fdest = sdest(splitsub);
+            encsub->fapex = sapex(splitsub);
+            encsub->foppo = encloop->foppo;
+            for (i = 0; i < 3; i++) encsub->cent[i] = newpoint[i];
+            encsub->nextface = (badface *) NULL;
+            setshell2badface(encsub->ss, encsub);
+            // Add the subface to the end of a queue.
+            *subquetail[quenumber] = encsub;
+            // Maintain a pointer to the NULL pointer at the end of the queue.
+            subquetail[quenumber] = &encsub->nextface;
+            if (b->verbose > 2) {
+               printf("    Requeuing subface (%d, %d, %d) [%d].\n", 
+                      pointmark(encsub->forg), pointmark(encsub->fdest),
+                      pointmark(encsub->fapex), quenumber);
+            }
+          }
+          */
+          // Delete the newpoint.
+          pointdealloc(newpoint);
+          // Repair all the encroached segments.
+          if (badsubsegs->items > 0) {
+            repairencsegs(rpsarray, bqual, flipqueue);
+          }
+        }
+      }
+    } else {
+      // enq = false!  This subface has been changed, check it again.
+      checksub4encroach(&splitsub, NULL, true);
+    }
+    // Remove this entry from list.
+    badfacedealloc(badsubfaces, encloop);
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// repairbadtets()    Repair all bad-quality tetrahedra until no tetrahedron //
+//                    is considered as bad-quality.                          //
+//                                                                           //
+// At beginning, all bad-quality tetrahedra are stored in 'badtetrahedrons'. //
+// Each bad tetrahedron is repaired by splitting it, i.e., inserting a point //
+// at its circumcenter.  However, if this point encroaches any subsegment or //
+// subface, we do not add it and instead split the subsegment or subface.    //
+// Newly inserted points may create other bad-quality tetrahedra, these are  //
+// also repaired.                                                            //
+//                                                                           //
+// After splitting a subface, the Delaunay property of the mesh is recovered //
+// by flip operations. 'flipqueue' returns a list of updated faces and may   //
+// be non-locally Delaunay.                                                  //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::repairbadtets(REAL* rpsarray, int* idx2facetlist,
+                               list* cavtetlist, queue* flipqueue)
+{
+  badtetrahedron *badtet;
+  triface starttet;
+  point newpoint;
+  point torg, tdest, tapex, toppo;
+  enum insertsiteresult success;
+  bool reject;
+  int i;
+
+  // Loop until pool 'badtetrahedrons' is empty. Note that steinerleft == -1
+  //   if an unlimited number of Steiner points is allowed.
+  while ((badtetrahedrons->items > 0) && (steinerleft != 0)) {
+    badtet = dequeuebadtet();
+    assert (badtet != (badtetrahedron *) NULL);
+    // Make sure that this tetrahedron is still the same tetrahedron it was
+    //   when it was tested and determined to be of bad quality. Subsequent
+    //   transformations may have made it a different tetrahedron.
+    if (!isdead(&badtet->tet) && org(badtet->tet) == badtet->tetorg &&
+        dest(badtet->tet) == badtet->tetdest && 
+        apex(badtet->tet) == badtet->tetapex &&
+        oppo(badtet->tet) == badtet->tetoppo) {
+      // Create a newpoint at the circumcenter of this tetrahedron.
+      makepoint(&newpoint);
+      for (i = 0; i < 3; i++) newpoint[i] = badtet->cent[i];
+      for (i = 0; i < in->numberofpointattributes; i++) newpoint[3 + i] = 0.0;
+      // Set it's type be FREEVOLVERTEX.
+      setpointtype(newpoint, FREEVOLVERTEX);
+      
+      // Look if the newpoint encroaches upon some segments, subfaces.
+      recenttet = badtet->tet;  // Used for the input of preciselocate().
+      collectcavtets(newpoint, cavtetlist);
+      assert(cavtetlist->len() > 0);
+      reject = tallencsegs(newpoint, cavtetlist);
+      if (!reject) {
+        reject = tallencsubs(newpoint, cavtetlist);
+      }
+      // Clear the list for the next use.
+      cavtetlist->clear();
+
+      if (!reject) {
+        // Insert the point, it should be always success.
+        starttet = badtet->tet;
+        success = insertsite(newpoint, &starttet, true, flipqueue);
+        if (success != DUPLICATEPOINT) {
+          if (steinerleft > 0) steinerleft--;
+          // Recover Delaunay property by flipping.
+          flip(flipqueue, NULL);
+          // Queuing bad-quality tetrahedra if need.
+          doqualchecktetlist();
+        } else {
+          // !!! It's a bug!!!
+          pointdealloc(newpoint);
+        }
+      } else {
+        // newpoint encroaches upon some segments or subfaces. Rejected.
+        pointdealloc(newpoint);
+        if (badsubsegs->items > 0) {
+          // Repair all the encroached segments.
+          repairencsegs(rpsarray, false, flipqueue);
+        }
+        if (badsubfaces->items > 0) {
+          // Repair all the encroached subfaces.
+          repairencsubs(rpsarray, idx2facetlist, cavtetlist, flipqueue);
+        }
+      }
+    }
+    // Remove the bad-quality tetrahedron from the pool.
+    badtetrahedrons->dealloc((void *) badtet);
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// enforcequality()    Remove all the encroached subsegments, subfaces  and  //
+//                     bad tetrahedra from the tetrahedral mesh.             //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::enforcequality()
+{
+  queue *flipqueue;
+  list *cavtetlist;
+  REAL *rpsarray;
+  int *idx2facetlist;
+  int i;
+  
+  if (!b->quiet) {
+    printf("Adding Steiner points to enforce quality.\n");
+  } 
+
+  // Initialize working queues, lists.
+  flipqueue = new queue(sizeof(badface));
+  cavtetlist = new list(sizeof(triface), NULL, 256);
+  rpsarray = new REAL[points->items];
+
+  // Mark segment vertices (acute or not) for determining segment types.
+  markacutevertices(89.0);
+  // Mark facets have sharp corners (for termination).
+  marksharpfacets(idx2facetlist, 89.0);
+  // Calculate the protecting spheres for all acute points.
+  initializerpsarray(rpsarray);
+
+  // Initialize the pool of encroached subsegments.
+  badsubsegs = new memorypool(sizeof(badface), SUBPERBLOCK, POINTER, 0);
+  // Test all segments to see if they're encroached.
+  tallencsegs(NULL, NULL);
+  if (b->verbose && badsubsegs->items > 0) {
+    printf("  Splitting encroached subsegments.\n");
+  }
+  // Fix encroached subsegments without noting any encr. subfaces.
+  repairencsegs(rpsarray, true, flipqueue);
+  
+  // Initialize the pool of encroached subfaces.
+  badsubfaces = new memorypool(sizeof(badface), SUBPERBLOCK, POINTER, 0);
+  // Initialize the queues of badfaces.
+  for (i = 0; i < 2; i++) subquefront[i] = (badface *) NULL;
+  for (i = 0; i < 2; i++) subquetail[i] = &subquefront[i];
+  // Test all subfaces to see if they're encroached.
+  tallencsubs(NULL, NULL);
+  if (b->verbose && badsubfaces->items > 0) {
+    printf("  Splitting encroached subfaces.\n");
+  }
+  // Fix encroached subfaces without noting bad tetrahedra.
+  repairencsubs(rpsarray, idx2facetlist, cavtetlist, flipqueue);
+  // At this point, the mesh should be (conforming) Delaunay.
+
+  // Next, fix bad quality tetrahedra.
+  if ((b->minratio > 0.0) || b->varvolume || b->fixedvolume) {
+    // Initialize the pool of bad tetrahedra.
+    badtetrahedrons = new memorypool(sizeof(badtetrahedron), ELEPERBLOCK,
+                                     POINTER, 0);
+    // Initialize the list of bad tetrahedra.
+    qualchecktetlist = new list(sizeof(triface), NULL);
+    // Initialize the queues of bad tetrahedra.
+    for (i = 0; i < 64; i++) tetquefront[i] = (badtetrahedron *) NULL;
+    for (i = 0; i < 64; i++) tetquetail[i] = &tetquefront[i];
+    // Test all tetrahedra to see if they're bad.
+    tallbadtetrahedrons();
+    if (b->verbose && badtetrahedrons->items > 0) {
+      printf("  Splitting bad tetrahedra.\n");
+    }
+    repairbadtets(rpsarray, idx2facetlist, cavtetlist, flipqueue);
+    // At this point, it should no bad quality tetrahedra.
+    delete qualchecktetlist;
+    delete badtetrahedrons;
+  }
+
+  delete badsubfaces;
+  delete badsubsegs;
+  delete cavtetlist;
+  delete flipqueue;
+  delete [] idx2facetlist;
+  delete [] rpsarray;
+}
+
+//
+// End of Delaunay refinement routines
+//
+
+//
+// Begin of I/O rouitnes
+//
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// transfernodes()    Transfer nodes from 'io->pointlist' to 'this->points'. //
+//                                                                           //
+// Initializing 'this->points'.  Transferring all points from 'in->pointlist'//
+// into it. All points are indexed (start from in->firstnumber).  Each point //
+// is initialized be UNUSEDVERTEX.  The bounding box (xmin, xmax, ymin, ymax,//
+// zmin, zmax) and the diameter (longest) of the point set are calculated.   //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::transfernodes()
+{
+  point pointloop;
+  REAL x, y, z;
+  int coordindex;
+  int attribindex;
+  int i, j;
+
+  // Read the points.
+  coordindex = 0;
+  attribindex = 0;
+  for (i = 0; i < in->numberofpoints; i++) {
+    makepoint(&pointloop);
+    // Read the point coordinates.
+    x = pointloop[0] = in->pointlist[coordindex++];
+    y = pointloop[1] = in->pointlist[coordindex++];
+    z = pointloop[2] = in->pointlist[coordindex++];
+    // Read the point attributes.
+    for (j = 0; j < in->numberofpointattributes; j++) {
+      pointloop[3 + j] = in->pointattributelist[attribindex++];
+    }
+    // Determine the smallest and largests 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.
+  //   It is used as the measure to distinguish two identical points.
+  x = xmax - xmin;
+  y = ymax - ymin;
+  z = zmax - zmin;
+  longest = sqrt(x * x + y * y + z * z);
+  if (longest == 0.0) {
+    printf("Error:  The point set is trivial.\n");
+    exit(1);
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// 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 idx;
+
+  if (!b->quiet) {
+    printf("Jettisoning redundants points.\n");
+  }
+
+  points->traversalinit();
+  pointloop = pointtraverse();
+  idx = in->firstnumber;
+  while (pointloop != (point) NULL) {
+    jetflag = (pointtype(pointloop) == DUPLICATEDVERTEX) || 
+      (pointtype(pointloop) == UNUSEDVERTEX);
+    if (jetflag) {
+      // It is a duplicated point, delete it.
+      pointdealloc(pointloop);
+    } else {
+      // Index it.
+      setpointmark(pointloop, idx);
+      idx++;
+    }
+    pointloop = pointtraverse();
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// highorder()   Create extra nodes for quadratic subparametric elements.    //
+//                                                                           //
+// 'highordertable' is an array (size = numberoftetrahedra * 6) for storing  //
+// high-order nodes of each tetrahedron.  This routine is used only when -o2 //
+// switch is used.                                                           //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::highorder()
+{
+  triface tetloop, worktet;
+  triface spintet, adjtet;
+  point torg, tdest, tapex;
+  point *extralist, *adjextralist;
+  point newpoint;
+  int hitbdry, ptmark;
+  int i, j;
+
+  // The 'edgeindex' (from 0 to 5) is list as follows:
+  //   0 - (v0, v1), 1 - (v1, v2), 2 - (v2, v0)
+  //   3 - (v3, v0), 4 - (v3, v1), 5 - (v3, v2)
+  // Define an edgeindex map: (loc, ver)->edgeindex.
+  int edgeindexmap[4][6] = {0, 0, 1, 1, 2, 2,
+                            3, 3, 4, 4, 0, 0,
+                            4, 4, 5, 5, 1, 1,
+                            5, 5, 3, 3, 2, 2};
+
+  if (!b->quiet) {
+    printf("Adding vertices for second-order tetrahedra.\n");
+  }
+
+  // Initialize the 'highordertable'.
+  highordertable = new point[tetrahedrons->items * 6];
+  if (highordertable == (point *) NULL) {
+    printf("Error:  Out of memory.\n");
+    exit(1);
+  }
+
+  // The following line ensures that dead items in the pool of nodes cannot
+  //   be allocated for the extra nodes associated with high order elements.
+  //   This ensures that the primary nodes (at the corners of elements) will
+  //   occur earlier in the output files, and have lower indices, than the
+  //   extra nodes.
+  points->deaditemstack = (void *) NULL;
+
+  // Assign an entry for each tetrahedron to find its extra nodes. At the
+  //   mean while, initialize all extra nodes be NULL.
+  i = 0;
+  tetrahedrons->traversalinit();
+  tetloop.tet = tetrahedrontraverse();
+  while (tetloop.tet != (tetrahedron *) NULL) {
+    tetloop.tet[highorderindex] = (tetrahedron) &highordertable[i];
+    for (j = 0; j < 6; j++) {
+      highordertable[i + j] = (point) NULL;
+    }
+    i += 6;
+    tetloop.tet = tetrahedrontraverse();
+  }
+
+  // To create a unique node on each edge. Loop over all tetrahedra, and
+  //   look at the six edges of each tetrahedron.  If the extra node in
+  //   the tetrahedron corresponding to this edge is NULL, create a node
+  //   for this edge, at the same time, set the new node into the extra
+  //   node lists of all other tetrahedra sharing this edge.  
+  tetrahedrons->traversalinit();
+  tetloop.tet = tetrahedrontraverse();
+  while (tetloop.tet != (tetrahedron *) NULL) {
+    // Get the list of extra nodes.
+    extralist = (point *) tetloop.tet[highorderindex];
+    for (i = 0; i < 6; i++) {
+      if (extralist[i] == (point) NULL) {
+        // Operate on this edge.
+        worktet = tetloop;
+        worktet.loc = 0; worktet.ver = 0;
+        // Get the correct edge in 'worktet'.
+        switch(i) {
+        case 0: // (v0, v1) 
+          break;
+        case 1: // (v1, v2)
+          enextself(worktet);
+          break;
+        case 2: // (v2, v0)
+          enext2self(worktet);
+          break;
+        case 3: // (v3, v0)
+          fnextself(worktet);
+          enext2self(worktet);
+          break;
+        case 4: // (v3, v1)
+          enextself(worktet);
+          fnextself(worktet);
+          enext2self(worktet);
+          break;
+        case 5: // (v3, v2)
+          enext2self(worktet);
+          fnextself(worktet);
+          enext2self(worktet);
+        }
+        // Create a new node on this edge.
+        torg = org(worktet);
+        tdest = dest(worktet);
+        // Create a new node in the middle of the edge.
+        newpoint = (point) points->alloc();
+        // Interpolate its attributes.
+        for (j = 0; j < 3 + in->numberofpointattributes; j++) {
+          newpoint[j] = 0.5 * (torg[j] + tdest[j]);
+        }
+        ptmark = (int) points->items - (in->firstnumber == 1 ? 0 : 1);
+        setpointmark(newpoint, ptmark);
+        // Add this node to its extra node list.
+        extralist[i] = newpoint;
+        // Set 'newpoint' into extra node lists of other tetrahedra
+        //   sharing this edge.
+        tapex = apex(worktet);
+        spintet = worktet;
+        hitbdry = 0;
+        while (hitbdry < 2) {
+          if (fnextself(spintet)) {
+            // Get the extra node list of 'spintet'.
+            adjextralist = (point *) spintet.tet[highorderindex];
+            // Find the index of its extra node list.
+            j = edgeindexmap[spintet.loc][spintet.ver];
+            // Only set 'newpoint' into 'adjextralist' if it is a NULL.
+            //   Because two faces can belong to the same tetrahedron.
+            if (adjextralist[j] == (point) NULL) {
+              adjextralist[j] = newpoint;
+            }
+            if (apex(spintet) == tapex) {
+              break;
+            }
+          } else {
+            hitbdry++;
+            if (hitbdry < 2) {
+              esym(worktet, spintet);
+	    }
+          }
+        }
+      }
+    }
+    tetloop.tet = tetrahedrontraverse();
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// outnodes()    Output the points to a .node file or a tetgenio structure.  //
+//                                                                           //
+// Note: each point has already been numbered on input (the first index is   //
+// 'in->firstnumber').                                                       //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::outnodes(tetgenio* out)
+{
+  FILE *outfile;
+  char outnodefilename[FILENAMESIZE];
+  point pointloop;
+  int nextras, bmark, marker;
+  int coordindex, attribindex;
+  int pointnumber, index, i;
+
+  if (out == (tetgenio *) NULL) {
+    strcpy(outnodefilename, b->outfilename);
+    strcat(outnodefilename, ".node");
+  } 
+  
+  if (!b->quiet) {
+    if (out == (tetgenio *) NULL) {
+      printf("Writing %s.\n", outnodefilename);
+    } else {
+      printf("Writing nodes.\n");
+    }
+  }
+
+  nextras = in->numberofpointattributes;
+  bmark = !b->nobound && in->pointmarkerlist;
+
+  if (out == (tetgenio *) NULL) {
+    outfile = fopen(outnodefilename, "w");
+    if (outfile == (FILE *) NULL) {
+      printf("File I/O Error:  Cannot create file %s.\n", outnodefilename);
+      exit(1);
+    }
+    // Number of points, number of dimensions, number of point attributes,
+    //   and number of boundary markers (zero or one).
+    fprintf(outfile, "%ld  %d  %d  %d\n", points->items, 3, nextras, bmark);
+  } else {
+    // Allocate space for 'pointlist';
+    out->pointlist = new REAL[points->items * 3];
+    if (out->pointlist == (REAL *) NULL) {
+      printf("Error:  Out of memory.\n");
+      exit(1);
+    }
+    // Allocate space for 'pointattributelist' if necessary;
+    if (nextras > 0) {
+      out->pointattributelist = new REAL[points->items * nextras];
+      if (out->pointattributelist == (REAL *) NULL) {
+        printf("Error:  Out of memory.\n");
+        exit(1);
+      }
+    }
+    // Allocate space for 'pointmarkerlist' if necessary;
+    if (bmark) {
+      out->pointmarkerlist = new int[points->items];
+      if (out->pointmarkerlist == (int *) NULL) {
+        printf("Error:  Out of memory.\n");
+        exit(1);
+      }
+    }
+    out->numberofpoints = points->items;
+    out->numberofpointattributes = nextras;
+    coordindex = 0;
+    attribindex = 0;
+  }
+
+  points->traversalinit();
+  pointloop = pointtraverse();
+  pointnumber = in->firstnumber;
+  index = 0;
+  while (pointloop != (point) NULL) {
+    if (bmark) {
+      // Determine the boundary marker.
+      if (index < in->numberofpoints) {
+        // Input point's marker is directly copied to output.
+        marker = in->pointmarkerlist[index];
+        if (marker == 0) {
+          // Change the marker if it is a boundary point.
+          marker = ((pointtype(pointloop) != UNUSEDVERTEX) &&
+                    (pointtype(pointloop) != FREEVOLVERTEX) &&
+                    (pointtype(pointloop) != DUPLICATEDVERTEX)) 
+                 ? 1 : 0;
+        }
+      } else if ((pointtype(pointloop) != UNUSEDVERTEX) &&
+                 (pointtype(pointloop) != FREEVOLVERTEX) &&
+                 (pointtype(pointloop) != DUPLICATEDVERTEX)) {
+        // A boundary vertex has marker 1.
+        marker = 1;
+      } else {
+        // Free or internal point has a zero marker.
+        marker = 0;
+      }
+    }
+    if (out == (tetgenio *) NULL) {
+      // Point number, x, y and z coordinates.
+      fprintf(outfile, "%4d    %.17g  %.17g  %.17g", pointnumber,
+              pointloop[0], pointloop[1], pointloop[2]);
+      for (i = 0; i < nextras; i++) {
+        // Write an attribute.
+        fprintf(outfile, "  %.17g", pointloop[3 + i]);
+      }
+      if (bmark) {
+        // Write the boundary marker.
+        fprintf(outfile, "    %d", marker);
+      }
+      fprintf(outfile, "\n");
+    } else {
+      // X, y, and z coordinates.
+      out->pointlist[coordindex++] = pointloop[0];
+      out->pointlist[coordindex++] = pointloop[1];
+      out->pointlist[coordindex++] = pointloop[2];
+      // Point attributes.
+      for (i = 0; i < nextras; i++) {
+        // Output an attribute.
+        out->pointattributelist[attribindex++] = pointloop[3 + i];
+      }
+      if (bmark) {
+        // Output the boundary marker.  
+        out->pointmarkerlist[index] = marker;
+      }
+    }
+    pointloop = pointtraverse();
+    pointnumber++; 
+    index++;
+  }
+
+  if (out == (tetgenio *) NULL) {
+    fprintf(outfile, "# Generated by %s\n", b->commandline);
+    fclose(outfile);
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// outelements()    Output the tetrahedra to an .ele file or a tetgenio      //
+//                  structure.                                               //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::outelements(tetgenio* out)
+{
+  FILE *outfile;
+  char outelefilename[FILENAMESIZE];
+  tetrahedron* tptr;
+  int *tlist;
+  REAL *talist;
+  int pointindex;
+  int attribindex;
+  point p1, p2, p3, p4;
+  point *extralist;
+  int elementnumber;
+  int eextras;
+  int i;
+
+  if (out == (tetgenio *) NULL) {
+    strcpy(outelefilename, b->outfilename);
+    strcat(outelefilename, ".ele");
+  }
+
+  if (!b->quiet) {
+    if (out == (tetgenio *) NULL) {
+      printf("Writing %s.\n", outelefilename);
+    } else {
+      printf("Writing elements.\n");
+    }
+  }
+
+  eextras = in->numberoftetrahedronattributes;
+  if (out == (tetgenio *) NULL) {
+    outfile = fopen(outelefilename, "w");
+    if (outfile == (FILE *) NULL) {
+      printf("File I/O Error:  Cannot create file %s.\n", outelefilename);
+      exit(1);
+    }
+    // Number of tetras, points per tetra, attributes per tetra.
+    fprintf(outfile, "%ld  %d  %d\n", tetrahedrons->items,
+            b->order == 1 ? 4 : 10, eextras);
+  } else {
+    // Allocate memory for output tetrahedra.
+    out->tetrahedronlist = new int[tetrahedrons->items * 
+                                   (b->order == 1 ? 4 : 10)];
+    if (out->tetrahedronlist == (int *) NULL) {
+      printf("Error:  Out of memory.\n");
+      exit(1);
+    }
+    // Allocate memory for output tetrahedron attributes if necessary.
+    if (eextras > 0) {
+      out->tetrahedronattributelist = new REAL[tetrahedrons->items * eextras];
+      if (out->tetrahedronattributelist == (REAL *) NULL) {
+        printf("Error:  Out of memory.\n");
+        exit(1);
+      }
+    }
+    out->numberoftetrahedra = tetrahedrons->items;
+    out->numberofcorners = b->order == 1 ? 4 : 10;
+    out->numberoftetrahedronattributes = eextras;
+    tlist = out->tetrahedronlist;
+    talist = out->tetrahedronattributelist;
+    pointindex = 0;
+    attribindex = 0;
+  }
+
+  tetrahedrons->traversalinit();
+  tptr = tetrahedrontraverse();
+  elementnumber = in->firstnumber;
+  while (tptr != (tetrahedron *) NULL) {
+    p1 = (point) tptr[4];
+    p2 = (point) tptr[5];
+    p3 = (point) tptr[6];
+    p4 = (point) tptr[7];
+    if (out == (tetgenio *) NULL) {
+      // Tetrahedron number, indices for four points.
+      fprintf(outfile, "%5d   %5d %5d %5d %5d", elementnumber,
+              pointmark(p1), pointmark(p2), pointmark(p3), pointmark(p4));
+      if (b->order == 2) {
+        extralist = (point *) tptr[highorderindex];
+        // Tetrahedron number, indices for four points plus six extra points.
+        fprintf(outfile, "  %5d %5d %5d %5d %5d %5d",
+                pointmark(extralist[0]), pointmark(extralist[1]),
+                pointmark(extralist[2]), pointmark(extralist[3]),
+                pointmark(extralist[4]), pointmark(extralist[5]));
+      }
+      for (i = 0; i < eextras; i++) {
+        fprintf(outfile, "    %.17g", elemattribute(tptr, i));
+      }
+      fprintf(outfile, "\n");
+    } else {
+      tlist[pointindex++] = pointmark(p1);
+      tlist[pointindex++] = pointmark(p2);
+      tlist[pointindex++] = pointmark(p3);
+      tlist[pointindex++] = pointmark(p4);
+      if (b->order == 2) {
+        extralist = (point *) tptr[highorderindex];
+        tlist[pointindex++] = pointmark(extralist[0]);
+        tlist[pointindex++] = pointmark(extralist[1]);
+        tlist[pointindex++] = pointmark(extralist[2]);
+        tlist[pointindex++] = pointmark(extralist[3]);
+        tlist[pointindex++] = pointmark(extralist[4]);
+        tlist[pointindex++] = pointmark(extralist[5]);
+      }
+      for (i = 0; i < eextras; i++) {
+        talist[attribindex++] = elemattribute(tptr, i);
+      }
+    }
+    tptr = tetrahedrontraverse();
+    elementnumber++;
+  }
+
+  if (out == (tetgenio *) NULL) {
+    fprintf(outfile, "# Generated by %s\n", b->commandline);
+    fclose(outfile);
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// outfaces()    Output all faces to a .face file or a tetgenio structure.   //
+//                                                                           //
+// This routines outputs all triangular faces (including outer boundary      //
+// faces and inner faces) of this mesh.                                      //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::outfaces(tetgenio* out)
+{
+  FILE *outfile;
+  char facefilename[FILENAMESIZE];
+  int *elist;
+  int *emlist;
+  int index;
+  triface tface, tsymface;
+  face checkmark;
+  point torg, tdest, tapex;
+  long faces;
+  int bmark, faceid, marker;
+  int facenumber;
+
+  if (out == (tetgenio *) NULL) {
+    strcpy(facefilename, b->outfilename);
+    strcat(facefilename, ".face");
+  }
+
+  if (!b->quiet) {
+    if (out == (tetgenio *) NULL) {
+      printf("Writing %s.\n", facefilename);
+    } else {
+      printf("Writing faces.\n");
+    }
+  }
+
+  faces = (4l * tetrahedrons->items + hullsize) / 2l;
+  bmark = !b->nobound && in->facetmarkerlist;
+
+  if (out == (tetgenio *) NULL) {
+    outfile = fopen(facefilename, "w");
+    if (outfile == (FILE *) NULL) {
+      printf("File I/O Error:  Cannot create file %s.\n", facefilename);
+      exit(1);
+    }
+    fprintf(outfile, "%ld  %d\n", faces, bmark);
+  } else {
+    // Allocate memory for 'trifacelist'.
+    out->trifacelist = new int[faces * 3];
+    if (out->trifacelist == (int *) NULL) {
+      printf("Error:  Out of memory.\n");
+      exit(1);
+    }
+    // Allocate memory for 'trifacemarkerlist' if necessary.
+    if (bmark) {
+      out->trifacemarkerlist = new int[faces];
+      if (out->trifacemarkerlist == (int *) NULL) {
+        printf("Error:  Out of memory.\n");
+        exit(1);
+      }
+    }
+    out->numberoftrifaces = faces;
+    elist = out->trifacelist;
+    emlist = out->trifacemarkerlist;
+    index = 0;
+  }
+
+  tetrahedrons->traversalinit();
+  tface.tet = tetrahedrontraverse();
+  facenumber = in->firstnumber;
+  // To loop over the set of faces, loop over all tetrahedra, and look at
+  //   the four faces of each one. If there isn't another tetrahedron
+  //   adjacent to this face, operate on the face.  If there is another
+  //   adjacent tetrahedron, operate on the face only if the current
+  //   tetrahedron has a smaller pointer than its neighbor.  This way, each
+  //   face is considered only once.
+  while (tface.tet != (tetrahedron *) NULL) {
+    for (tface.loc = 0; tface.loc < 4; tface.loc ++) {
+      sym(tface, tsymface);
+      if ((tsymface.tet == dummytet) || (tface.tet < tsymface.tet)) {
+        torg = org(tface);
+        tdest = dest(tface);
+        tapex = apex(tface);
+        if (bmark) {
+          // Get the boundary marker of this face. If it is an inner face,
+          //   it has no boundary marker, set it be zero.
+          if (b->useshelles) {
+            // Shell face is used.
+            tspivot(tface, checkmark);
+            if (checkmark.sh == dummysh) {
+              marker = 0;  // It is an inner face.
+            } else {
+              faceid = shellmark(checkmark) - 1;
+              marker = in->facetmarkerlist[faceid];
+            }
+          } else {
+            // Shell face is not used, only distinguish outer and inner face.
+            marker = tsymface.tet != dummytet ? 1 : 0;
+          }
+        }
+        if (out == (tetgenio *) NULL) {
+          // Face number, indices of three vertices.
+          fprintf(outfile, "%5d   %4d  %4d  %4d", facenumber,
+                  pointmark(torg), pointmark(tdest), pointmark(tapex));
+          if (bmark) {
+            // Output a boundary marker.
+            fprintf(outfile, "  %d", marker);
+          }
+          fprintf(outfile, "\n");
+        } else {
+          // Output indices of three vertices.
+          elist[index++] = pointmark(torg);
+          elist[index++] = pointmark(tdest);
+          elist[index++] = pointmark(tapex);
+          if (bmark) {
+            emlist[facenumber - in->firstnumber] = marker;
+          }
+        }
+        facenumber++;
+      }
+    }
+    tface.tet = tetrahedrontraverse();
+  }
+
+  if (out == (tetgenio *) NULL) {
+    fprintf(outfile, "# Generated by %s\n", b->commandline);
+    fclose(outfile);
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// outhullfaces()    Output outer boundary faces to a .face file or a        //
+//                   tetgenio structure.                                     //
+//                                                                           //
+// The normal of each face is arranged to point inside of the domain (use    //
+// right-hand rule).  This routines will outputs convex hull faces if the    //
+// mesh is a Delaunay tetrahedralization.                                    //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::outhullfaces(tetgenio* out)
+{
+  FILE *outfile;
+  char facefilename[FILENAMESIZE];
+  int *elist;
+  int index;
+  triface tface, tsymface;
+  face checkmark;
+  point torg, tdest, tapex;
+  int facenumber;
+
+  if (out == (tetgenio *) NULL) {
+    strcpy(facefilename, b->outfilename);
+    strcat(facefilename, ".face");
+  }
+
+  if (!b->quiet) {
+    if (out == (tetgenio *) NULL) {
+      printf("Writing %s.\n", facefilename);
+    } else {
+      printf("Writing faces.\n");
+    }
+  }
+
+  if (out == (tetgenio *) NULL) {
+    outfile = fopen(facefilename, "w");
+    if (outfile == (FILE *) NULL) {
+      printf("File I/O Error:  Cannot create file %s.\n", facefilename);
+      exit(1);
+    }
+    fprintf(outfile, "%ld  0\n", hullsize);
+  } else {
+    // Allocate memory for 'trifacelist'.
+    out->trifacelist = new int[hullsize * 3];
+    if (out->trifacelist == (int *) NULL) {
+      printf("Error:  Out of memory.\n");
+      exit(1);
+    }
+    out->numberoftrifaces = hullsize;
+    elist = out->trifacelist;
+    index = 0;
+  }
+
+  tetrahedrons->traversalinit();
+  tface.tet = tetrahedrontraverse();
+  facenumber = in->firstnumber;
+  // To loop over the set of hull faces, loop over all tetrahedra, and look
+  //   at the four faces of each one. If there isn't another tetrahedron
+  //   adjacent to this face, operate on the face.
+  while (tface.tet != (tetrahedron *) NULL) {
+    for (tface.loc = 0; tface.loc < 4; tface.loc ++) {
+      sym(tface, tsymface);
+      if (tsymface.tet == dummytet) {
+        torg = org(tface);
+        tdest = dest(tface);
+        tapex = apex(tface);
+        if (out == (tetgenio *) NULL) {
+          // Face number, indices of three vertices.
+          fprintf(outfile, "%5d   %4d  %4d  %4d", facenumber,
+                  pointmark(torg), pointmark(tdest), pointmark(tapex));
+          fprintf(outfile, "\n");
+        } else {
+          // Output indices of three vertices.
+          elist[index++] = pointmark(torg);
+          elist[index++] = pointmark(tdest);
+          elist[index++] = pointmark(tapex);
+        }
+        facenumber++;
+      }
+    }
+    tface.tet = tetrahedrontraverse();
+  }
+
+  if (out == (tetgenio *) NULL) {
+    fprintf(outfile, "# Generated by %s\n", b->commandline);
+    fclose(outfile);
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// outsubfaces()    Output subfaces (i.e. boundary faces) to a .face file or //
+//                  a tetgenio structure.                                    //
+//                                                                           //
+// The boundary faces are exist in 'subfaces'. For listing triangle vertices //
+// in the same sense for all triangles in the mesh, the direction determined //
+// by right-hand rule is pointer to the inside of the volume.                //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::outsubfaces(tetgenio* out)
+{
+  FILE *outfile;
+  char facefilename[FILENAMESIZE];
+  int *elist;
+  int *emlist;
+  int index;
+  triface abuttingtet;
+  face faceloop;
+  point torg, tdest, tapex;
+  int bmark, faceid, marker;
+  int facenumber;
+
+  if (out == (tetgenio *) NULL) {
+    strcpy(facefilename, b->outfilename);
+    strcat(facefilename, ".face");
+  }
+
+  if (!b->quiet) {
+    if (out == (tetgenio *) NULL) {
+      printf("Writing %s.\n", facefilename);
+    } else {
+      printf("Writing faces.\n");
+    }
+  }
+
+  bmark = !b->nobound && in->facetmarkerlist;
+
+  if (out == (tetgenio *) NULL) {
+    outfile = fopen(facefilename, "w");
+    if (outfile == (FILE *) NULL) {
+      printf("File I/O Error:  Cannot create file %s.\n", facefilename);
+      exit(1);
+    }
+    // Number of subfaces.
+    fprintf(outfile, "%ld  %d\n", subfaces->items, bmark);
+  } else {
+    // Allocate memory for 'trifacelist'.
+    out->trifacelist = new int[subfaces->items * 3];
+    if (out->trifacelist == (int *) NULL) {
+      printf("Error:  Out of memory.\n");
+      exit(1);
+    }
+    // Allocate memory for 'trifacemarkerlist', if necessary.
+    if (bmark) {
+      out->trifacemarkerlist = new int[subfaces->items];
+      if (out->trifacemarkerlist == (int *) NULL) {
+        printf("Error:  Out of memory.\n");
+        exit(1);
+      }
+    }
+    out->numberoftrifaces = subfaces->items;
+    elist = out->trifacelist;
+    emlist = out->trifacemarkerlist;
+    index = 0;
+  }
+
+  subfaces->traversalinit();
+  faceloop.sh = shellfacetraverse(subfaces);
+  facenumber = in->firstnumber;
+  while (faceloop.sh != (shellface *) NULL) {
+    stpivot(faceloop, abuttingtet);
+    if (abuttingtet.tet == dummytet) {
+      sesymself(faceloop);
+      stpivot(faceloop, abuttingtet);
+      // assert(abuttingtet.tet != dummytet) {
+    }
+    if (abuttingtet.tet != dummytet) {
+      // If there is a tetrahedron containing this subface, orient it so
+      //   that the normal of this face points to inside of the volume by
+      //   right-hand rule.
+      adjustedgering(abuttingtet, CCW);
+      torg = org(abuttingtet);
+      tdest = dest(abuttingtet);
+      tapex = apex(abuttingtet);
+    } else {
+      // This may happen when only a surface mesh be generated.
+      torg = sorg(faceloop);
+      tdest = sdest(faceloop);
+      tapex = sapex(faceloop);
+    }
+    if (bmark) {
+      faceid = shellmark(faceloop) - 1;
+      marker = in->facetmarkerlist[faceid];
+    }
+    if (out == (tetgenio *) NULL) {
+      fprintf(outfile, "%5d   %4d  %4d  %4d", facenumber,
+              pointmark(torg), pointmark(tdest), pointmark(tapex));
+      if (bmark) {
+        fprintf(outfile, "    %d", marker);
+      }
+      fprintf(outfile, "\n");
+    } else {
+      // Output three vertices of this face;
+      elist[index++] = pointmark(torg);
+      elist[index++] = pointmark(tdest);
+      elist[index++] = pointmark(tapex);
+      if (bmark) {
+        emlist[facenumber - in->firstnumber] = marker;
+      }
+    }
+    facenumber++;
+    faceloop.sh = shellfacetraverse(subfaces);
+  }
+
+  if (out == (tetgenio *) NULL) {
+    fprintf(outfile, "# Generated by %s\n", b->commandline);
+    fclose(outfile);
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// outsubsegments()    Output segments (i.e. boundary edges) to a .edge file //
+//                     or a tetgenio structure.                              //
+//                                                                           //
+// The boundary edges are stored in 'subsegs'.                               //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::outsubsegments(tetgenio* out)
+{
+  FILE *outfile;
+  char edgefilename[FILENAMESIZE];
+  int *elist;
+  int index;
+  face edgeloop;
+  point torg, tdest;
+  int edgenumber;
+
+  if (out == (tetgenio *) NULL) {
+    strcpy(edgefilename, b->outfilename);
+    strcat(edgefilename, ".edge");
+  }
+
+  if (!b->quiet) {
+    if (out == (tetgenio *) NULL) {
+      printf("Writing %s.\n", edgefilename);
+    } else {
+      printf("Writing faces.\n");
+    }
+  }
+
+  if (out == (tetgenio *) NULL) {
+    outfile = fopen(edgefilename, "w");
+    if (outfile == (FILE *) NULL) {
+      printf("File I/O Error:  Cannot create file %s.\n", edgefilename);
+      exit(1);
+    }
+    // Number of subsegments.
+    fprintf(outfile, "%ld\n", subsegs->items);
+  } else {
+    // Allocate memory for 'edgelist'.
+    out->edgelist = new int[subsegs->items * 2];
+    if (out->edgelist == (int *) NULL) {
+      printf("Error:  Out of memory.\n");
+      exit(1);
+    }
+    out->numberofedges = subsegs->items;
+    elist = out->edgelist;
+    index = 0;
+  }
+
+  subsegs->traversalinit();
+  edgeloop.sh = shellfacetraverse(subsegs);
+  edgenumber = in->firstnumber;
+  while (edgeloop.sh != (shellface *) NULL) {
+    torg = sorg(edgeloop);
+    tdest = sdest(edgeloop);
+    if (out == (tetgenio *) NULL) {
+      fprintf(outfile, "%5d   %4d  %4d\n", edgenumber, pointmark(torg),
+              pointmark(tdest));
+    } else {
+      // Output three vertices of this face;
+      elist[index++] = pointmark(torg);
+      elist[index++] = pointmark(tdest);
+    }
+    edgenumber++;
+    edgeloop.sh = shellfacetraverse(subsegs);
+  }
+
+  if (out == (tetgenio *) NULL) {
+    fprintf(outfile, "# Generated by %s\n", b->commandline);
+    fclose(outfile);
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// outneighbors()    Output a list of neighbors to a .neigh file or a        //
+//                   tetgenio structure.                                     //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::outneighbors(tetgenio* out)
+{
+  FILE *outfile;
+  char neighborfilename[FILENAMESIZE];
+  int *nlist;
+  int index;
+  tetrahedron *tptr;
+  triface tetloop, tetsym;
+  int neighbor1, neighbor2, neighbor3, neighbor4;
+  int elementnumber;
+
+  if (out == (tetgenio *) NULL) {
+    strcpy(neighborfilename, b->outfilename);
+    strcat(neighborfilename, ".neigh");
+  }
+
+  if (!b->quiet) {
+    if (out == (tetgenio *) NULL) {
+      printf("Writing %s.\n", neighborfilename);
+    } else {
+      printf("Writing neighbors.\n");
+    }
+  }
+
+  if (out == (tetgenio *) NULL) {
+    outfile = fopen(neighborfilename, "w");
+    if (outfile == (FILE *) NULL) {
+      printf("File I/O Error:  Cannot create file %s.\n", neighborfilename);
+      exit(1);
+    }
+    // Number of tetrahedra, four faces per tetrahedron.
+    fprintf(outfile, "%ld  %d\n", tetrahedrons->items, 4);
+  } else {
+    // Allocate memory for 'neighborlist'.
+    out->neighborlist = new int[tetrahedrons->items * 4];
+    if (out->neighborlist == (int *) NULL) {
+      printf("Error:  Out of memory.\n");
+      exit(1);
+    }
+    nlist = out->neighborlist;
+    index = 0;
+  }
+
+  tetrahedrons->traversalinit();
+  tptr = tetrahedrontraverse();
+  elementnumber = in->firstnumber;
+  while (tptr != (tetrahedron *) NULL) {
+    * (int *) (tptr + 8) = elementnumber;
+    tptr = tetrahedrontraverse();
+    elementnumber++;
+  }
+  * (int *) (dummytet + 8) = -1;
+
+  tetrahedrons->traversalinit();
+  tetloop.tet = tetrahedrontraverse();
+  elementnumber = in->firstnumber;
+  while (tetloop.tet != (tetrahedron *) NULL) {
+    tetloop.loc = 2;
+    sym(tetloop, tetsym);
+    neighbor1 = * (int *) (tetsym.tet + 8);
+    tetloop.loc = 3;
+    sym(tetloop, tetsym);
+    neighbor2 = * (int *) (tetsym.tet + 8);
+    tetloop.loc = 1;
+    sym(tetloop, tetsym);
+    neighbor3 = * (int *) (tetsym.tet + 8);
+    tetloop.loc = 0;
+    sym(tetloop, tetsym);
+    neighbor4 = * (int *) (tetsym.tet + 8);
+    if (out == (tetgenio *) NULL) {
+      // Tetrahedra number, neighboring tetrahedron numbers.
+      fprintf(outfile, "%4d    %4d  %4d  %4d  %4d\n", elementnumber,
+              neighbor1, neighbor2, neighbor3, neighbor4);
+    } else {
+      nlist[index++] = neighbor1;
+      nlist[index++] = neighbor2;
+      nlist[index++] = neighbor3;
+      nlist[index++] = neighbor4;
+    }
+    tetloop.tet = tetrahedrontraverse();
+    elementnumber++;
+  }
+
+  if (out == (tetgenio *) NULL) {
+    fprintf(outfile, "# Generated by %s\n", b->commandline);
+    fclose(outfile);
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// outsmesh()    Write surface mesh to a .smesh file, which can be read and  //
+//               tetrahedralized by TetGen.                                  //
+//                                                                           //
+// You can specify a filename (without suffix) in 'smfilename'. If you don't //
+// supply a filename (let smfilename be NULL), the default name stored in    //
+// 'tetgenbehavior' will be used.                                            //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::outsmesh(char* smfilename)
+{
+  FILE *outfile;
+  char smefilename[FILENAMESIZE];
+  face faceloop;
+  point pointloop;
+  point p1, p2, p3;
+  int pointnumber;
+  int nextras, bmark;
+  int faceid, marker;
+
+  if (smfilename != (char *) NULL && smfilename[0] != '\0') {
+    strcpy(smefilename, smfilename);
+  } else if (b->outfilename[0] != '\0') {
+    strcpy(smefilename, b->outfilename);
+  } else {
+    strcpy(smefilename, "unnamed");
+  }
+  strcat(smefilename, ".smesh");
+
+  if (!b->quiet) {
+    printf("Writing %s.\n", smefilename);
+  }
+  outfile = fopen(smefilename, "w");
+  if (outfile == (FILE *) NULL) {
+    printf("File I/O Error:  Cannot create file %s.\n", smefilename);
+    return;
+  }
+
+  fprintf(outfile, "# %s.  TetGen's input file.\n", smefilename);
+  
+  nextras = in->numberofpointattributes;
+  bmark = !b->nobound && in->pointmarkerlist;
+  
+  fprintf(outfile, "\n# part 1: node list.\n");
+  // Number of points, number of dimensions, number of point attributes,
+  //   and number of boundary markers (zero or one).
+  fprintf(outfile, "%ld  %d  %d  %d\n", points->items, 3, nextras, bmark);
+
+  points->traversalinit();
+  pointloop = pointtraverse();
+  pointnumber = in->firstnumber;
+  while (pointloop != (point) NULL) {
+    // Point coordinates.
+    fprintf(outfile, "%4d  %.17g  %.17g  %.17g",  pointnumber,
+            pointloop[0], pointloop[1], pointloop[2]);
+    if (in->numberofpointattributes > 0) {
+      // Write an attribute, ignore others if more than one.
+      fprintf(outfile, "  %.17g", pointloop[3]);
+    }
+    fprintf(outfile, "\n");
+    setpointmark(pointloop, pointnumber);
+    pointloop = pointtraverse();
+    pointnumber++;
+  }
+
+  bmark = !b->nobound && in->facetmarkerlist;  
+
+  fprintf(outfile, "\n# part 2: facet list.\n");
+  // Number of facets, boundary marker.
+  fprintf(outfile, "%ld  %d\n", subfaces->items, bmark);
+  
+  subfaces->traversalinit();
+  faceloop.sh = shellfacetraverse(subfaces);
+  while (faceloop.sh != (shellface *) NULL) {
+    p1 = sorg(faceloop);
+    p2 = sdest(faceloop);
+    p3 = sapex(faceloop);
+    if (bmark) {
+      faceid = shellmark(faceloop) - 1;
+      marker = in->facetmarkerlist[faceid];
+    }
+    fprintf(outfile, "3    %4d  %4d  %4d", pointmark(p1), pointmark(p2),
+            pointmark(p3));
+    if (bmark) {
+      fprintf(outfile, "    %d", marker);
+    }
+    fprintf(outfile, "\n");
+    faceloop.sh = shellfacetraverse(subfaces);
+  }
+
+  fprintf(outfile, "\n# part 3: hole list.\n");
+  fprintf(outfile, "0\n");
+
+  fprintf(outfile, "\n# part 4: region list.\n");
+  fprintf(outfile, "0\n");
+
+  fprintf(outfile, "# Generated by %s\n", b->commandline);
+  fclose(outfile);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// outmesh2medit()    Write mesh to a .mesh file, which can be read and      //
+//                    rendered by Medit (a free mesh viewer from INRIA).     //
+//                                                                           //
+// You can specify a filename (without suffix) in 'mfilename'.  If you don't //
+// supply a filename (let mfilename be NULL), the default name stored in     //
+// 'tetgenbehavior' will be used. The output file will have the suffix .mesh.//
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::outmesh2medit(char* mfilename)
+{
+  FILE *outfile;
+  char mefilename[FILENAMESIZE];
+  tetrahedron* tetptr;
+  triface tface, tsymface;
+  face segloop, checkmark;
+  point pointloop, p1, p2, p3, p4;
+  long faces;
+  int pointnumber;
+  int i;
+
+  if (mfilename != (char *) NULL && mfilename[0] != '\0') {
+    strcpy(mefilename, mfilename);
+  } else if (b->outfilename[0] != '\0') {
+    strcpy(mefilename, b->outfilename);
+  } else {
+    strcpy(mefilename, "unnamed");
+  }
+  strcat(mefilename, ".mesh");
+
+  if (!b->quiet) {
+    printf("Writing %s.\n", mefilename);
+  }
+  outfile = fopen(mefilename, "w");
+  if (outfile == (FILE *) NULL) {
+    printf("File I/O Error:  Cannot create file %s.\n", mefilename);
+    return;
+  }
+
+  fprintf(outfile, "MeshVersionFormatted 1\n");
+  fprintf(outfile, "\n");
+  fprintf(outfile, "Dimension\n");
+  fprintf(outfile, "3\n");
+  fprintf(outfile, "\n");
+
+  fprintf(outfile, "\n# Set of mesh vertices\n");
+  fprintf(outfile, "Vertices\n");
+  fprintf(outfile, "%ld\n", points->items);
+
+  points->traversalinit();
+  pointloop = pointtraverse();
+  pointnumber = 1;                        // Medit need start number form 1.
+  while (pointloop != (point) NULL) {
+    // Point coordinates.
+    fprintf(outfile, "%.17g  %.17g  %.17g",
+            pointloop[0], pointloop[1], pointloop[2]);
+    if (in->numberofpointattributes > 0) {
+      // Write an attribute, ignore others if more than one.
+      fprintf(outfile, "  %.17g\n", pointloop[3]);
+    } else {
+      fprintf(outfile, "    0\n");
+    }
+    setpointmark(pointloop, pointnumber);
+    pointloop = pointtraverse();
+    pointnumber++;
+  }
+
+  // Compute the number of edges.
+  faces = (4l * tetrahedrons->items + hullsize) / 2l;
+
+  fprintf(outfile, "\n# Set of Triangles\n");
+  fprintf(outfile, "Triangles\n");
+  fprintf(outfile, "%ld\n", faces);
+
+  tetrahedrons->traversalinit();
+  tface.tet = tetrahedrontraverse();
+  // To loop over the set of faces, loop over all tetrahedra, and look at
+  //   the four faces of each tetrahedron. If there isn't another tetrahedron
+  //   adjacent to the face, operate on the face.  If there is another adj-
+  //   acent tetrahedron, operate on the face only if the current tetrahedron
+  //   has a smaller pointer than its neighbor.  This way, each face is
+  //   considered only once.
+  while (tface.tet != (tetrahedron *) NULL) {
+    for (tface.loc = 0; tface.loc < 4; tface.loc ++) {
+      sym(tface, tsymface);
+      if (tface.tet < tsymface.tet || tsymface.tet == dummytet) {
+        p1 = org (tface);
+        p2 = dest(tface);
+        p3 = apex(tface);
+        fprintf(outfile, "%5d  %5d  %5d",
+                pointmark(p1), pointmark(p2), pointmark(p3));
+        fprintf(outfile, "    0\n");
+      }
+    }
+    tface.tet = tetrahedrontraverse();
+  }
+
+  fprintf(outfile, "\n# Set of Tetrahedra\n");
+  fprintf(outfile, "Tetrahedra\n");
+  fprintf(outfile, "%ld\n", tetrahedrons->items);
+
+  tetrahedrons->traversalinit();
+  tetptr = tetrahedrontraverse();
+  while (tetptr != (tetrahedron *) NULL) {
+    p1 = (point) tetptr[4];
+    p2 = (point) tetptr[5];
+    p3 = (point) tetptr[6];
+    p4 = (point) tetptr[7];
+    fprintf(outfile, "%5d  %5d  %5d  %5d",
+            pointmark(p1), pointmark(p2), pointmark(p3), pointmark(p4));
+    if (in->numberoftetrahedronattributes > 0) {
+      fprintf(outfile, "  %.17g", elemattribute(tetptr, 0));
+    } else {
+      fprintf(outfile, "  0");
+    }
+    fprintf(outfile, "\n");
+    tetptr = tetrahedrontraverse();
+  }
+
+  fprintf(outfile, "\nCorners\n");
+  fprintf(outfile, "%d\n", in->numberofpoints);
+
+  for (i = 0; i < in->numberofpoints; i++) {
+    fprintf(outfile, "%4d\n", i + 1);
+  }
+
+  if (b->useshelles) {
+    fprintf(outfile, "\nEdges\n");
+    fprintf(outfile, "%ld\n", subsegs->items);
+
+    subsegs->traversalinit();
+    segloop.sh = shellfacetraverse(subsegs);
+    while (segloop.sh != (shellface *) NULL) {
+      p1 = sorg(segloop);
+      p2 = sdest(segloop);
+      fprintf(outfile, "%5d  %5d", pointmark(p1), pointmark(p2));
+      fprintf(outfile, "    0\n");
+      segloop.sh = shellfacetraverse(subsegs);
+    }
+  }
+
+  fprintf(outfile, "\nEnd\n");
+  fclose(outfile);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// outmesh2gid()    Write mesh to a .ele.msh file and a .face.msh file,      //
+//                  which can be imported and rendered by Gid.               //
+//                                                                           //
+// You can specify a filename (without suffix) in 'gfilename'.  If you don't //
+// supply a filename (let gfilename be NULL), the default name stored in     //
+// 'tetgenbehavior' will be used. The suffixes (.ele.msh and .face.msh) will //
+// be automatically added.                                                   //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::outmesh2gid(char* gfilename)
+{
+  FILE *outfile;
+  char gidfilename[FILENAMESIZE];
+  tetrahedron* tetptr;
+  triface tface, tsymface;
+  face sface;
+  point pointloop, p1, p2, p3, p4;
+  int pointnumber;
+  int elementnumber;
+
+  if (gfilename != (char *) NULL && gfilename[0] != '\0') {
+    strcpy(gidfilename, gfilename);
+  } else if (b->outfilename[0] != '\0') {
+    strcpy(gidfilename, b->outfilename);
+  } else {
+    strcpy(gidfilename, "unnamed");
+  }
+  strcat(gidfilename, ".ele.msh");
+
+  if (!b->quiet) {
+    printf("Writing %s.\n", gidfilename);
+  }
+  outfile = fopen(gidfilename, "w");
+  if (outfile == (FILE *) NULL) {
+    printf("File I/O Error:  Cannot create file %s.\n", gidfilename);
+    return;
+  }
+
+  fprintf(outfile, "mesh dimension = 3 elemtype tetrahedron nnode = 4\n");
+  fprintf(outfile, "coordinates\n");
+
+  points->traversalinit();
+  pointloop = pointtraverse();
+  pointnumber = 1;                        // Gid need start number form 1.
+  while (pointloop != (point) NULL) {
+    // Point coordinates.
+    fprintf(outfile, "%4d  %.17g %.17g %.17g", pointnumber,
+            pointloop[0], pointloop[1], pointloop[2]);
+    if (in->numberofpointattributes > 0) {
+      // Write an attribute, ignore others if more than one.
+      fprintf(outfile, "  %.17g", pointloop[3]);
+    }
+    fprintf(outfile, "\n");
+    setpointmark(pointloop, pointnumber);
+    pointloop = pointtraverse();
+    pointnumber++;
+  }
+
+  fprintf(outfile, "end coordinates\n");
+  fprintf(outfile, "elements\n");
+
+  tetrahedrons->traversalinit();
+  tetptr = tetrahedrontraverse();
+  elementnumber = 1;
+  while (tetptr != (tetrahedron *) NULL) {
+    p1 = (point) tetptr[4];
+    p2 = (point) tetptr[5];
+    p3 = (point) tetptr[6];
+    p4 = (point) tetptr[7];
+    fprintf(outfile, "%5d  %5d %5d %5d %5d", elementnumber,
+            pointmark(p1), pointmark(p2), pointmark(p3), pointmark(p4));
+    if (in->numberoftetrahedronattributes > 0) {
+      fprintf(outfile, "  %.17g", elemattribute(tetptr, 0));
+    } 
+    fprintf(outfile, "\n");
+    tetptr = tetrahedrontraverse();
+    elementnumber++;
+  }
+
+  fprintf(outfile, "end elements\n");
+  fclose(outfile);
+
+  if (gfilename != (char *) NULL && gfilename[0] != '\0') {
+    strcpy(gidfilename, gfilename);
+  } else if (b->outfilename[0] != '\0') {
+    strcpy(gidfilename, b->outfilename);
+  } else {
+    strcpy(gidfilename, "unnamed");
+  }
+  strcat(gidfilename, ".face.msh");
+
+  if (!b->quiet) {
+    printf("Writing %s.\n", gidfilename);
+  }
+  outfile = fopen(gidfilename, "w");
+  if (outfile == (FILE *) NULL) {
+    printf("File I/O Error:  Cannot create file %s.\n", gidfilename);
+    return;
+  }
+
+  fprintf(outfile, "mesh dimension = 3 elemtype triangle nnode = 3\n");
+  fprintf(outfile, "coordinates\n");
+
+  points->traversalinit();
+  pointloop = pointtraverse();
+  pointnumber = 1;                        // Gid need start number form 1.
+  while (pointloop != (point) NULL) {
+    // Point coordinates.
+    fprintf(outfile, "%4d  %.17g %.17g %.17g", pointnumber,
+            pointloop[0], pointloop[1], pointloop[2]);
+    if (in->numberofpointattributes > 0) {
+      // Write an attribute, ignore others if more than one.
+      fprintf(outfile, "  %.17g", pointloop[3]);
+    }
+    fprintf(outfile, "\n");
+    setpointmark(pointloop, pointnumber);
+    pointloop = pointtraverse();
+    pointnumber++;
+  }
+
+  fprintf(outfile, "end coordinates\n");
+  fprintf(outfile, "elements\n");
+
+  tetrahedrons->traversalinit();
+  tface.tet = tetrahedrontraverse();
+  elementnumber = 1;
+  while (tface.tet != (tetrahedron *) NULL) {
+    for (tface.loc = 0; tface.loc < 4; tface.loc ++) {
+      sym(tface, tsymface);
+      if ((tface.tet < tsymface.tet) || (tsymface.tet == dummytet)) {
+        p1 = org(tface);
+        p2 = dest(tface);
+        p3 = apex(tface);
+        if (tsymface.tet == dummytet) {
+          // It's a hull face, output it.
+          fprintf(outfile, "%5d   %d  %d  %d\n", elementnumber,
+                  pointmark(p1), pointmark(p2), pointmark(p3));
+          elementnumber++;
+        } else if (b->useshelles) {
+          // Only output it if it's a subface.
+          tspivot(tface, sface);
+          if (sface.sh != dummysh) {
+            fprintf(outfile, "%5d   %d  %d  %d\n", elementnumber,
+                    pointmark(p1), pointmark(p2), pointmark(p3));
+            elementnumber++;
+          }
+        }
+      }
+    }
+    tface.tet = tetrahedrontraverse();
+  }
+
+  fprintf(outfile, "end elements\n");
+  fclose(outfile);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// outmesh2off()    Write the mesh to an .off file.                          //
+//                                                                           //
+// .off, the Object File Format, is one of the popular file formats from the //
+// Geometry Center's Geomview package (http://www.geomview.org).             //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::outmesh2off(char* ofilename)
+{
+  FILE *outfile;
+  char offfilename[FILENAMESIZE];
+  triface tface, tsymface;
+  point pointloop, p1, p2, p3;
+  long faces;
+  int shift;
+
+  if (ofilename != (char *) NULL && ofilename[0] != '\0') {
+    strcpy(offfilename, ofilename);
+  } else if (b->outfilename[0] != '\0') {
+    strcpy(offfilename, b->outfilename);
+  } else {
+    strcpy(offfilename, "unnamed");
+  }
+  strcat(offfilename, ".off");
+
+  if (!b->quiet) {
+    printf("Writing %s.\n", offfilename);
+  }
+  outfile = fopen(offfilename, "w");
+  if (outfile == (FILE *) NULL) {
+    printf("File I/O Error:  Cannot create file %s.\n", offfilename);
+    return;
+  }
+
+  // Calculate the number of triangular faces in the tetrahedral mesh.
+  faces = (4l * tetrahedrons->items + hullsize) / 2l;
+
+  // Number of points, faces, and edges(not used, here show hullsize).
+  fprintf(outfile, "OFF\n%ld  %ld  %ld\n", points->items, faces, hullsize);
+
+  // Write the points.
+  points->traversalinit();
+  pointloop = pointtraverse();
+  while (pointloop != (point) NULL) {
+    fprintf(outfile, " %.17g  %.17g  %.17g\n", pointloop[0], pointloop[1],
+            pointloop[2]);
+    pointloop = pointtraverse();
+  }
+
+  // OFF always use zero as the first index.
+  shift = in->firstnumber == 1 ? 1 : 0;
+
+  tetrahedrons->traversalinit();
+  tface.tet = tetrahedrontraverse();
+  // To loop over the set of faces, loop over all tetrahedra, and look at
+  //   the four faces of each tetrahedron. If there isn't another tetrahedron
+  //   adjacent to the face, operate on the face.  If there is another adj-
+  //   acent tetrahedron, operate on the face only if the current tetrahedron
+  //   has a smaller pointer than its neighbor.  This way, each face is
+  //   considered only once.
+  while (tface.tet != (tetrahedron *) NULL) {
+    for (tface.loc = 0; tface.loc < 4; tface.loc ++) {
+      sym(tface, tsymface);
+      if ((tface.tet < tsymface.tet) || (tsymface.tet == dummytet)) {
+        p1 = org(tface);
+        p2 = dest(tface);
+        p3 = apex(tface);
+        // Face number, indices of three vertexs.
+        fprintf(outfile, "3   %4d  %4d  %4d\n", pointmark(p1) - shift,
+                pointmark(p2) - shift, pointmark(p3) - shift);
+      }
+    }
+    tface.tet = tetrahedrontraverse();
+  }
+
+  fprintf(outfile, "# Generated by %s\n", b->commandline);
+  fclose(outfile);
+}
+
+//
+// End of I/O rouitnes
+//
+
+//
+// Begin of user interaction routines
+//
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// internalerror()    Ask the user to send me the defective product.  Exit.  //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::internalerror()
+{
+  printf("  Please report this bug to sihang@mail.berlios.de. Include the\n");
+  printf("    message above, your input data set, and the exact command\n");
+  printf("    line you used to run this program, thank you.\n");
+  exit(1);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// checkmesh()    Test the mesh for topological consistency.                 //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::checkmesh()
+{
+  triface tetraloop;
+  triface oppotet, oppooppotet;
+  point tetorg, tetdest, tetapex, tetoppo;
+  point oppodest, oppoapex;
+  REAL oritest;
+  int horrors;
+
+  if (!b->quiet) {
+    printf("  Checking consistency of mesh...\n");
+  }
+  horrors = 0;
+  // Run through the list of tetrahedra, checking each one.
+  tetrahedrons->traversalinit();
+  tetraloop.tet = tetrahedrontraverse();
+  while (tetraloop.tet != (tetrahedron *) NULL) {
+    // Check all four faces of the tetrahedron.
+    for (tetraloop.loc = 0; tetraloop.loc < 4; tetraloop.loc++) {
+      tetorg = org(tetraloop);
+      tetdest = dest(tetraloop);
+      tetapex = apex(tetraloop);
+      tetoppo = oppo(tetraloop);
+      if (tetraloop.loc == 0) {             // Only test for inversion once.
+        oritest = orient3d(tetorg, tetdest, tetapex, tetoppo);
+        if (oritest >= 0.0) {
+          printf("  !! !! %s ", oritest > 0.0 ? "Inverted" : "Degenerated");
+          printtet(&tetraloop);
+          printf("  orient3d = %.17g.\n", oritest);
+          horrors++;
+        }
+      }
+      // Find the neighboring tetrahedron on this face.
+      sym(tetraloop, oppotet);
+      if (oppotet.tet != dummytet) {
+        // Check that the tetrahedron's neighbor knows it's a neighbor.
+        sym(oppotet, oppooppotet);
+        if ((tetraloop.tet != oppooppotet.tet)
+            || (tetraloop.loc != oppooppotet.loc)) {
+          printf("  !! !! Asymmetric tetra-tetra bond:\n");
+          if (tetraloop.tet == oppooppotet.tet) {
+            printf("   (Right tetrahedron, wrong orientation)\n");
+          }
+          printf("    First ");
+          printtet(&tetraloop);
+          printf("    Second (nonreciprocating) ");
+          printtet(&oppotet);
+          horrors++;
+        }
+        // Check that both tetrahedra agree on the identities
+        //   of their shared vertices.
+        if (findorg(&oppotet, tetorg)) {
+          oppodest = dest(oppotet);
+          oppoapex = apex(oppotet);
+        } else {
+          oppodest = (point) NULL;
+        }
+        if ((tetdest != oppoapex) || (tetapex != oppodest)) {
+          printf("  !! !! Mismatched face coordinates between two tetras:\n");
+          printf("    First mismatched ");
+          printtet(&tetraloop);
+          printf("    Second mismatched ");
+          printtet(&oppotet);
+          horrors++;
+        }
+      }
+    }
+    tetraloop.tet = tetrahedrontraverse();
+  }
+  if (horrors == 0) {
+    if (!b->quiet) {
+      printf("  In my studied opinion, the mesh appears to be consistent.\n");
+    }
+  } else if (horrors == 1) {
+    printf("  !! !! !! !! Precisely one festering wound discovered.\n");
+  } else {
+    printf("  !! !! !! !! %d abominations witnessed.\n", horrors);
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// checkshells()       Test the boundary mesh for topological consistency.   //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::checkshells()
+{
+  triface oppotet, oppooppotet, testtet; 
+  face shloop, segloop, spin;
+  face testsh, testseg, testshsh;
+  point shorg, shdest, segorg, segdest;
+  REAL checksign;
+  bool same;
+  int horrors;
+  int i;
+
+  if (!b->quiet) {
+    printf("  Checking consistency of the mesh boundary...\n");
+  }
+  horrors = 0;
+
+  // Run through the list of subfaces, checking each one.
+  subfaces->traversalinit();
+  shloop.sh = shellfacetraverse(subfaces);
+  while (shloop.sh != (shellface *) NULL) {
+    // Check two connected tetrahedra if they exist.
+    shloop.shver = 0;
+    stpivot(shloop, oppotet);
+    if (oppotet.tet != dummytet) {
+      tspivot(oppotet, testsh);
+      if (testsh.sh != shloop.sh) {
+        printf("  !! !! Wrong tetra-subface connection.\n");
+        printf("    Tetra: ");
+        printtet(&oppotet);
+        printf("    Subface: ");
+        printsh(&shloop);
+        horrors++;
+      }
+      if (oppo(oppotet) != (point) NULL) {
+        adjustedgering(oppotet, CCW);
+        checksign = orient3d(sorg(shloop), sdest(shloop), sapex(shloop),
+                             oppo(oppotet));
+        if (checksign >= 0.0) {
+          printf("  !! !! Wrong subface orientation.\n");
+          printf("    Subface: ");
+          printsh(&shloop);
+          horrors++;
+        }
+      }
+    }
+    sesymself(shloop);
+    stpivot(shloop, oppooppotet);
+    if (oppooppotet.tet != dummytet) {
+      tspivot(oppooppotet, testsh);
+      if (testsh.sh != shloop.sh) {
+        printf("  !! !! Wrong tetra-subface connection.\n");
+        printf("    Tetra: ");
+        printtet(&oppooppotet);
+        printf("    Subface: ");
+        printsh(&shloop);
+        horrors++;
+      }
+      if (oppotet.tet != dummytet) {
+        sym(oppotet, testtet);
+        if (testtet.tet != oppooppotet.tet) {
+          printf("  !! !! Wrong tetra-subface-tetra connection.\n");
+          printf("    Tetra 1: ");
+          printtet(&oppotet);
+          printf("    Subface: ");
+          printsh(&shloop);
+          printf("    Tetra 2: ");
+          printtet(&oppooppotet);
+          horrors++;
+        }
+      }
+      if (oppo(oppooppotet) != (point) NULL) {
+        adjustedgering(oppooppotet, CCW);
+        checksign = orient3d(sorg(shloop), sdest(shloop), sapex(shloop),
+                             oppo(oppooppotet));
+        if (checksign >= 0.0) {
+          printf("  !! !! Wrong subface orientation.\n");
+          printf("    Subface: ");
+          printsh(&shloop);
+          horrors++;
+        }
+      }
+    }
+    // Check connection between subfaces.
+    shloop.shver = 0;
+    for (i = 0; i < 3; i++) {
+      shorg = sorg(shloop);
+      shdest = sdest(shloop);
+      sspivot(shloop, testseg);
+      if (testseg.sh != dummysh) {
+        segorg = sorg(testseg);
+        segdest = sdest(testseg);
+        same = ((shorg == segorg) && (shdest == segdest)) 
+	    || ((shorg == segdest) && (shdest == segorg));
+        if (!same) {
+          printf("  !! !! Wrong subface-subsegment connection.\n");
+          printf("    Subface: ");
+          printsh(&shloop);
+          printf("    Subsegment: ");
+          printsh(&testseg);
+          horrors++;
+        } 
+      } 
+      spivot(shloop, testsh);
+      if (testsh.sh != dummysh) {
+        segorg = sorg(testsh);
+        segdest = sdest(testsh);
+        same = ((shorg == segorg) && (shdest == segdest)) 
+	    || ((shorg == segdest) && (shdest == segorg));
+        if (!same) {
+          printf("  !! !! Wrong subface-subface connection.\n");
+          printf("    Subface 1: ");
+          printsh(&shloop);
+          printf("    Subface 2: ");
+          printsh(&testsh);
+          horrors++;
+        }
+        spivot(testsh, testshsh);
+        shorg = sorg(testshsh);
+        shdest = sdest(testshsh);
+        same = ((shorg == segorg) && (shdest == segdest)) 
+	    || ((shorg == segdest) && (shdest == segorg));
+        if (!same) {
+          printf("  !! !! Wrong subface-subface connection.\n");
+          printf("    Subface 1: ");
+          printsh(&testsh);
+          printf("    Subface 2: ");
+          printsh(&testshsh);
+          horrors++;
+        }
+        if (testseg.sh == dummysh) {
+          if (testshsh.sh != shloop.sh) {
+            printf("  !! !! Wrong subface-subface connection.\n");
+            printf("    Subface 1: ");
+            printsh(&shloop);
+            printf("    Subface 2: ");
+            printsh(&testsh);
+            horrors++;
+          }
+        } 
+      }
+      senextself(shloop);
+    }
+    shloop.sh = shellfacetraverse(subfaces);
+  }
+
+  // Run through the list of subsegs, checking each one.
+  subsegs->traversalinit();
+  segloop.sh = shellfacetraverse(subsegs);
+  while (segloop.sh != (shellface *) NULL) {
+    segorg = sorg(segloop);
+    segdest = sdest(segloop);
+    spivot(segloop, testsh);
+    if (testsh.sh == dummysh) {
+      printf("  !! !! Wrong subsegment-subface connection.\n");
+      printf("    Subsegment: ");
+      printsh(&segloop);
+      horrors++;
+      segloop.sh = shellfacetraverse(subsegs);
+      continue;
+    }
+    shorg = sorg(testsh);
+    shdest = sdest(testsh);
+    same = ((shorg == segorg) && (shdest == segdest)) 
+        || ((shorg == segdest) && (shdest == segorg));
+    if (!same) {
+      printf("  !! !! Wrong subsegment-subface connection.\n");
+      printf("    Subsegment : ");
+      printsh(&segloop);
+      printf("    Subface : ");
+      printsh(&testsh);
+      horrors++;
+      segloop.sh = shellfacetraverse(subsegs);
+      continue;
+    }
+    // Check the connection of face loop around this subsegment.
+    spin = testsh;
+    i = 0;
+    do {
+      spivotself(spin);
+      shorg = sorg(spin);
+      shdest = sdest(spin);
+      same = ((shorg == segorg) && (shdest == segdest)) 
+          || ((shorg == segdest) && (shdest == segorg));
+      if (!same) {
+        printf("  !! !! Wrong subsegment-subface connection.\n");
+        printf("    Subsegment : ");
+        printsh(&segloop);
+        printf("    Subface : ");
+        printsh(&testsh);
+        horrors++;
+        break;
+      }
+      i++;
+    } while (spin.sh != testsh.sh && i < 1000);
+    if (i >= 1000) {
+      printf("  !! !! Wrong subsegment-subface connection.\n");
+      printf("    Subsegment : ");
+      printsh(&segloop);
+      horrors++;
+    }
+    segloop.sh = shellfacetraverse(subsegs);
+  }
+  if (horrors == 0) {
+    if (!b->quiet) {
+      printf("  Mesh boundaries connected correctly.\n");
+    }
+  } else {
+    printf("  !! !! !! !! %d boundary connection viewed with horror.\n",
+           horrors);
+    return;
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// checkdelaunay()    Ensure that the mesh is constrained Delaunay.          //
+//                                                                           //
+// If 'flipqueue' is not NULL, non-locally Delaunay faces are saved in it.   //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::checkdelaunay(queue* flipqueue)
+{
+  triface tetraloop;
+  triface oppotet;
+  face opposhelle;
+  point tetorg, tetdest, tetapex, tetoppo;
+  point oppooppo;
+  REAL sign;
+  int shouldbedelaunay;
+  int horrors;
+
+  if (!b->quiet) {
+    printf("  Checking Delaunay property of the mesh...\n");
+  }
+  horrors = 0;
+  // Run through the list of triangles, checking each one.
+  tetrahedrons->traversalinit();
+  tetraloop.tet = tetrahedrontraverse();
+  while (tetraloop.tet != (tetrahedron *) NULL) {
+    // Check all four faces of the tetrahedron.
+    for (tetraloop.loc = 0; tetraloop.loc < 4; tetraloop.loc++) {
+      tetorg = org(tetraloop);
+      tetdest = dest(tetraloop);
+      tetapex = apex(tetraloop);
+      tetoppo = oppo(tetraloop);
+      sym(tetraloop, oppotet);
+      oppooppo = oppo(oppotet);
+      // Only test that the face is locally Delaunay if there is an
+      //   adjoining tetrahedron whose pointer is larger (to ensure that
+      //   each pair isn't tested twice).
+      shouldbedelaunay = (oppotet.tet != dummytet)
+                          && (tetoppo != (point) NULL)
+                          && (oppooppo != (point) NULL)
+                          && (tetraloop.tet < oppotet.tet);
+      if (checksubfaces && shouldbedelaunay) {
+        // If a shell edge separates the triangles, then the edge is
+        //   constrained, so no local Delaunay test should be done.
+        tspivot(tetraloop, opposhelle);
+        if (opposhelle.sh != dummysh){
+          shouldbedelaunay = 0;
+        }
+      }
+      if (shouldbedelaunay) {
+        sign = insphere(tetdest, tetorg, tetapex, tetoppo, oppooppo);
+        if (checksubfaces && sign > 0.0) {
+          if (iscospheric(tetdest, tetorg, tetapex, tetoppo, oppooppo,
+                          b->epsilon)) sign = 0.0;
+        }
+        if (sign > 0.0) {
+          if (flipqueue) {
+            enqueueflipface(tetraloop, flipqueue);
+          } else {
+            printf("  !! Non-locally Delaunay face (%d, %d, %d).\n",
+                   pointmark(tetorg), pointmark(tetdest), pointmark(tetapex));
+          }
+          horrors++;
+        }
+      }
+    }
+    tetraloop.tet = tetrahedrontraverse();
+  }
+  if (flipqueue == (queue *) NULL) {
+    if (horrors == 0) {
+      if (!b->quiet) {
+        printf("  The mesh is %s.\n",
+               checksubfaces ? "constrained Delaunay" : "Delaunay");
+      }
+    } else {
+      printf("  !! !! !! !! %d obscenities viewed with horror.\n", horrors);
+    }
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// checkconforming()    Ensure that the mesh is conforming Delaunay.         //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::checkconforming()
+{
+  face segloop, shloop;
+  int encsubsegs, encsubfaces;
+
+  if (!b->quiet) {
+    printf("  Checking conforming Delaunay property of mesh...\n");
+  }
+  encsubsegs = encsubfaces = 0;
+  // Run through the list of subsegments, check each one.
+  subsegs->traversalinit();
+  segloop.sh = shellfacetraverse(subsegs);
+  while (segloop.sh != (shellface *) NULL) {
+    if (checkseg4encroach(&segloop, NULL, false)) {
+      printf("  !! !! Non-conforming subsegment: ");
+      printsh(&segloop);
+      encsubsegs++;
+    }
+    segloop.sh = shellfacetraverse(subsegs);
+  }
+  // Run through the list of subfaces, check each one.
+  subfaces->traversalinit();
+  shloop.sh = shellfacetraverse(subfaces);
+  while (shloop.sh != (shellface *) NULL) {
+    if (checksub4encroach(&shloop, NULL, false)) {
+      printf("  !! !! Non-conforming subface: ");
+      printsh(&shloop);
+      encsubfaces++;
+    }
+    shloop.sh = shellfacetraverse(subfaces);
+  }
+  if (encsubsegs == 0 && encsubfaces == 0) {
+    if (!b->quiet) {
+      printf("  The mesh is conforming Delaunay.\n");
+    }
+  } else {
+    if (encsubsegs > 0) {
+      printf("  !! !! %d subsegments are non-conforming.\n", encsubsegs);
+    }
+    if (encsubfaces > 0) {
+      printf("  !! !! %d subfaces are non-conforming.\n", encsubfaces);
+    }
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// qualitystatistics()    Print statistics about the quality of the mesh.    //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::qualitystatistics()
+{
+  triface tetloop;
+  point p[4];
+  char sbuf[128];
+  REAL radiusratiotable[12];
+  REAL aspectratiotable[16];
+  REAL dx[6], dy[6], dz[6];
+  REAL edgelength[6];
+  REAL alldihed[6];
+  REAL cent[3];
+  REAL shortest, longest;
+  REAL smallestvolume, biggestvolume;
+  REAL smallestdiangle, biggestdiangle;
+  REAL tetvol;
+  REAL tetlongest2;
+  REAL minaltitude;
+  REAL cirradius, insradius;
+  REAL shortlen, longlen;
+  REAL tetaspect, tetradius;
+  REAL smalldiangle, bigdiangle;
+  int radiustable[12];
+  int aspecttable[16];
+  int dihedangletable[18];
+  int radiusindex;
+  int aspectindex;
+  int tendegree;
+  int i, j, k;
+
+  printf("Mesh quality statistics:\n\n");
+
+  radiusratiotable[0]  =    0.707;    radiusratiotable[1]  =     1.0;
+  radiusratiotable[2]  =      1.1;    radiusratiotable[3]  =     1.2;
+  radiusratiotable[4]  =      1.4;    radiusratiotable[5]  =     1.6;
+  radiusratiotable[6]  =      1.8;    radiusratiotable[7]  =     2.0;
+  radiusratiotable[8]  =      2.5;    radiusratiotable[9]  =     3.0;
+  radiusratiotable[10] =     10.0;    radiusratiotable[11] =     0.0;
+
+  aspectratiotable[0]  =      1.5;    aspectratiotable[1]  =     2.0;
+  aspectratiotable[2]  =      2.5;    aspectratiotable[3]  =     3.0;
+  aspectratiotable[4]  =      4.0;    aspectratiotable[5]  =     6.0;
+  aspectratiotable[6]  =     10.0;    aspectratiotable[7]  =    15.0;
+  aspectratiotable[8]  =     25.0;    aspectratiotable[9]  =    50.0;
+  aspectratiotable[10] =    100.0;    aspectratiotable[11] =   300.0;
+  aspectratiotable[12] =   1000.0;    aspectratiotable[13] = 10000.0;
+  aspectratiotable[14] = 100000.0;    aspectratiotable[15] =     0.0;
+  
+  for (i = 0; i < 12; i++) {
+    radiustable[i] = 0;
+  }
+  for (i = 0; i < 16; i++) {
+    aspecttable[i] = 0;
+  }
+  for (i = 0; i < 18; i++) {
+    dihedangletable[i] = 0;
+  }
+
+  minaltitude = xmax - xmin + ymax - ymin + zmax - zmin;
+  minaltitude = minaltitude * minaltitude;
+  shortest = minaltitude;
+  longest = 0.0;
+  smallestvolume = minaltitude;
+  biggestvolume = 0.0;
+  smallestdiangle = 180.0;
+  biggestdiangle = 0.0;
+
+  // Loop all elements, calculate quality parameters for each element.
+  tetrahedrons->traversalinit();
+  tetloop.tet = tetrahedrontraverse();
+  while (tetloop.tet != (tetrahedron *) NULL) {
+    p[0] = org(tetloop);
+    p[1] = dest(tetloop);
+    p[2] = apex(tetloop);
+    p[3] = oppo(tetloop);
+    tetlongest2 = 0.0;
+    
+    // Calculate the longest and shortest edge length.
+    for (i = 0; i < 3; i++) {
+      j = plus1mod3[i];
+      k = minus1mod3[i];
+      dx[i] = p[j][0] - p[k][0];
+      dy[i] = p[j][1] - p[k][1];
+      dz[i] = p[j][2] - p[k][2];
+      edgelength[i] = dx[i] * dx[i] + dy[i] * dy[i] + dz[i] * dz[i];
+      if (i == 0) {
+        shortlen = longlen = edgelength[i];
+      } else {
+        shortlen = edgelength[i] < shortlen ? edgelength[i] : shortlen;
+        longlen  = edgelength[i] > longlen  ? edgelength[i] : longlen;
+      }
+      if (edgelength[i] > tetlongest2) {
+        tetlongest2 = edgelength[i];
+      }
+      if (edgelength[i] > longest) {
+        longest = edgelength[i];
+      }
+      if (edgelength[i] < shortest) {
+        shortest = edgelength[i];
+      }
+    }
+    for (i = 3; i < 6; i++) {
+      j = i - 3;
+      k = 3;
+      dx[i] = p[j][0] - p[k][0];
+      dy[i] = p[j][1] - p[k][1];
+      dz[i] = p[j][2] - p[k][2];
+      edgelength[i] = dx[i] * dx[i] + dy[i] * dy[i] + dz[i] * dz[i];
+      shortlen = edgelength[i] < shortlen ? edgelength[i] : shortlen;
+      longlen  = edgelength[i] > longlen  ? edgelength[i] : longlen;
+      if (edgelength[i] > tetlongest2) {
+        tetlongest2 = edgelength[i];
+      }
+      if (edgelength[i] > longest) {
+        longest = edgelength[i];
+      }
+      if (edgelength[i] < shortest) {
+        shortest = edgelength[i];
+      }
+    }
+    
+    // Calculate the largest and smallest volume.
+    tetvol = orient3d(p[0], p[1], p[2], p[3]) / 6.0;
+    if (tetvol < 0) tetvol = -tetvol;
+    if (tetvol < smallestvolume) {
+      smallestvolume = tetvol;
+    }
+    if (tetvol > biggestvolume) {
+      biggestvolume = tetvol;
+    }
+    
+    // Calculate the largest and smallest dihedral angles.
+    tetalldihedral(p[0], p[1], p[2], p[3], alldihed);
+    for (i = 0; i < 6; i++) {
+      alldihed[i] = alldihed[i] * 180.0 / PI;
+      if (i == 0) {
+        smalldiangle = bigdiangle = alldihed[i];
+      } else {
+        smalldiangle = alldihed[i] < smalldiangle ? alldihed[i] : smalldiangle;
+        bigdiangle = alldihed[i] > bigdiangle ? alldihed[i] : bigdiangle;
+      }
+      if (alldihed[i] < smallestdiangle) {
+        smallestdiangle = alldihed[i];
+      } else if (alldihed[i] > biggestdiangle) {
+        biggestdiangle = alldihed[i];
+      }
+    }
+    tendegree = (int) (smalldiangle / 10.);
+    dihedangletable[tendegree]++;
+    tendegree = (int) (bigdiangle / 10.);
+    dihedangletable[tendegree]++;
+
+    // Calculate aspect ratio and radius-edge ratio for this element.
+    tetaspect = 0.0;
+    if (!circumsphere(p[0], p[1], p[2], p[3], cent, &cirradius)) {
+      // ! Very bad element.
+      tetaspect = 1.e+8;  
+      tetradius = 100.0;
+    } else { 
+      inscribedsphere(p[0], p[1], p[2], p[3], cent, &insradius);
+    }
+    if (tetaspect == 0.0) {
+      tetradius = cirradius / sqrt(shortlen);
+      tetaspect = sqrt(longlen) / (2.0 * insradius);
+      
+    }
+    aspectindex = 0;
+    while ((tetaspect > aspectratiotable[aspectindex]) && (aspectindex < 15)) {
+      aspectindex++;
+    }
+    aspecttable[aspectindex]++;
+    radiusindex = 0;
+    while ((tetradius > radiusratiotable[radiusindex]) && (radiusindex < 11)) {
+      radiusindex++;
+    }
+    radiustable[radiusindex]++;
+
+    tetloop.tet = tetrahedrontraverse();
+  }
+
+  shortest = sqrt(shortest);
+  longest = sqrt(longest);
+  minaltitude = sqrt(minaltitude);
+
+  printf("  Smallest volume: %16.5g   |  Largest volume: %16.5g\n",
+         smallestvolume, biggestvolume);
+  printf("  Shortest edge:   %16.5g   |  Longest edge:   %16.5g\n",
+         shortest, longest);
+  sprintf(sbuf, "%.17g", biggestdiangle);
+  if (strlen(sbuf) > 8) {
+    sbuf[8] = '\0';
+  }
+  printf("  Smallest dihedral: %14.5g   |  Largest dihedral:       %s\n\n",
+         smallestdiangle, sbuf);
+
+  printf("  Radius-edge ratio histogram:\n");
+  printf("         < %-6.6g    :  %8d      | %6.6g - %-6.6g     :  %8d\n",
+         radiusratiotable[0], radiustable[0], radiusratiotable[5],
+         radiusratiotable[6], radiustable[6]);
+  for (i = 1; i < 5; i++) {
+    printf("  %6.6g - %-6.6g    :  %8d      | %6.6g - %-6.6g     :  %8d\n",
+           radiusratiotable[i - 1], radiusratiotable[i], radiustable[i],
+           radiusratiotable[i + 5], radiusratiotable[i + 6],
+           radiustable[i + 6]);
+  }
+  printf("  %6.6g - %-6.6g    :  %8d      | %6.6g -            :  %8d\n",
+         radiusratiotable[4], radiusratiotable[5], radiustable[5],
+         radiusratiotable[10], radiustable[11]);
+  printf("  (A tetrahedron's radius-edge ratio is its radius of ");
+  printf("circumsphere divided\n");
+  printf("    by its shortest edge length)\n\n");
+
+  printf("  Aspect ratio histogram:\n");
+  printf("  1.1547 - %-6.6g    :  %8d      | %6.6g - %-6.6g     :  %8d\n",
+         aspectratiotable[0], aspecttable[0], aspectratiotable[7],
+         aspectratiotable[8], aspecttable[8]);
+  for (i = 1; i < 7; i++) {
+    printf("  %6.6g - %-6.6g    :  %8d      | %6.6g - %-6.6g     :  %8d\n",
+           aspectratiotable[i - 1], aspectratiotable[i], aspecttable[i],
+           aspectratiotable[i + 7], aspectratiotable[i + 8],
+           aspecttable[i + 8]);
+  }
+  printf("  %6.6g - %-6.6g    :  %8d      | %6.6g -            :  %8d\n",
+         aspectratiotable[6], aspectratiotable[7], aspecttable[7],
+         aspectratiotable[14], aspecttable[15]);
+  printf("  (A tetrahedron's aspect ratio is its longest edge length");
+  printf(" divided by the\n");
+  printf("    diameter of its inscribed sphere)\n\n");
+
+  printf("  Dihedral Angle histogram:\n");
+  for (i = 0; i < 9; i++) {
+    printf("     %3d - %2d degrees:  %8d      |    %3d - %3d degrees:  %8d\n",
+           i * 10, i * 10 + 10, dihedangletable[i],
+           i * 10 + 90, i * 10 + 100, dihedangletable[i + 9]);
+  }
+  printf("\n");
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// statistics()    Print all sorts of cool facts.                            //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetgenmesh::statistics()
+{
+  printf("\nStatistics:\n\n");
+  printf("  Input points: %d\n", in->numberofpoints);
+  if (b->refine) {
+    printf("  Input tetrahedra: %d\n", in->numberoftetrahedra);
+  }
+  if (b->plc) {
+    printf("  Input facets: %d\n", in->numberoffacets);
+    printf("  Input holes: %d\n", in->numberofholes);
+    printf("  Input regions: %d\n", in->numberofregions);
+  }
+
+  printf("\n  Mesh points: %ld\n", points->items);
+  printf("  Mesh tetrahedra: %ld\n", tetrahedrons->items);
+  if (b->plc || b->refine) {
+    printf("  Mesh faces: %ld\n", (4l * tetrahedrons->items + hullsize) / 2l);
+  }
+  if (b->plc || b->refine) {
+    printf("  Mesh subfaces: %ld\n", subfaces->items);
+    printf("  Mesh subsegments: %ld\n\n", subsegs->items);
+  } else {
+    printf("  Convex hull faces: %ld\n\n", hullsize);
+  }
+  if (b->verbose) {
+    // if (b->quality || b->removesliver) {
+    qualitystatistics();
+    // }
+    printf("\n");
+  }
+}
+
+//
+// End of user interaction routines
+//
+
+//
+// Begin of constructor and destructor of tetgenmesh
+//
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// ~tetgenmesh()    Deallocte memory occupied by a tetgenmesh object.        //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+tetgenmesh::~tetgenmesh()
+{
+  in = (tetgenio *) NULL;
+  b = (tetgenbehavior *) NULL;
+
+  if (tetrahedrons != (memorypool *) NULL) {
+    delete tetrahedrons;
+  }
+  if (subfaces != (memorypool *) NULL) {
+    delete subfaces;
+  }
+  if (subsegs != (memorypool *) NULL) {
+    delete subsegs;
+  }
+  if (points != (memorypool *) NULL) {
+    delete points;
+  }
+  if (dummytetbase != (tetrahedron *) NULL) {
+    delete [] dummytetbase;
+  }
+  if (dummyshbase != (shellface *) NULL) {
+    delete [] dummyshbase;
+  }
+  if (liftpointarray != (REAL *) NULL) {
+    delete [] liftpointarray;
+  }
+  if (highordertable != (point *) NULL) {
+    delete [] highordertable;
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// tetgenmesh()    Initialize a tetgenmesh object.                           //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+tetgenmesh::tetgenmesh()
+{
+  in = (tetgenio *) NULL;
+  b = (tetgenbehavior *) NULL;
+
+  tetrahedrons = (memorypool *) NULL;
+  subfaces = (memorypool *) NULL;
+  subsegs = (memorypool *) NULL;
+  points = (memorypool *) NULL;
+  badsubsegs = (memorypool *) NULL;
+  badsubfaces = (memorypool *) NULL;
+  badtetrahedrons = (memorypool *) NULL;
+  flipstackers = (memorypool *) NULL;
+
+  dummytet = (tetrahedron *) NULL;
+  dummytetbase = (tetrahedron *) NULL;
+  dummysh = (shellface *) NULL;
+  dummyshbase = (shellface *) NULL;
+
+  liftpointarray = (REAL *) NULL;
+  highordertable = (point *) NULL;
+
+  xmax = xmin = ymax = ymin = zmax = zmin = 0.0; 
+  longest = 0.0;
+  hullsize = 0l;
+  insegment = 0l;
+  pointmarkindex = 0;
+  point2simindex = 0;
+  highorderindex = 0;
+  elemattribindex = 0;
+  volumeboundindex = 0;
+  shmarkindex = 0;
+  areaboundindex = 0;
+  checksubfaces = 0;
+  checkquality = 0;
+  nonconvex = 0;
+  dupverts = 0;
+  samples = 0l;
+  randomseed = 0l;
+  macheps = 0.0;
+  flip23s = flip32s = flip22s = flip44s = 0l;
+}
+
+//
+// End of constructor and destructor of tetgenmesh
+//
+
+//
+// End of class 'tetgenmesh' implementation.
+//
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// tetrahedralize()    The interface for users using TetGen library to       //
+//                     generate tetrahedral meshes with all features.        //
+//                                                                           //
+// The sequence is roughly as follows.  Many of these steps can be skipped,  //
+// depending on the command line switches.                                   //
+//                                                                           //
+// - Initialize constants and parse the command line.                        //
+// - Read the vertices from a file and either                                //
+//   - tetrahedralize them (no -r), or                                       //
+//   - read an old mesh from files and reconstruct it (-r).                  //
+// - Insert the PLC segments and facets (-p).                                //
+// - Read the holes (-p), regional attributes (-pA), and regional volume     //
+//   constraints (-pa).  Carve the holes and concavities, and spread the     //
+//   regional attributes and volume constraints.                             //
+// - Enforce the constraints on minimum quality bound (-q) and maximum       //
+//   volume (-a). Also enforce the conforming Delaunay property (-q and -a). //
+// - Promote the mesh's linear tetrahedra to higher order elements (-o).     //
+// - Write the output files and print the statistics.                        //
+// - Check the consistency and Delaunay property of the mesh (-C).           //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+#include <time.h>           // Defined type clock_t, constant CLOCKS_PER_SEC.
+
+void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out)
+{
+  tetgenmesh m;
+  clock_t tv0, tv1, tv2, tv3, tv4, tv5, tv6, tv7, tv8;
+
+  if (!b->quiet) {
+    tv0 = clock();
+  }
+ 
+  m.b = b;
+  m.in = in;
+
+  m.macheps = exactinit();
+  m.initializepointpool();
+  m.initializetetshpools();
+  m.steinerleft = b->steiner;
+
+  if (!b->quiet) {
+    tv1 = clock();
+  }
+
+  m.transfernodes();
+  if (b->refine) {
+    m.reconstructmesh();
+  } else {
+    m.incrflipdelaunay();
+  }
+
+  if (!b->quiet) {
+    tv2 = clock();
+    if (b->refine) {
+      printf("Mesh reconstruction seconds:  %g\n",
+             (tv2 - tv1) / (REAL) CLOCKS_PER_SEC);
+    } else if (!b->detectinter) {
+      printf("Delaunay seconds:  %g\n", (tv2 - tv1) / (REAL) CLOCKS_PER_SEC);
+    }
+  }
+
+  if (b->useshelles && !b->refine) {
+    m.insegment = m.meshsurface();
+    if (b->detectinter) {
+      m.detectinterfaces();
+    } else {
+      if (!b->nobisect) {
+        m.delaunizesegments();
+        m.checksubfaces = 1;
+        m.constrainedfacets();
+      }
+    }
+  }
+
+  if (!b->quiet) {
+    tv3 = clock();
+    if (b->useshelles && !b->refine) {
+      if (b->detectinter) {
+        printf("Intersection seconds:  %g\n", 
+               (tv3 - tv2) / (REAL) CLOCKS_PER_SEC);  
+      } else {
+        if (!b->nobisect) {
+          printf("Segment and facet seconds:  %g\n",
+                 (tv3 - tv2) / (REAL) CLOCKS_PER_SEC);
+        }
+      }
+    } 
+  }
+
+  if (b->plc && !b->refine && !b->detectinter) {
+    if (b->checkclosure) {
+      m.indenthull();
+    } else {
+      m.carveholes(); 
+    }
+    m.nonconvex = 1;
+  }
+
+  if (!b->quiet) {
+    tv4 = clock();
+    if (b->plc && !b->refine && !b->detectinter) {
+      printf("Hole seconds:  %g\n", (tv4 - tv3) / (REAL) CLOCKS_PER_SEC);
+    }
+  }
+
+  if ((b->plc || b->refine) && !b->detectinter && !b->checkclosure) {
+    m.removeilltets(); 
+  }
+
+  if (!b->quiet) {
+    tv5 = clock();
+    if ((b->plc || b->refine) && !b->detectinter) {
+      printf("Repair seconds:  %g\n", (tv5 - tv4) / (REAL) CLOCKS_PER_SEC);
+    }
+  }
+
+  if (b->insertaddpoints) {
+    if (in->numberofaddpoints == 0) {
+      in->load_addnodes(b->infilename);
+    }
+    if (in->numberofaddpoints > 0) {
+      m.insertaddpoints(); 
+    }
+  }
+
+  if (!b->quiet) {
+    tv6 = clock();
+    if ((b->plc || b->refine) && (in->numberofaddpoints > 0)) {
+      printf("Add points seconds:  %g\n", (tv6 - tv5) / (REAL) CLOCKS_PER_SEC);
+    }
+  }
+
+  if (b->quality && (m.tetrahedrons->items > 0)) {
+    m.enforcequality();
+  }
+
+  if (!b->quiet) {
+    tv7 = clock();
+    if (b->quality && (m.tetrahedrons->items > 0)) {
+      printf("Quality seconds:  %g\n", (tv7 - tv6) / (REAL) CLOCKS_PER_SEC);
+    }
+  }
+
+  if ((b->plc || b->refine) && b->removesliver) {
+    m.removeslivers();
+  }
+
+  if (!b->quiet) {
+    tv8 = clock();
+    if ((b->plc || b->refine) && b->removesliver) {
+      printf("Sliver repair seconds:  %g\n", 
+             (tv8 - tv7) / (REAL) CLOCKS_PER_SEC);
+    }
+  }
+
+  if (b->order > 1) {
+    m.highorder();
+  }
+
+  if (!b->quiet) {
+    printf("\n");
+  }
+
+  if (out != (tetgenio *) NULL) {
+    out->firstnumber = in->firstnumber;
+    out->mesh_dim = in->mesh_dim;
+  }
+
+  if (b->nonodewritten || b->noiterationnum) {
+    if (!b->quiet) {
+      printf("NOT writing a .node file.\n");
+    }
+  } else {
+    if (b->detectinter) {
+      if (m.subfaces->items > 0l) {
+        // Only output when there are intersecting faces.
+        m.outnodes(out);
+      }
+    } else {
+      m.outnodes(out);
+    }
+  }
+
+  if (b->noelewritten) {
+    if (!b->quiet) {
+      printf("NOT writing an .ele file.\n");
+    }
+  } else {
+    if (!b->detectinter) {
+      if (m.tetrahedrons->items > 0l) {
+        m.outelements(out);
+      }
+    }
+  }
+
+  if (b->nofacewritten) {
+    if (!b->quiet) {
+      printf("NOT writing an .face file.\n");
+    }
+  } else {
+    if (b->facesout) {
+      if (m.tetrahedrons->items > 0l) {
+        // Output all faces.
+        m.outfaces(out);
+      }
+    } else {
+      if (b->detectinter) {
+        if (m.subfaces->items > 0l) {
+          // Only output when there are intersecting faces.
+          m.outsubfaces(out);
+        }
+      } else if (b->plc || b->refine) {
+        if (m.tetrahedrons->items > 0l) {
+          // Output boundary faces.
+          m.outsubfaces(out); 
+        }
+      } else {
+        if (m.tetrahedrons->items > 0l) {
+          // Output convex hull faces.
+          m.outhullfaces(out); 
+        }
+      }
+    }
+  }
+
+  if (b->edgesout && b->plc) {
+    m.outsubsegments(out); 
+  }
+
+  if (!out && b->plc && ((b->object == tetgenbehavior::OFF) ||
+                         (b->object == tetgenbehavior::PLY) ||
+                         (b->object == tetgenbehavior::STL))) {
+    m.outsmesh(b->outfilename);
+  }
+
+  if (!out && b->meditview) {
+    m.outmesh2medit(b->outfilename); 
+  }
+
+  if (!out && b->gidview) {
+    m.outmesh2gid(b->outfilename); 
+  }
+
+  if (!out && b->geomview) {
+    m.outmesh2off(b->outfilename); 
+  }
+
+  if (b->neighout) {
+    m.outneighbors(out);
+  }
+
+  if (!b->quiet) {
+    tv7 = clock();
+    printf("\nOutput seconds:  %g\n", (tv7 - tv6) / (REAL) CLOCKS_PER_SEC);
+    printf("Total running seconds:  %g\n",
+           (tv7 - tv0) / (REAL) CLOCKS_PER_SEC);
+  }
+
+  if (b->docheck) {
+    m.checkmesh();
+    if (m.checksubfaces) {
+      m.checkshells();
+    }
+    if (b->docheck > 1) {
+      m.checkdelaunay(NULL);
+      if (b->docheck > 2) {
+        if (b->quality || b->refine) {
+          m.checkconforming();
+        }
+      }
+    }
+  }
+
+  if (!b->quiet) {
+    m.statistics();
+  }
+}
+
+#ifndef TETLIBRARY
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// main()    The entrance for running TetGen from command line.              //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+int main(int argc, char *argv[])
+
+#else // with TETLIBRARY
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// tetrahedralize()    The entrance for calling TetGen from another program. //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetrahedralize(char *switches, tetgenio *in, tetgenio *out)
+
+#endif // not TETLIBRARY
+
+{
+  tetgenbehavior b;
+
+#ifndef TETLIBRARY
+
+  tetgenio in;
+  
+  if (!b.parse_commandline(argc, argv)) {
+    exit(1);
+  }
+  if (b.refine) {
+    if (!in.load_tetmesh(b.infilename)) {
+      exit(1);
+    }
+  } else {
+    if (!in.load_plc(b.infilename, (int) b.object)) {
+      exit(1);
+    }
+  }
+  tetrahedralize(&b, &in, NULL);
+
+  return 0;
+
+#else // with TETLIBRARY
+
+  if (!b.parse_commandline(switches)) {
+    exit(1);
+  }
+  tetrahedralize(&b, in, out);
+
+#endif // not TETLIBRARY
+}
diff --git a/contrib/Tetgen/tetgen.h b/contrib/Tetgen/tetgen.h
new file mode 100644
index 0000000000..cbe84252e8
--- /dev/null
+++ b/contrib/Tetgen/tetgen.h
@@ -0,0 +1,1764 @@
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// TetGen                                                                    //
+//                                                                           //
+// A Quality Tetrahedral Mesh Generator and 3D Delaunay Triangulator         //
+//                                                                           //
+// Version 1.3                                                               //
+// June 13, 2004                                                             //
+//                                                                           //
+// Copyright 2002, 2004                                                      //
+// Hang Si                                                                   //
+// Rathausstr. 9, 10178 Berlin, Germany                                      //
+// si@wias-berlin.de                                                         //
+//                                                                           //
+// You can obtain TetGen via internet: http://tetgen.berlios.de.  It may be  //
+//   freely copied, modified, and redistributed under the copyright notices  //
+//   given in the file LICENSE.                                              //
+//                                                                           //
+// TetGen is a program for generating quality tetrahedral meshes and three-  //
+//   dimensional Delaunay triangulations.  It currently computes exact       //
+//   Delaunay tetrahedralizations, constrained Delaunay tetrahedralizations, //
+//   and quality tetrahedral meshes. The latter are nicely graded and whose  //
+//   tetrahedra have radius-edge ratio bounded, and are conforming Delaunay  //
+//   if there are no input angles smaller than 60 degree.                    //
+//                                                                           //
+// TetGen incorporates a suit of geometrical and mesh generation algorithms. //
+//   A brief description of these algorithms used in TetGen can be found in  //
+//   the first section of the user's manual.  References are given for users //
+//   who are interesting in these approaches.  However, the main references  //
+//   are listed below:                                                       //
+//                                                                           //
+//   The efficient Delaunay tetrahedralization algorithm is: H. Edelsbrunner //
+//   and N. R. Shah, "Incremental Topological Flipping Works for Regular     //
+//   Triangulations". Algorithmica 15: 223-241, 1996.                        //
+//                                                                           //
+//   The constrained Delaunay tetrahedralization algorithm is described in:  //
+//   H. Si and K. Gaertner, "An Algorithm for Three-Dimensional Constrained  //
+//   Delaunay Triangles". Proceedings of the 4th International Conference on //
+//   Engineering Computational Technology, Lisbon, September 2004.           //
+//                                                                           //
+//   The Delaunay refinement algorithm is from: J. R. Shewchuk, "Tetrahedral //
+//   Mesh Generation by Delaunay Refinement". Proceedings of the 14th Annual //
+//   Symposium on Computational Geometry, pages 86-95, 1998.                 //
+//                                                                           //
+// The mesh data structure of TetGen is a combination of two types of mesh   //
+//   data structures.  The tetrahedron-based mesh data structure introduced  //
+//   by Shewchuk is eligible to implement algorithms of generating Delaunay  //
+//   tetrahedralizations. However, constrained Delaunay tetrahedralization   //
+//   and quality mesh generation algorithms require other mesh elements      //
+//   (subfaces, subsegments) be handled at the same time.  The triangle-edge //
+//   data structure from Muecke is adopted for this purpose. Handling        //
+//   these data types together is through a set of fast mesh manipulation    //
+//   primitives.  References of these two data structures are found below:   //
+//                                                                           //
+//   J. R. Shewchuk, "Delaunay Refinement Mesh Generation". PhD thesis,      //
+//   Carnegie Mellon University, 1997.                                       //
+//                                                                           //
+//   E. P. Muecke, "Shapes and Implementations in Three-Dimensional          //
+//   Geometry". PhD thesis, Univ. of Illinois, Urbana, Illinois, 1993.       //
+//                                                                           //
+// The research of mesh generation is definitly on the move. A lot of state- //
+//   of-the-art algorithms need to be implemented and evaluated.  I heartily //
+//   welcome new algorithms especially for quality conforming Delaunay mesh  //
+//   generation and anisotropic conforming Delaunay mesh generation. If you  //
+//   have any idea on new approaches, please please kindly let me know.      //
+//                                                                           //
+// TetGen is supported by the "pdelib" project of Weierstrass Institute for  //
+//   Applied Analysis and Stochastics (WIAS) in Berlin.  It is a collection  //
+//   of software components for solving non-linear partial differential      //
+//   equations including 2D and 3D mesh generators, sparse matrix solvers,   //
+//   and scientific visualization tools, etc.  For more information please   //
+//   see:   http://www.wias-berlin.de/software/pdelib.                       //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// tetgen.h                                                                  //
+//                                                                           //
+// Header file of the TetGen library. Also is the user-level header file.    //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// TetGen Library Overview                                                   //
+//                                                                           //
+// TetGen library is comprised by several data types and global functions.   //
+//                                                                           //
+// If you quickly go through this file, you will find there are only three   //
+// main data types defined,  which are "tetgenio", "tetgenbehavior", and     //
+// "tetgenmesh". Tetgenio is used to pass data into and out of mesh routines //
+// of the library;  tetgenbehavior sets the command line options selected by //
+// user and thus controls the behaviors of TetGen;  tetgenmesh, the biggest  //
+// data type I've ever defined,  contains everything for creating Delaunay   //
+// tetrahedralizations and tetrahedral meshes. These data types are defined  //
+// as C++ classes.                                                           //
+//                                                                           //
+// There are few global functions as well.  "tetrahedralize()" is the (only) //
+// user interface for calling TetGen from other programs.  Two functions     //
+// "orient3d()" and "insphere()" are incorporated from a public C code for   //
+// performing exact geometrical tests.                                       //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef tetgenH
+#define tetgenH
+
+// To compile TetGen as a library (e.g. libtet.a) but not as an executable
+//   program, define the TETLIBRARY symbol.  The library of TetGen can be
+//   linked with programs which want to call TetGen as a function.
+
+// #define TETLIBRARY
+
+// Uncomment the following line to disable assert macros. These macros are
+//   inserted in places where I hope to catch bugs. Somewhat, they slow down
+//   the speed of TetGen.  They can be ignored by adding the -DNDEBUG
+//   compiler switch or uncomment the following line 
+
+// #define NDEBUG
+
+// To insert lots of self-checks for internal errors, define the SELF_CHECK
+//   symbol.  This will slow down the program significantly. 
+
+// #define SELF_CHECK
+
+// For single precision ( which will save some memory and reduce paging ),
+//   define the symbol SINGLE by using the -DSINGLE compiler switch or by
+//   writing "#define SINGLE" below.
+//
+// For double precision ( which will allow you to refine meshes to a smaller
+//   edge length), leave SINGLE undefined.
+
+// #define SINGLE
+
+#ifdef SINGLE
+  #define REAL float
+#else
+  #define REAL double
+#endif 	// not defined SINGLE
+
+// Here is the most general used head files for all C/C++ codes
+
+#include <stdio.h>            // Standard IO: FILE, NULL, EOF, printf(), ...
+#include <stdlib.h>        // Standard lib: abort(), system(), getenv(), ...
+#include <string.h>         // String lib: strcpy(), strcat(), strcmp(), ...
+#include <math.h>                     // Math lib: sin(), sqrt(), pow(), ...
+#include <assert.h>
+ 
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// The tetgenio data type                                                    //
+//                                                                           //
+// Used to pass data into and out of the library of TetGen.                  //
+//                                                                           //
+// If you want to program with the library of TetGen, it's necessary for you //
+// to understand the tetgenio data type, while the other two data types can  //
+// be hidden through calling the global function "tetrahedralize()".  As you //
+// will see below, that basically tetgenio is nothing more than a collection //
+// of arrays. These arrays are used to store points, tetrahedra, (triangular)//
+// faces, boundary markers, and so forth.  They are used to describe data in //
+// input & output files of TetGen.  If you understand TetGen's file formats, //
+// then it is straighforward for you to understand these arrays.  The file   //
+// formats of TetGen are described in the third section of the user's manual.//
+//                                                                           //
+// Once you create an object of tetgenio, all arrays are initialized to NULL.//
+// This is done by routine "initialize()", it is automatically called by the //
+// constructor. Before you set data into these arrays, you need to allocate  //
+// enough memory for them.  After you use the object, you need to clear the  //
+// memory occupied by these arrays.  Routine "deinitialize()" will be auto-  //
+// matically called on deletion of the object. It will clear the memory in   //
+// each array if it is not a NULL. However, it assumes that the memory is    //
+// allocated by C++ operator 'new'. If you use malloc() to allocate memory,  //
+// you should free them yourself, after they're freed, call "initialize()"   //
+// once to disable "deinitialize()".                                         //
+//                                                                           //
+// In all cases, the first item in any array is stored starting at index [0].//
+// However, that item is item number `firstnumber' which may be '0' or '1'.  //
+// Be sure to set the 'firstnumber' be '1' if your indices pointing into the //
+// pointlist is starting from '1'. Default, it is initialized be '0'.        //
+//                                                                           //
+// Tetgenio also contains routines for reading and writing TetGen's files as //
+// well.  Both the library of TetGen and TetView use these routines to parse //
+// input files, i.e., .node, .poly, .smesh, .ele, .face, and .edge files.    //
+// Other routines are provided mainly for debugging purpose.                 //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+class tetgenio {
+
+  public:
+
+    // Maximum number of characters in a file name (including the null).
+    enum {FILENAMESIZE = 1024};
+
+    // Maxi. numbers of chars in a line read from a file (incl. the null).
+    enum {INPUTLINESIZE = 1024};
+
+    // The polygon data structure.  A "polygon" is a planar polygon. It can
+    //   be arbitrary shaped (convex or non-convex) and bounded by non-
+    //   crossing segments, i.e., the number of vertices it has indictes the
+    //   same number of edges.
+    // 'vertexlist' is a list of vertex indices (integers), its length is
+    //   indicated by 'numberofvertices'.  The vertex indices are odered in
+    //   either counterclockwise or clockwise way.
+    typedef struct {
+      int *vertexlist;
+      int numberofvertices;
+    } polygon;
+
+    static void init(polygon* p) {
+      p->vertexlist = (int *) NULL;
+      p->numberofvertices = 0;
+    }
+
+    // The facet data structure.  A "facet" is a planar facet.  It is used
+    //   to represent a planar straight line graph (PSLG) in two dimension.
+    //   A PSLG contains a list of polygons. It also may conatin holes in it,
+    //   indicated by a list of hole points (their coordinates).
+    typedef struct {
+      polygon *polygonlist;
+      int numberofpolygons;
+      REAL *holelist;
+      int numberofholes;
+    } facet;
+
+    static void init(facet* f) {
+      f->polygonlist = (polygon *) NULL;
+      f->numberofpolygons = 0;
+      f->holelist = (REAL *) NULL;
+      f->numberofholes = 0;
+    }
+
+  public:
+
+    // Items are numbered starting from 'firstnumber' (0 or 1), default is 0.
+    int firstnumber; 
+
+    // Dimension of the mesh (2 or 3), default is 3.
+    int mesh_dim;
+
+    // `pointlist':  An array of point coordinates.  The first point's x
+    //   coordinate is at index [0] and its y coordinate at index [1], its
+    //   z coordinate is at index [2], followed by the coordinates of the
+    //   remaining points.  Each point occupies three REALs. 
+    // `pointattributelist':  An array of point attributes.  Each point's
+    //   attributes occupy `numberofpointattributes' REALs. 
+    // 'addpointlist':  An array of additional point coordinates.
+    // `pointmarkerlist':  An array of point markers; one int per point.
+    REAL *pointlist;
+    REAL *pointattributelist;
+    REAL *addpointlist;
+    int *pointmarkerlist;
+    int numberofpoints;
+    int numberofpointattributes;
+    int numberofaddpoints;
+ 
+    // `elementlist':  An array of element (triangle or tetrahedron) corners. 
+    //   The first element's first corner is at index [0], followed by its
+    //   other corners in counterclockwise order, followed by any other
+    //   nodes if the element represents a nonlinear element.  Each element
+    //   occupies `numberofcorners' ints.
+    // `elementattributelist':  An array of element attributes.  Each
+    //   element's attributes occupy `numberofelementattributes' REALs.
+    // `elementconstraintlist':  An array of constraints, i.e. triangle's
+    //   area or tetrahedron's volume; one REAL per element.  Input only.
+    // `neighborlist':  An array of element neighbors; 3 or 4 ints per
+    //   element.  Output only.
+    int *tetrahedronlist;
+    REAL *tetrahedronattributelist;
+    REAL *tetrahedronvolumelist;
+    int *neighborlist;
+    int numberoftetrahedra;
+    int numberofcorners;
+    int numberoftetrahedronattributes;
+
+    // `facetlist':  An array of facets.  Each entry is a structure of facet.
+    // `facetmarkerlist':  An array of facet markers; one int per facet.
+    facet *facetlist;
+    int *facetmarkerlist;
+    int numberoffacets;
+
+    // `holelist':  An array of holes.  The first hole's x, y and z
+    //   coordinates  are at indices [0], [1] and [2], followed by the
+    //   remaining holes. Three REALs per hole. 
+    REAL *holelist;
+    int numberofholes;
+
+    // `regionlist':  An array of regional attributes and area or volume
+    //   constraints. The first constraint's x, y and z coordinates are at
+    //   indices [0], [1] and [2], followed by the regional attribute and
+    //   index [3], followed by the maximum area or volume at index [4],
+    //   followed by the remaining area or volume constraints. Five REALs
+    //   per constraint. 
+    // Note that each regional attribute is used only if you select the `A'
+    //   switch, and each constraint is used only if you select the `a'
+    //   switch (with no number following), but omitting one of these
+    //   switches does not change the memory layout.
+    REAL *regionlist;
+    int numberofregions;
+
+    // `trifacelist':  An array of triangular face endpoints.  The first
+    //   face's endpoints are at indices [0], [1] and [2], followed by the
+    //   remaining faces.  Three ints per face.
+    // `trifacemarkerlist':  An array of face markers; one int per face.
+    int *trifacelist;
+    int *trifacemarkerlist;
+    int numberoftrifaces;
+
+    // `edgelist':  An array of edge endpoints.  The first edge's endpoints
+    //   are at indices [0] and [1], followed by the remaining edges.  Two
+    //   ints per edge.
+    // `edgemarkerlist':  An array of edge markers; one int per edge.
+    int *edgelist;
+    int *edgemarkerlist;
+    int numberofedges;
+
+  public:
+
+    // Initialize routine.
+    void initialize();
+    void deinitialize();
+
+    // Input & output routines.
+    bool load_node_call(FILE* infile, int markers, char* nodefilename);
+    bool load_node(char* filename);
+    bool load_addnodes(char* filename);
+    bool load_poly(char* filename);
+    bool load_off(char* filename);
+    bool load_ply(char* filename);
+    bool load_stl(char* filename);
+    bool load_medit(char* filename);
+    bool load_plc(char* filename, int object);
+    bool load_tetmesh(char* filename);
+    void save_nodes(char* filename);
+    void save_elements(char* filename);
+    void save_faces(char* filename);
+    void save_edges(char* filename);
+    void save_neighbors(char* filename);
+    void save_poly(char* filename);
+
+    // Read line and parse string functions.
+    char *readline(char* string, FILE* infile, int *linenumber);
+    char *findnextfield(char* string);
+    char *readnumberline(char* string, FILE* infile, char* infilename);
+    char *findnextnumber(char* string);
+
+    // Constructor and destructor.
+    tetgenio() {initialize();}
+    ~tetgenio() {deinitialize();}
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// The tetgenbehavior data type                                              //
+//                                                                           //
+// Used to parse command line switches and file names.                       //
+//                                                                           //
+// It includes a list of variables corresponding to the commandline switches //
+// for control the behavior of TetGen.  These varibales are all initialized  //
+// to their default values.                                                  //
+//                                                                           //
+// Routine "parse_commandline()" defined in this data type is used to change //
+// the vaules of the variables. This routine accepts the standard parameters //
+// ('argc' and 'argv') that pass to C/C++ main() function. It also accepts a //
+// string which contains the command line options.                           //
+//                                                                           //
+// You don't need to understand this data type. It can be implicitly called  //
+// by the global function "tetrahedralize()" defined below.  The necessary   //
+// thing you need to know is the meaning of command line switches of TetGen. //
+// They are described in the third section of the user's manual.             //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+class tetgenbehavior {
+
+  public:
+
+    // Labels define the objects which are acceptable by TetGen. They are 
+    //   recoggnized from the extensions of the input filenames.
+    //   - 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);
+    //   - 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 commandline switch
+    //   (-p or -r) implies the object. 
+
+    enum objecttype {NONE, NODES, POLY, OFF, PLY, STL, MEDIT, MESH};
+
+    // Variables of command line switches.  After each variable are the 
+    //   corresponding switch and its default value.  Read the user's manul
+    //   or online instructions to find out the meaning of these switches.
+
+    int plc;                                              // '-p' switch, 0.
+    int refine;                                           // '-r' switch, 0.
+    int quality;                                          // '-q' switch, 0.
+    REAL minratio;                         // number after '-q' switch, 2.0.
+    REAL goodratio;               // number calculated from 'minratio', 0.0. 
+    REAL minangle;                             // minimum angle bound, 20.0.
+    REAL goodangle;                      // cosine squared of minangle, 0.0.
+    int varvolume;                         // '-a' switch without number, 0.
+    int fixedvolume;                          // '-a' switch with number, 0.
+    REAL maxvolume;                       // number after '-a' switch, -1.0.
+    int removesliver;                                     // '-s' switch, 0.
+    REAL maxdihedral;                      // number after '-s' switch, 0.0.
+    int insertaddpoints;                                  // '-i' switch, 0.
+    int regionattrib;                                     // '-A' switch, 0.
+    REAL epsilon;                       // number after '-T' switch, 1.0e-8.
+    int nomerge;           // not merge two coplanar facets, '-M' switch, 0.
+    int detectinter;                                      // '-d' switch, 0.
+    int checkclosure;                                     // '-c' switch, 0.
+    int zeroindex;                                        // '-z' switch, 0.
+    int jettison;                                         // '-j' switch, 0.
+    int order;             // element order, specified after '-o' switch, 1.
+    int facesout;                                         // '-f' switch, 0.
+    int edgesout;                                         // '-e' switch, 0.
+    int neighout;                                         // '-n' switch, 0.
+    int meditview;                                        // '-g' switch, 0.
+    int gidview;                                          // '-G' switch, 0.
+    int geomview;                                         // '-O' switch, 0.
+    int nobound;                                          // '-B' switch, 0.
+    int nonodewritten;                                    // '-N' switch, 0.
+    int noelewritten;                                     // '-E' switch, 0.
+    int nofacewritten;                                    // '-F' switch, 0.
+    int noiterationnum;                                   // '-I' switch, 0.
+    int nobisect;          // count of how often '-Y' switch is selected, 0.
+    int noflip;                     // do not perform flips. '-Y' switch. 0.
+    int steiner;                             // number after '-S' switch. 0.
+    int dopermute;                        // do permutation. '-P' switch, 0.
+    int srandseed;          // number of a random seed after '-P' switch, 1.
+    int docheck;                                          // '-C' switch, 0.
+    int quiet;                                            // '-Q' switch, 0.
+    int verbose;           // count of how often '-V' switch is selected, 0.
+    int useshelles;              // '-p', '-r', '-q', 'd', or 'c' switch, 0.
+    enum objecttype object;         // determined by -p, or -r switch. NONE.
+
+    // Variables used to save command line switches and in/out file names.
+    char commandline[1024];
+    char infilename[1024];
+    char outfilename[1024];
+
+    // Default initialize and de-initialize functions.
+    tetgenbehavior();
+    ~tetgenbehavior() {}
+
+    void versioninfo();
+    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);
+    }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// Geometric predicates                                                      //
+//                                                                           //
+// TetGen uses two basic geometric predicates, which are orientation test,   //
+// and locally Delaunay test (insphere test).                                //
+//                                                                           //
+// Orientation test:  let a, b, c be a sequence of 3 points in R^3 and are   //
+// not collinear, there exists a unique plane H passes through them. Let H+  //
+// H- be the two spaces separated by H, which are defined as follows (using  //
+// left-hand rule): make a fist using your left hand in such a way that your //
+// fingers follow the order of a, b and c, then your thumb is pointing to H+.//
+// The orientation test is to determine whether another point d lies in H+   //
+// (also say that d has positive orientation), or in H- (also say that d has //
+// negative orientation), or on H (zero orientation).                        //
+//                                                                           //
+// Locally Delaunay test (insphere test):  let a, b, c, d be 4 points in R^3 //
+// and are not coplanar, there exists a unique circumsphere S passes through //
+// these 4 points.  The task is to check if another point e lies outside, on //
+// or inside S.                                                              //
+//                                                                           //
+// The following routines use arbitrary precision floating-point arithmetic  //
+// to implement these geometric predicates. They are fast and robust. It is  //
+// provided by J. R. Schewchuk in public domain. See the following link:     //
+// http://www.cs.cmu.edu/~quake/robust.html.  The source code are found in a //
+// separate file "predicates.cxx".                                           //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+REAL exactinit();
+REAL orient3d(REAL *pa, REAL *pb, REAL *pc, REAL *pd);
+REAL insphere(REAL *pa, REAL *pb, REAL *pc, REAL *pd, REAL *pe);
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// The tetgenmesh data type                                                  //
+//                                                                           //
+// Includes data types and mesh routines for Delaunay tetrahedralizations    //
+// and tetrahedral meshes.                                                   //
+//                                                                           //
+// An object of tetgenmesh can be used to store a triangular or tetrahedral  //
+// mesh and its settings. TetGen's functions operates on one mesh each time. //
+// This type allows reusing of the same function for different meshes.       //
+//                                                                           //
+// The mesh data structure (tetrahedron-based and triangle-edge data struct- //
+// ures) are declared in this data type. There are other accessary data type //
+// defined as well, they are used for efficient memory management and fast   //
+// link list operations, etc.                                                //
+//                                                                           //
+// All algorithms TetGen used are implemented in this data type as member    //
+// functions. References of these algorithms can be found in user's manual.  //
+//                                                                           //
+// You don't need to study this data type if you only want to use TetGen's   //
+// library to create tetrahedral mesh. The global function "tetrahedralize()"//
+// implicitly creates the object and calls its member functions due to the   //
+// command line switches you used.                                           //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+class tetgenmesh {
+
+  public:
+
+    // Maximum number of characters in a file name (including the null).
+    enum {FILENAMESIZE = 1024};
+
+    // For efficiency, a variety of data structures are allocated in bulk.
+    //   The following constants determine how many of each structure is
+    //   allocated at once.
+    enum {VERPERBLOCK = 4092, SUBPERBLOCK = 4092, ELEPERBLOCK = 8188};
+
+    // Used for the point location scheme of Mucke, Saias, and Zhu, to
+    //   decide how large a random sample of tetrahedra to inspect.
+    enum {SAMPLEFACTOR = 11};
+
+    // Labels that signify two edge rings of a triangle defined in Muecke's
+    //   triangle-edge data structure, one (CCW) traversing edges in count-
+    //   erclockwise direction and one (CW) in clockwise direction.
+    enum {CCW = 0, CW = 1};
+
+    // Labels that signify whether a record consists primarily of pointers
+    //   or of floating-point words.  Used to make decisions about data
+    //   alignment.
+    enum wordtype {POINTER, FLOATINGPOINT};
+
+    // Labels that signify the type of a vertex. An UNUSEDVERTEX is a vertex
+    //   read from input (.node file or tetgenio structure) or an isolated
+    //   vertex (outside the mesh).  It is the default type for a newpoint. 
+    enum verttype {UNUSEDVERTEX, NONACUTEVERTEX, ACUTEVERTEX, FREESEGVERTEX,
+                   FACETVERTEX, PROTCYLSPHVERTEX, FREECYLSPHVERTEX,
+                   PROTCYLVERTEX, PROTSPHVERTEX, FREECYLVERTEX,
+                   FREESPHVERTEX, FREESUBVERTEX, FREEVOLVERTEX,
+                   DUPLICATEDVERTEX, DEADVERTEX = -32768};
+ 
+    // Labels that signify the type of a subsegment or a subface.  An input
+    //   (sub)segment may have type NONSHARPSEGMENT, SHARPSEGMENT.  Segments
+    //   preceding with "PROT" are artificially created for protecting the
+    //   internal region of cylinders and spheres. A subface may have one of
+    //   the three types PROTCYLSUBFACE, PROTSPHSUBFACE, and NONPROTSUBFACE.
+    enum shestype {NONSHARPSEGMENT, SHARPSEGMENT, PROTCYLSEGMENT,
+                   PROTSPHSEGMENT, PROTCYLSUBFACE, PROTSPHSUBFACE,
+                   NONPROTSUBFACE};
+
+    // Labels that signify the type of flips which can be applied on a face.
+    //   A flipable face has one of the types T23, T32, T22, and T44. Types
+    //   NONCONVEX, FORBIDDEN and UNFLIPABLE indicate non-flipable faces.
+    enum fliptype {T23, T32, T22, T44, UNFLIPABLE, FORBIDDENFACE,
+                   FORBIDDENEDGE, NONCONVEX};
+
+    // Labels that signify the type of a bad tetrahedron.
+    enum badtettype {SKINNY, CAP, SLIVER, ILLEGAL};
+
+    // Labels that signify the result of triangle-triangle intersection.
+    //   The result indicates that two triangles t1 and t2 are completely
+    //   DISJOINT, or adjoint only at a vertex SHAREVERTEX, or adjoint at
+    //   an edge SHAREEDGE, or coincident SHAREFACE, or INTERSECT.
+    enum intersectresult {DISJOINT, SHAREVERTEX, SHAREEDGE, SHAREFACE,
+                          INTERSECT};
+
+    // Labels that signify the result of point location.  The result of a
+    //   search indicates that the point falls inside a tetrahedron, inside
+    //   a triangle, on an edge, on a vertex, or outside the mesh. 
+    enum locateresult {INTETRAHEDRON, ONFACE, ONEDGE, ONVERTEX, OUTSIDE};
+
+    // Labels that signify the result of vertex insertion.  The result
+    //   indicates that the vertex was inserted with complete success, was
+    //   inserted but encroaches upon a subsegment, was not inserted because
+    //   it lies on a segment, or was not inserted because another vertex
+    //   occupies the same location.
+    enum insertsiteresult {SUCCESSINTET, SUCCESSONFACE, SUCCESSONEDGE,
+                           ENCROACHINGPOINT, DUPLICATEPOINT, OUTSIDEPOINT};
+
+    // Labels that signify the result of direction finding.  The result
+    //   indicates that a segment connecting the two query points accross
+    //   an edge of the direction triangle/tetrahedron, across a face of
+    //   the direction tetrahedron, along the left edge of the direction
+    //   triangle/tetrahedron, along the right edge of the direction
+    //   triangle/tetrahedron, or along the top edge of the tetrahedron.
+    enum finddirectionresult {ACROSSEDGE, ACROSSFACE, LEFTCOLLINEAR,
+                              RIGHTCOLLINEAR, TOPCOLLINEAR, BELOWHULL};
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// The basic mesh element data structures                                    //
+//                                                                           //
+// There are four types of mesh elements: tetrahedra, subfaces, subsegments, //
+// and points,  where subfaces and subsegments are triangles and edges which //
+// appear on boundaries.  A tetrahedralization of a 3D point set comprises   //
+// tetrahedra and points;  a surface mesh of a 3D domain comprises subfaces  //
+// (triangles), subsegments and points;  and it is the elements of all the   //
+// four types consist of a tetrahedral mesh of a 3D domain.  However, TetGen //
+// uses three data types: 'tetrahedron', 'shellface', and 'point' to repres- //
+// ent the basic mesh elements.  A 'tetrahedron' is a tetrahedron; while a   //
+// 'shellface' can represent either a subface or a subsegment; and a 'point' //
+// represent a point.  Theese three data types, linked by pointers comprise  //
+// a tetrahedralization or a mesh.                                           //
+//                                                                           //
+// The data type 'tetrahedron' primarily consists of a list of four pointers //
+// to its corners, a list of four pointers to its adjoining tetrahedra, a    //
+// list of four pointers to its adjoining subfaces(when subfaces are needed).//
+// Optinoally, (depending on the selected switches), it may contain an arbi- //
+// trary number of user-defined floating-point attributes,  an optional max- //
+// imum volume constraint (-a switch), and a pointer to a list of high-order //
+// nodes (-o2 switch). Because the size of a tetrahedron is not determined   //
+// until running time, it is not simply declared as a structure.             //
+//                                                                           //
+// For purpose of storing geometric information, it is important to know the //
+// ordering of the vertices of a tetrahedron.  Let v0, v1, v2, and v3 be the //
+// four nodes corresponding to the order of their storage in a tetrahedron.  //
+// v3 always has negative orientation with respect to v0, v1, v2 (in other   //
+// words, v3 lies above the oriented plane passes through v0, v1, v2).  Let  //
+// the four faces of the tetrahedron be f0, f1, f2, and f3. Vertices of each //
+// face are stipulated as follows: f0 (v0, v1, v2), f1 (v0, v3, v1), f2 (v1, //
+// v3, v2), f3 (v2, v3, v0).  Adjoining tetrahedra as well as subfaces are   //
+// stored in the order of its faces, e.g., the first adjoining tetrahedra is //
+// the neighbor at f0, and so on.                                            //
+//                                                                           //
+// A subface is represented by the data type 'shellface'.  It has three      //
+// pointers to its vertices, three pointers to its adjoining subfaces, three //
+// pointers to subsegments, two pointers to its adjoining tetrahedra, and a  //
+// boundary marker (an integer). Furthermore, the pointers to vertices,      //
+// adjoining subfaces, and subsegments are ordered in a way that indicates   //
+// their geometric relation.  Let the three vertices according to the order  //
+// of their storage be v0, v1 and v2, respectively, and e0, e1 and e2 be the //
+// three edges, then we have: e0 (v0, v1), e1 (v1, v2), e2 (v2, v0). Adjoin- //
+// ing subfaces and subsegments are stored in the order of its edges.        //
+//                                                                           //
+// A subsegment is also represented by a 'shellface'. It has exactly the     //
+// same data fields as a subface has, but only uses some of them. It has two //
+// pointers to its endpoints, two pointers to its adjoining (and collinear)  //
+// subsegments, one pointer to a subface containing it (there may exist any  //
+// number of subfaces having it, choose one of them arbitrarily). The geome- //
+// tric relation between its endpoints and adjoining (collinear) subsegments //
+// is kept with respect to the storing order of its endpoints. The adjoining //
+// subsegment at the first endpoint is saved ahead of the other.             //
+//                                                                           //
+// The data type 'point' is relatively simple. A point is a list of floating //
+// -point numbers, starting with the x, y, and z coordinates, followed by an //
+// arbitrary number of optional user-defined floating-point attributes, an   //
+// integer boundary marker, an integer for the point type, and a pointer to  //
+// a tetrahedron. The latter is used for speeding up point location during   //
+// the mesh generation.                                                      //
+//                                                                           //
+// For a tetrahedron on a boundary (or a hull) of the mesh, some or all of   //
+// the adjoining tetrahedra may not be present. For an interior tetrahedron, //
+// often no neighboring subfaces are present,  Such absent tetrahedra and    //
+// subfaces are never represented by the NULL pointers; they are represented //
+// by two special records: `dummytet', the tetrahedron fills "outer space",  //
+// and `dummysh',  the vacuous subfaces which are omnipresent.               //
+//                                                                           //
+// Tetrahedra and adjoining subfaces are glued together through the pointers //
+// saved in each data fields of them. Subfaces and adjoining subsegments are //
+// connected in the same fashion.  However, there are no pointers directly   //
+// gluing tetrahedra and adjoining subsegments.  For the purpose of saving   //
+// space, the connections between tetrahedra and subsegments are entirely    //
+// mediated through subfaces.  The following part is an explaination of how  //
+// subfaces are connected in TetGen.                                         //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// The subface-subface and subface-subsegment connections                    //
+//                                                                           //
+// Adjoining subfaces sharing a common edge are connected in such a way that //
+// they form a face ring around the edge. It is in deed a single linked list //
+// which is cyclic, e.g., one can start from any subface in it and traverse  //
+// back. When the edge is not a subsegment, the ring only has two coplanar   //
+// subfaces which are pointing to each other. Otherwise, the face ring may   //
+// have any number of subfaces (and are not all coplanar).                   //
+//                                                                           //
+// The face ring around a subsegment is formed as follows.  Let s be a sub-  //
+// segment, and f be a subface containing s as an edge.  The direction of s  //
+// is stipulated from its first endpoint to its second (the first and second //
+// endpoints are according to their storage in s).  When the direction of s  //
+// is determined, other two edges of f are oriented following this direction.//
+// The "directional normal" of f is a ray starts from any point in f, points //
+// to the direction of the cross product of any of two edge vectors of f.    //
+//                                                                           //
+// The face ring of s is a cyclic ordered set of subfaces containing s, i.e.,//
+// F(s) = {f1, f2, ..., fn}, n >= 1.  Where the order is defined as follows: //
+// let fi, fj be two faces in F(s), the "normal-angle", nangle(i,j) (range   //
+// from 0 to 360 degree) is the angle between the directional normals of fi  //
+// and fj;  that fi is in front of fj (or symbolically, fi < fj) if there    //
+// exists another fk in F(s), and nangle(k, i) < nangle(k, j). The face ring //
+// of s can be represented as: f1 < f2 < ... < fn < f1.                      //
+//                                                                           //
+// The easiest way to imagine how a face ring is formed is to use the right- //
+// hand rule.  Make a fist using your right hand with the thumb pointing to  //
+// the direction of the subsegment. The face ring is connected following the //
+// direction of your fingers.                                                //
+//                                                                           //
+// The subface and subsegment are also connected through pointers stored in  //
+// their own data fields.  Every subface has a pointer ti its adjoining sub- //
+// segment. However, a subsegment only has one pointer to a subface which is //
+// containing it. Such subface can be choosn arbitrarily, other subfaces are //
+// found through the face ring.                                              //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+    // The tetrahedron data structure.  Fields of a tetrahedron contains:
+    //   - a list of four adjoining tetrahedra;
+    //   - a list of four vertices;
+    //   - a list of four subfaces (optional, used for -p switch);
+    //   - a list of user-defined floating-point attributes (optional);
+    //   - a volume constraint (optional, used for -a switch);
+    //   - a pointer to a list of high-ordered nodes (optional, -o2 switch);
+
+    typedef REAL **tetrahedron;
+
+    // The shellface data structure.  Fields of a shellface contains:
+    //   - a list of three adjoining subfaces;
+    //   - a list of three vertices;
+    //   - a list of two adjoining tetrahedra;
+    //   - a list of three adjoining subsegments;
+    //   - a pointer to a badface containing it (optional, used for -q);
+    //   - an area constraint (optional, used for -q);
+    //   - an integer for boundary marker;
+    //   - an integer for type: SHARPSEGMENT, NONSHARPSEGMENT, ...;
+
+    typedef REAL **shellface;
+
+    // The point data structure.  It is actually an array of REALs:
+    //   - x, y and z coordinates;
+    //   - a list of user-defined point attributes (optional);
+    //   - a pointer to a simplex (tet, tri, edge, or vertex);
+    //   - a pointer to a parent point (optional, used for -q);
+    //   - an integer for boundary marker;
+    //   - an integer for verttype: INPUTVERTEX, FREEVERTEX, ...;
+
+    typedef REAL *point;
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// The mesh handle (triface, face) data types                                //
+//                                                                           //
+// Two special data types, 'triface' and 'face' are defined for maintaining  //
+// and updating meshes. They are like pointers (or handles), which allow you //
+// to hold one particular part of the mesh, i.e., a tetrahedron, a triangle, //
+// an edge and a vertex.  However, these data types do not themselves store  //
+// any part of the mesh. The mesh is made of the data types defined above.   //
+//                                                                           //
+// Muecke's "triangle-edge" data structure is the prototype for these data   //
+// types.  It allows a universal representation for every tetrahedron,       //
+// triangle, edge and vertex.  For understanding the following descriptions  //
+// of these handle data structures,  readers are required to read both the   //
+// introduction and implementation detail of "triangle-edge" data structure  //
+// in Muecke's thesis.                                                       //
+//                                                                           //
+// A 'triface' represents a face of a tetrahedron and an oriented edge of    //
+// the face simultaneously.  It has a pointer 'tet' to a tetrahedron, an     //
+// integer 'loc' (range from 0 to 3) as the face index, and an integer 'ver' //
+// (range from 0 to 5) as the edge version. A face of the tetrahedron can be //
+// uniquly determined by the pair (tet, loc), and an oriented edge of this   //
+// face can be uniquly determined by the triple (tet, loc, ver).  Therefore, //
+// different usages of one triface are possible.  If we only use the pair    //
+// (tet, loc), it refers to a face, and if we add the 'ver' additionally to  //
+// the pair, it is an oriented edge of this face.                            //
+//                                                                           //
+// A 'face' represents a subface and an oriented edge of it simultaneously.  //
+// It has a pointer 'sh' to a subface, an integer 'shver'(range from 0 to 5) //
+// as the edge version.  The pair (sh, shver) determines a unique oriented   //
+// edge of this subface.  A 'face' is also used to represent a subsegment,   //
+// in this case, 'sh' points to the subsegment, and 'shver' indicates the    //
+// one of two orientations of this subsegment, hence, it only can be 0 or 1. //
+//                                                                           //
+// Mesh navigation and updating are accomplished through a set of mesh       //
+// manipulation primitives which operate on trifaces and faces.  They are    //
+// introduced below.                                                         //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+    class triface {
+
+      public:
+
+        tetrahedron* tet;
+        int loc, ver;
+
+        // Constructors;
+        triface() : tet(0), loc(0), ver(0) {}
+        // Operators;
+        triface& operator=(const triface& t) {
+          tet = t.tet; loc = t.loc; ver = t.ver;
+          return *this;
+        }
+        bool operator==(triface& t) {
+          return tet == t.tet && loc == t.loc && ver == t.ver;
+        }
+        bool operator!=(triface& t) {
+          return tet != t.tet || loc != t.loc || ver != t.ver;
+        }
+    };
+
+    class face {
+
+      public:
+
+        shellface *sh;
+        int shver;
+
+        // Constructors;
+        face() : sh(0), shver(0) {}
+        // Operators;
+        face& operator=(const face& s) {
+          sh = s.sh; shver = s.shver;
+          return *this;
+        }
+        bool operator==(face& s) {return (sh == s.sh) && (shver == s.shver);}
+        bool operator!=(face& s) {return (sh != s.sh) || (shver != s.shver);}
+    };
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// The badface structure                                                     //
+//                                                                           //
+// This structure has several usages in TetGen.  A 'badface' can represent a //
+// tetrahedron face possibly be non-locally Delaunay and will be flipped if  //
+// it is. A 'badface' can hold an encroached subsegment or subface needs to  //
+// be split in conforming Delaunay process.                                  //
+//                                                                           //
+// A badface has the following fields:  'tt' points to a tetrahedral face    //
+// which is possibly non-locally Delaunay.  'ss' points to an encroached     //
+// subsegment or subface. 'cent' is the diametric circumcent of the 'shface',//
+// Three vertices 'forg', 'fdest' and 'fapex' are stored so that one can     //
+// check whether a face is still the same.  'prevface' and 'nextface' are    //
+// used to implement a double link for managing many badfaces.               //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+    struct badface {
+      triface tt; 
+      face ss; 
+      REAL cent[3];
+      point forg, fdest, fapex, foppo; 
+      struct badface *prevface, *nextface; 
+    };
+
+    // A queue structure used to store bad tetrahedra. Each tetrahedron's
+    //   vertices are stored so that one can check whether a tetrahedron is
+    //   still the same.
+
+    struct badtetrahedron {
+      triface tet;                                             // A bad tet.
+      REAL key;                                      // radius-edge ratio^2.
+      REAL cent[3];                       // The circumcenters' coordinates.
+      point tetorg, tetdest, tetapex, tetoppo;         // The four vertices.
+      struct badtetrahedron *nexttet;            // Pointer to next bad tet.
+    };
+
+    // A stack of faces flipped during the most recent vertex insertion.
+    //   The stack is used to undo the point insertion if the point
+    //   encroaches upon other subfaces or subsegments.
+
+    struct flipstacker {
+      triface flippedface;                       // A recently flipped face.
+      enum fliptype fc;            // The flipped type T23, T32, T22 or T44.
+      point forg, fdest, fapex;          // The three vertices for checking.
+      struct flipstacker *prevflip;           // Previous flip in the stack.
+    };
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// The list, link and queue data structures                                  //
+//                                                                           //
+// These data types are used to manipulate a set of (same-typed) data items. //
+// For a given set S = {a, b, c, ...}, a list stores the elements of S in a  //
+// piece of continuous memory. It allows quickly accessing each element of S,//
+// thus is suitable for storing a fix-sized set.  While a link stores its    //
+// elements incontinuously. It allows quickly inserting or deleting one item,//
+// thus is good for storing a size-changable set.  A queue is basically a    //
+// special case of a link where one data element joins the link at the end   //
+// and leaves in an ordered fashion at the other end.                        //
+//                                                                           //
+// These data types are all implemented with dynamic memory re-allocation.   //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+    // The compfunc data type.  "compfunc" is a pointer to a linear-order
+    //   function, which takes two 'void*' arguments and returning an 'int'. 
+    //   
+    // A function: int cmp(const T &, const T &),  is said to realize a
+    //   linear order on the type T if there is a linear order <= on T such
+    //   that for all x and y in T satisfy the following relation:
+    //                 -1  if x < y.
+    //   comp(x, y) =   0  if x is equivalent to y.
+    //                 +1  if x > y.
+    typedef int (*compfunc) (const void *, const void *);
+
+    // The predefined compare functions for primitive data types.  They
+    //   take two pointers of the corresponding date type, perform the
+    //   comparation, and return -1, 0 or 1 indicating the default linear
+    //   order of them.
+
+    // Compare two 'integers'.
+    static int compare_2_ints(const void* x, const void* y);
+    // Compare two 'longs'. 
+    static int compare_2_longs(const void* x, const void* y);
+    // Compare two 'unsigned longs'. 
+    static int compare_2_unsignedlongs(const void* x, const void* y);
+
+    // The function used to determine the size of primitive data types and
+    //   set the corresponding predefined linear order functions for them.
+    static void set_compfunc(char* str, int* itembytes, compfunc* pcomp);
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// List data structure.                                                      //
+//                                                                           //
+// A 'list' is an array of items with automatically reallocation of memory.  //
+// It behaves like an array.                                                 //
+//                                                                           //
+// 'base' is the starting address of the array;  The memory unit in list is  //
+//   byte, i.e., sizeof(char). 'itembytes' is the size of each item in byte, //
+//   so that the next item in list will be found at the next 'itembytes'     //
+//   counted from the current position.                                      //
+//                                                                           //
+// 'items' is the number of items stored in list.  'maxitems' indicates how  //
+//   many items can be stored in this list. 'expandsize' is the increasing   //
+//   size (items) when the list is full.                                     //
+//                                                                           //
+// 'comp' is a pointer pointing to a linear order function for the list.     //
+//   default it is set to 'NULL'.                                            //
+//                                                                           //
+// The index of list always starts from zero, i.e., for a list L contains    //
+//   n elements, the first element is L[0], and the last element is L[n-1].  //
+//   This feature lets lists likes C/C++ arrays.                             //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+    class list {
+
+      public:
+
+        char *base;
+        int  itembytes;
+        int  items, maxitems, expandsize;
+        compfunc comp;
+
+      public:
+
+        list(int itbytes, compfunc pcomp, int mitems = 256, int exsize = 128) {
+          listinit(itbytes, pcomp, mitems, exsize);
+        }
+        list(char* str, int mitems = 256, int exsize = 128) {
+          set_compfunc(str, &itembytes, &comp);
+          listinit(itembytes, comp, mitems, exsize);
+        }
+        ~list() { free(base); }
+
+        void *operator[](int i) { return (void *) (base + i * itembytes); }
+
+        void listinit(int itbytes, compfunc pcomp, int mitems, int exsize);
+        void setcomp(compfunc compf) { comp = compf; }    
+        void clear() { items = 0; }
+        int  len() { return items; }
+        void *append(void* appitem);
+        void *insert(int pos, void* insitem);
+        void del(int pos);
+        int  hasitem(void* checkitem);
+        int  remove(void* remitem);
+        void sort();
+    }; 
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// Memorypool data structure.                                                //
+//                                                                           //
+// A type used to allocate memory.  (It is incorporated from Shewchuk's      //
+// Triangle program)                                                         //
+//                                                                           //
+// 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.                                              //
+//                                                                           //
+// itemwordtype is set to POINTER or FLOATINGPOINT, and is used to suggest   //
+//   what sort of word the record is primarily made up of.  alignbytes       //
+//   determines how new records should be aligned in memory.  itembytes and  //
+//   itemwords are the length of a record in bytes (after rounding up) and   //
+//   words.  itemsperblock is the number of items allocated at once in a     //
+//   single block.  items is the number of currently allocated items.        //
+//   maxitems is the maximum number of items that have been allocated at     //
+//   once; it is the current number of items plus the number of records kept //
+//   on deaditemstack.                                                       //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+    class memorypool {
+
+      public:
+
+        void **firstblock, **nowblock;
+        void *nextitem;
+        void *deaditemstack;
+        void **pathblock;
+        void *pathitem;
+        wordtype itemwordtype;
+        int  alignbytes;
+        int  itembytes, itemwords;
+        int  itemsperblock;
+        long items, maxitems;
+        int  unallocateditems;
+        int  pathitemsleft;
+
+      public:
+
+        memorypool();
+        memorypool(int, int, enum wordtype, int);
+        ~memorypool();
+    
+        void poolinit(int, int, enum wordtype, int);
+        void restart();
+        void *alloc();
+        void dealloc(void*);
+        void traversalinit();
+        void *traverse();
+    };  
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// Link data structure.                                                      //
+//                                                                           //
+// A 'link' is a double linked nodes. It uses the memorypool data structure  //
+// for memory management.  Following is an image of a link.                  //
+//                                                                           //
+//   head-> ____0____      ____1____      ____2____      _________<-tail     //
+//         |__next___|--> |__next___|--> |__next___|--> |__NULL___|          //
+//         |__NULL___|<-- |__prev___|<-- |__prev___|<-- |__prev___|          //
+//         |         |    |_       _|    |_       _|    |         |          //
+//         |         |    |_ Data1 _|    |_ Data2 _|    |         |          //
+//         |_________|    |_________|    |_________|    |_________|          //
+//                                                                           //
+// The unit size for storage is size of pointer, which may be 4-byte (in 32- //
+//   bit machine) or 8-byte (in 64-bit machine). The real size of an item is //
+//   stored in 'linkitembytes'.                                              //
+//                                                                           //
+// 'head' and 'tail' are pointers pointing to the first and last nodes. They //
+//   do not conatin data (See above).                                        //
+//                                                                           //
+// 'nextlinkitem' is a pointer pointing to a node which is the next one will //
+//   be traversed. 'curpos' remembers the position (1-based) of the current  //
+//   traversing node.                                                        //
+//                                                                           //
+// 'linkitems' indicates how many items in link. Note it is different with   //
+//   'items' of memorypool.                                                  //
+//                                                                           //
+// The index of link starts from 1, i.e., for a link K contains n elements,  //
+//   the first element of the link is K[1], and the last element is K[n].    //
+//   See the above figure.                                                   //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+    class link : public memorypool {
+
+      public:
+
+        void **head, **tail;
+        void *nextlinkitem;
+        int  linkitembytes;
+        int  linkitems;
+        int  curpos;
+        compfunc comp;
+
+      public:
+
+        link(int _itembytes, compfunc _comp, int itemcount) {
+          linkinit(_itembytes, _comp, itemcount);
+        }
+        link(char* str, int itemcount) {
+          set_compfunc(str, &linkitembytes, &comp);
+          linkinit(linkitembytes, comp, itemcount);
+        }
+
+        void linkinit(int _itembytes, compfunc _comp, int itemcount);
+        void setcomp(compfunc compf) { comp = compf; }
+        void rewind() { nextlinkitem = *head; curpos = 1; }
+        void goend() { nextlinkitem = *(tail + 1); curpos = linkitems; }    
+        long len() { return linkitems; }
+        void clear();
+        bool move(int numberofnodes);
+        bool locate(int pos);
+        void *add(void* newitem);
+        void *insert(int pos, void* insitem);
+        void *del(void* delitem);
+        void *del(int pos);
+        void *getitem();
+        void *getnitem(int pos);
+        int  hasitem(void* checkitem);
+    };
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// Queue data structure.                                                     //
+//                                                                           //
+// A 'queue' is a basically a link.  Following is an image of a queue.       //
+//              ___________     ___________     ___________                  //
+//   Pop() <-- |_         _|<--|_         _|<--|_         _| <-- Push()      //
+//             |_  Data0  _|   |_  Data1  _|   |_  Data2  _|                 //
+//             |___________|   |___________|   |___________|                 //
+//              queue head                       queue tail                  //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+    class queue : public link {
+
+      public:
+
+        queue(int bytes, int count = 256) : link(bytes, NULL, count) {}
+        queue(char* str, int count = 256) : link(str, count) {}
+
+        int  empty() { return linkitems == 0; }
+        void *push(void* newitem) { return link::add(newitem); }
+        void *bot() { return link::getnitem(1); }
+        void *pop() { return link::del(1); }
+    };
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// Following are variables used in 'tetgenmesh' for miscellaneous purposes.  //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+    // Pointer to an object of 'tetgenio', which contains input data.
+    tetgenio *in;
+
+    // Pointer to an object of 'tetgenbehavor', which contains the user-
+    //   defined command line swithes and filenames.
+    tetgenbehavior *b;
+
+    // Variables used to allocate and access memory for tetrahedra, subfaces
+    //   subsegments, points, encroached subfaces, encroached subsegments,
+    //   bad-quality tetrahedra, and so on.
+    memorypool *tetrahedrons;
+    memorypool *subfaces;
+    memorypool *subsegs;
+    memorypool *points;   
+    memorypool *badsubsegs;
+    memorypool *badsubfaces;
+    memorypool *badtetrahedrons;
+    memorypool *flipstackers;
+
+    // Pointer to a recently visited tetrahedron. Improves point location
+    //   if proximate points are inserted sequentially.
+    triface recenttet;
+
+    // Pointer to the 'tetrahedron' that occupies all of "outer space".
+    tetrahedron *dummytet;
+    tetrahedron *dummytetbase; // Keep base address so we can free it later.
+
+    // Pointer to the omnipresent subface.  Referenced by any tetrahedron,
+    //   or subface that isn't connected to a subface at that location.
+    shellface *dummysh;
+    shellface *dummyshbase;    // Keep base address so we can free it later.
+
+    // List of lifting points of facets used for surface triangulation.
+    REAL *liftpointarray;
+
+    // List used for Delaunay refinement algorithm. 
+    list *qualchecktetlist;
+
+    // Queues that maintain the bad (badly-shaped or too large) tetrahedra.
+    //   The tails are pointers to the pointers that have to be filled in to
+    //   enqueue an item.  The queues are ordered from 63 (highest priority)
+    //   to 0 (lowest priority).
+    badface *subquefront[2], **subquetail[2];
+    badtetrahedron *tetquefront[64], **tetquetail[64];
+
+    // Array (size = numberoftetrahedra * 6) for storing high-order nodes of
+    //   tetrahedra (only used when -o2 switch is selected).
+    point *highordertable;
+
+    REAL xmax, xmin, ymax, ymin, zmax, zmin;      // Bounding box of points.
+    REAL longest;                       // The longest possible edge length.
+    long hullsize;                        // Number of faces of convex hull.
+    long insegment;                             // Number of input segments.
+    int steinerleft;               // Number of Steiner points not yet used.
+    int pointmarkindex;         // Index to find boundary marker of a point.
+    int point2simindex;      // Index to find a simplex adjacent to a point.
+    int highorderindex; // Index to find extra nodes for highorder elements.
+    int elemattribindex;       // Index to find attributes of a tetrahedron.
+    int volumeboundindex;    // Index to find volume bound of a tetrahedron.
+    int shmarkindex;          // Index to find boundary marker of a subface.
+    int areaboundindex;            // Index to find area bound of a subface.
+    int checksubfaces;                // Are there subfaces in the mesh yet?
+    int checkquality;                // Has quality triangulation begun yet?
+    int nonconvex;                            // Is current mesh non-convex?
+    int dupverts;                          // Are there duplicated vertices?
+    long samples;            // Number of random samples for point location.
+    unsigned long randomseed;                 // Current random number seed.
+    REAL macheps;                                    // The machine epsilon.
+    long flip23s, flip32s, flip22s, flip44s;   // Number of flips performed.
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// Fast lookup tables for mesh manipulation primitives.                      //
+//                                                                           //
+// Mesh manipulation primitives (given below) are basic operations on mesh   //
+// data structures. They answer basic queries on mesh handles, such as "what //
+// is the origin (or destination, or apex) of the face?", "what is the next  //
+// (or previous) edge in the edge ring?", and "what is the next face in the  //
+// face ring?", and so on.                                                   //
+//                                                                           //
+// The implementation of basic queries can take advangtage of the fact that  //
+// the mesh data structures additionally store geometric informations.  For  //
+// example, we have ordered the four vertices (from 0 to 3) and four faces   //
+// (from 0 to 3) of a tetrahedron,  and for each face of the tetrahedron, a  //
+// sequence of vertices has stipulated,  therefore the origin of any face of //
+// the tetrahedron can be quickly determined by a table 'locver2org', which  //
+// takes the index of the face and the edge version as inputs.  A list of    //
+// fast lookup tables are defined below. They're just like global variables. //
+// All tables are initialized once at the runtime and used by all objects of //
+// tetgenmesh.                                                               //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+    // For enext() primitive, uses 'ver' as the index. 
+    static int ve[6];
+
+    // For org(), dest() and apex() primitives, uses 'ver' as the index.
+    static int vo[6], vd[6], va[6];
+
+    // For org(), dest() and apex() primitives, uses 'loc' as the first
+    //   index and 'ver' as the second index.
+    static int locver2org[4][6];
+    static int locver2dest[4][6];
+    static int locver2apex[4][6];
+
+    // For oppo() primitives, uses 'loc' as the index.
+    static int loc2oppo[4];
+
+    // For fnext() primitives, uses 'loc' as the first index and 'ver' as
+    //   the second index,  returns an array containing a new 'loc' and a
+    //   new 'ver'. Note: Only valid for 'ver' equals one of {0, 2, 4}.
+    static int locver2nextf[4][6][2];
+
+    // For enumerating three edges of a triangle.
+    static int plus1mod3[3];
+    static int minus1mod3[3];
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// Mesh manipulation primitives                                              //
+//                                                                           //
+// A serial of mesh operations such as topological maintenance,  navigation, //
+// local modification, etc.,  is accomplished through a set of mesh manipul- //
+// ation primitives. These primitives are indeed very simple functions which //
+// take one or two handles ('triface's and 'face's) as parameters,  perform  //
+// basic operations such as "glue two tetrahedra at a face",  "return the    //
+// origin of a tetrahedron", "return the subface adjoining at the face of a  //
+// tetrahedron", and so on.                                                  //
+//                                                                           //
+// In the following, symbols t, t1, and t2 denote handles of type 'triface', //
+// i.e., t is a face of a tetrahedron. Likewise, handles of type 'face' are  //
+// denoted by s, s1, s2; e denotes an oriented edge, and v denotes a vertex. //
+//                                                                           //
+// The basic primitives for tetrahedra are:                                  //
+//                                                                           //
+//   sym(t1, t2)      t1 and t2 refer to the same face but point to two      //
+//                    different tetrahedra respectively.                     //
+//   bond(t1, t2)     Bonds t1 and t2 together.  t1 and t2 should refer to   //
+//                    the same face.                                         //
+//   dissolve(t)      Detaches the adjoining tetrahedron from t. t bonds to  //
+//                    'dummytet' after this operation.                       //
+//                                                                           //
+//   v = org(t)       v is the origin of t.                                  //
+//   v = dest(t)      v is the destination of t.                             //
+//   v = apex(t)      v is the apex of t.                                    //
+//   v = oppo(t)      v is the opposite of t.                                //
+//                                                                           //
+//   esym(t1, t2)     t2 is the inversed edge of t1, i.e., t1 and t2 are two //
+//                    directed edges of the same undirected edge.            //
+//   enext(t1, t2)    t2 is the successor of t1 in the edge ring.            //
+//   enext2(t1, t2)   t2 is the precessor of t1 in the edge ring.            //
+//                                                                           //
+//   fnext(t1, t2)    t2 is the successor of t1 in the face ring.            //
+//                                                                           //
+// The basic primitives for subfaces (as well as subsegments) are:           //
+//                                                                           //
+//   spivot(s1, s2)   s1 and s2 refer to the same edge but point to two      //
+//                    different subfaces respectively.                       //
+//   sbond(s1, s2)    Bonds s1 and s2 together (at an edge).                 //
+//   sbond1(s1, s2)   Only bonds s2 to s1 (but not s1 to s2).  It is used    //
+//                    for creating the face ring.                            //
+//   sdissolve(s)     Detaches the adjoining subface from s. s bonds to      //
+//                    'dummysh' after this operation.                        //
+//                                                                           //
+//   v = sorg(s)      v is the origin of s.                                  //
+//   v = sdest(s)     v is the destination of s.                             //
+//   v = sapex(s)     v is the apex of s.                                    //
+//                                                                           //
+//   sesym(s1, s2)    s2 is the inversed edge of s1.                         //
+//   senext(s1, s2)   s2 is the successor of s1 in the edge ring.            //
+//   senext2(s1, s2)  s2 is the precessor of s1 in the edge ring..           //
+//                                                                           //
+// For interacting tetrahedra and subfaces:                                  //
+//                                                                           //
+//   tspivot(t, s)    Returns the adjoining subface of t in s. s may hold    //
+//                    'dummysh' when t is an internal face.                  //
+//   stpivot(s, t)    Returns the adjoining tetrahedron of s in t. t may be  //
+//                    'dummytet'.                                            //
+//   tsbond(t, s)     Bond t and s together.  t and s must represent the     //
+//                    same face.                                             //
+//   tsdissolve(t)    Detaches the adjoining subface from t.                 //
+//   stdissolve(s)    Detaches the adjoining tetrahedron from s.             //
+//                                                                           //
+// For interacting subfaces and subsegments:                                 //
+//                                                                           //
+//   sspivot(s, e)    Returns the adjoining subsegment of s in e.            //
+//   ssbond(s, e)     Bond s and e together.  s and e must represent the     //
+//                    same edge.                                             //
+//   ssdissolve(s)    Detaches the adjoining subsegment from s.              //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+    // Primitives for tetrahedra.
+    inline void decode(tetrahedron ptr, triface& t);
+    inline tetrahedron encode(triface& t);
+    inline void sym(triface& t1, triface& t2);
+    inline void symself(triface& t);
+    inline void bond(triface& t1, triface& t2);
+    inline void dissolve(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 pointptr);
+    inline void setdest(triface& t, point pointptr);
+    inline void setapex(triface& t, point pointptr);
+    inline void setoppo(triface& t, point pointptr);
+    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 enext2(triface& t1, triface& t2);
+    inline void enext2self(triface& t);
+    inline bool fnext(triface& t1, triface& t2);
+    inline bool fnextself(triface& t);
+    inline void enextfnext(triface& t1, triface& t2);
+    inline void enextfnextself(triface& t);
+    inline void enext2fnext(triface& t1, triface& t2);
+    inline void enext2fnextself(triface& t);
+    inline void infect(triface& t);
+    inline void uninfect(triface& t);
+    inline bool infected(triface& t);
+    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);
+ 
+    // Primitives for subfaces and subsegments.
+    inline void sdecode(shellface sptr, face& s);
+    inline shellface sencode(face& s);
+    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 void sfnext(face&, face&);
+    inline void sfnextself(face&);
+    inline badface* shell2badface(face& s);
+    inline void setshell2badface(face& s, badface* value);
+    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 enum shestype shelltype(face& s);
+    inline void setshelltype(face& s, enum shestype value); 
+    inline void sinfect(face& s);
+    inline void suninfect(face& s);
+    inline bool sinfected(face& s);
+
+    // Primitives for interacting tetrahedra and subfaces.
+    inline void tspivot(triface& t, face& s);
+    inline void stpivot(face& s, triface& t);
+    inline void tsbond(triface& t, face& s);
+    inline void tsdissolve(triface& t);    
+    inline void stdissolve(face& s);
+
+    // Primitives for interacting subfaces and subsegs.
+    inline void sspivot(face& s, face& edge);
+    inline void ssbond(face& s, face& edge);
+    inline void ssdissolve(face& s);
+
+    // 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 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 point2pt(point pt);
+    inline void setpoint2pt(point pt, point value);
+    inline point point2ppt(point pt);
+    inline void setpoint2ppt(point pt, point value);
+    inline point getliftpoint(int facetmark);
+
+    // Advanced primitives.
+    inline void adjustedgering(triface& t, int direction);
+    inline void adjustedgering(face& s, int direction);
+    inline bool isdead(triface* t);
+    inline bool isdead(face* s);
+    inline bool isfacehaspoint(face* t, point testpoint);
+    inline bool isfacehasedge(face* s, point tend1, point tend2);
+    inline bool issymexist(triface* t);
+    bool getnextface(triface*, triface*);
+    void getnextsface(face*, face*);
+    void tsspivot(triface*, face*);
+    void sstpivot(face*, triface*);   
+    bool findorg(triface* t, point dorg);
+    bool findorg(face* s, point dorg);
+    void findedge(triface* t, point eorg, point edest);
+    void findedge(face* s, point eorg, point edest);
+    void findface(triface *fface, point forg, point fdest, point fapex);
+    void getonextseg(face* s, face* lseg);
+    void getseghasorg(face* sseg, point dorg);
+    point getsubsegfarorg(face* sseg);
+    point getsubsegfardest(face* sseg);
+    void printtet(triface*);
+    void printsh(face*);    
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// Primitive geometric test functions                                        //
+//                                                                           //
+// A primitive operation is a function f that maps a set Q of k objects to   //
+// +1, 0, or -1. Primitive geometric functions operater on geometric objects //
+// (points, segments, triangles, polyhedron, etc), determine the geometric   //
+// relations between them. Like the orientation of a sequence of d+1 points  //
+// in d-dimension, whether or not a point lies inside a triangle, and so on. //
+// Algorithms for solving geometric problems are always based on the answers //
+// of some primitives so that the corresponding deterministic rules can be   //
+// applied.  However, the implementation of geometric algorithms is not a    //
+// trivial task even for one which is very simple and only relies on few     //
+// primitives. The correctness of primitives is crucial for the cotrol flow. //
+//                                                                           //
+// The following functions perform various primitives geometric tests, some  //
+// perform tests with exact arithmetic and some do not.                      //
+//                                                                           //
+// The triangle-triangle intersection test is implemented with exact arithm- //
+// etic. It exactly tells whether or not two triangles in three dimensions   //
+// intersect.  Before implementing this test myself,  I tried two C codes    //
+// (implemented by Thomas Moeller and Philippe Guigue, respectively), which  //
+// are all public available and very efficient.  However both of them failed //
+// frequently.  Another unsuitable problem is that both codes only tell      //
+// whether or not two triangles are intersecting and not distinguish the     //
+// cases whether they are exactly intersecting in interior or they share a   //
+// vertex, or share an edge.  All the latter cases are acceptable and should //
+// return not intersection in TetGen.                                        //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+    // Triangle-triangle intersection tests    
+    enum intersectresult edge_vertex_collinear_inter(REAL*, REAL*, REAL*);
+    enum intersectresult edge_edge_coplanar_inter(REAL*, REAL*, REAL*,
+                                                  REAL*, REAL*);
+    enum intersectresult triangle_vertex_coplanar_inter(REAL*, REAL*, REAL*,
+                                                        REAL*, REAL*);
+    enum intersectresult triangle_edge_coplanar_inter(REAL*, REAL*, REAL*,
+                                                      REAL*, REAL*, REAL*);
+    enum intersectresult triangle_edge_inter_tail(REAL*, REAL*, REAL*, REAL*,
+                                                  REAL*, REAL, REAL);
+    enum intersectresult triangle_edge_inter(REAL*, REAL*, REAL*, REAL*,
+                                             REAL*);
+    enum intersectresult triangle_triangle_inter(REAL*, REAL*, REAL*, REAL*,
+                                                 REAL*, REAL*);
+
+    // Degenerate cases tests
+    bool iscollinear(REAL*, REAL*, REAL*, REAL epspp);
+    bool iscoplanar(REAL*, REAL*, REAL*, REAL*, REAL vol6, REAL epspp);
+    bool iscospheric(REAL*, REAL*, REAL*, REAL*, REAL*, REAL epspp);
+
+    // Linear algebra functions
+    inline REAL dot(REAL* v1, REAL* v2);
+    inline void cross(REAL* v1, REAL* v2, REAL* n);
+    void initm44(REAL a00, REAL a01, REAL a02, REAL a03,
+                 REAL a10, REAL a11, REAL a12, REAL a13,
+                 REAL a20, REAL a21, REAL a22, REAL a23,
+                 REAL a30, REAL a31, REAL a32, REAL a33, REAL M[4][4]);
+    void m4xm4(REAL m1[4][4], REAL m2[4][4]);
+    void m4xv4(REAL v2[4], REAL m[4][4], REAL v1[4]);
+    bool lu_decmp(REAL lu[3][3], int n, int* ps, REAL* d, int N);
+    void lu_solve(REAL lu[3][3], int n, int* ps, REAL* b, int N);
+
+    // Geometric quantities calculators.
+    inline REAL distance(REAL* p1, REAL* p2);
+    REAL shortdistance(REAL* p, REAL* e1, REAL* e2);
+    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);
+    void facenormal(REAL* pa, REAL* pb, REAL* pc, REAL* n, REAL* nlen);
+    void edgeorthonormal(REAL* e1, REAL* e2, REAL* op, REAL* n);
+    REAL facedihedral(REAL* pa, REAL* pb, REAL* pc1, REAL* pc2);
+    void tetalldihedral(point, point, point, point, REAL dihed[6]);
+    bool circumsphere(REAL*, REAL*, REAL*, REAL*, REAL* cent, REAL* radius);
+    void inscribedsphere(REAL*, REAL*, REAL*, REAL*, REAL* cent, REAL* radius);
+    void rotatepoint(REAL* p, REAL rotangle, REAL* p1, REAL* p2);
+    void spherelineint(REAL* p1, REAL* p2, REAL* C, REAL R, REAL p[7]);
+    void linelineint(REAL *p1,REAL *p2, REAL *p3, REAL *p4, REAL p[7]);
+
+    // Memory managment routines.
+    void dummyinit(int, int);
+    void initializepointpool();
+    void initializetetshpools();
+    void tetrahedrondealloc(tetrahedron*);
+    tetrahedron *tetrahedrontraverse();
+    void shellfacedealloc(memorypool*, shellface*);
+    shellface *shellfacetraverse(memorypool*);
+    void badfacedealloc(memorypool*, badface*);
+    badface *badfacetraverse(memorypool*);
+    void pointdealloc(point);
+    point pointtraverse();
+    void maketetrahedron(triface*);
+    void makeshellface(memorypool*, face*);
+    void makepoint(point*);
+
+    // Mesh items searching routines.
+    void makepoint2tetmap();
+    void makeindex2pointmap(point*& idx2verlist);
+    void makesegmentmap(int*& idx2seglist, shellface**& segsperverlist);
+    void makesubfacemap(int*& idx2facelist, shellface**& facesperverlist);
+    void maketetrahedronmap(int*& idx2tetlist, tetrahedron**& tetsperverlist);
+
+    // Point location routines.
+    unsigned long randomnation(unsigned int choices);
+    REAL distance2(tetrahedron* tetptr, point p);
+    enum locateresult preciselocate(point searchpoint, triface* searchtet);
+    enum locateresult locate(point searchpoint, triface* searchtet);
+    enum locateresult adjustlocate(point searchpoint, triface* searchtet,
+                                   enum locateresult precise, REAL epspp);    
+
+    // Mesh transformation routines.
+    enum fliptype categorizeface(triface& horiz);
+    void enqueueflipface(triface& checkface, queue* flipqueue);
+    void enqueueflipedge(face& checkedge, queue* flipqueue);
+    void flip23(triface* flipface, queue* flipqueue);
+    void flip32(triface* flipface, queue* flipqueue);
+    void flip22(triface* flipface, queue* flipqueue);
+    void flip22sub(face* flipedge, queue* flipqueue);
+    long flip(queue* flipqueue, flipstacker **plastflip);
+    void undoflip(flipstacker *lastflip);
+
+    void splittetrahedron(point newpoint, triface* splittet, queue* flipqueue);
+    void unsplittetrahedron(triface* splittet);
+    void splittetface(point newpoint, triface* splittet, queue* flipqueue);
+    void unsplittetface(triface* splittet);
+    void splitsubface(point newpoint, face* splitface, queue* flipqueue);
+    void unsplitsubface(face* splitsh);
+    void splittetedge(point newpoint, triface* splittet, queue* flipqueue);
+    void unsplittetedge(triface* splittet);
+    void splitsubedge(point newpoint, face* splitsh, queue* flipqueue);
+    void unsplitsubedge(face* splitsh);
+    enum insertsiteresult insertsite(point newpoint, triface* searchtet,
+                                     bool approx, queue* flipqueue);
+    void undosite(enum insertsiteresult insresult, triface* splittet, 
+                  point torg, point tdest, point tapex, point toppo);
+    void inserthullsite(point inspoint, triface* horiz, queue* flipqueue,
+                        link* hulllink, int* worklist);
+    void collectcavtets(point newpoint, list* cavtetlist);
+
+    void removetetbypeeloff(triface *badtet, queue* flipqueue);
+    void removetetbyflip32(triface *badtet, queue* flipqueue);
+    bool removetetbycflips(triface *badtet, queue* flipqueue);
+    bool removebadtet(enum badtettype bt, triface *badtet, queue* flipqueue);
+
+    // Incremental flip Delaunay triangulation routines.
+    void incrflipinit(queue* insertqueue);
+    long incrflipdelaunay();
+
+    // Surface triangulation routines.
+    enum locateresult locatesub(point searchpt, face* searchsh, point abovept);
+    long flipsub(queue* flipqueue);
+    bool incrflipinitsub(int facetidx, list* ptlist, point* idx2verlist);
+    void collectvisiblesubs(int facetidx, point inspoint, face* horiz,
+                            queue* flipqueue);
+    void incrflipdelaunaysub(int facetidx, list* ptlist, point* idx2verlist,
+                             queue* flipqueue);
+    enum finddirectionresult finddirectionsub(face* searchsh, point tend);
+    void insertsubseg(face* tri);
+    bool scoutsegmentsub(face* searchsh, point tend);
+    void delaunayfixup(face* fixupsh, int leftside);
+    void constrainededge(face* startsh, point tend);
+    void insertsegmentsub(point tstart, point tend);
+    void infecthullsub(memorypool* viri);
+    void plaguesub(memorypool* viri);
+    void carveholessub(int holes, REAL* holelist);
+    void triangulatefacet(int facetidx, list* ptlist, list* conlist,
+                          point* idx2verlist, queue* flipqueue);
+    void unifysegments();
+    void mergefacets(queue* flipqueue);
+    long meshsurface();
+
+    // Detect intersecting facets of PLC.
+    void interecursive(shellface** subfacearray, int arraysize, int axis,
+                       REAL bxmin, REAL bxmax, REAL bymin, REAL bymax,
+                       REAL bzmin, REAL bzmax, int* internum);
+    void detectinterfaces(); 
+
+    // Segments recovery routines.
+    void markacutevertices(REAL acuteangle);
+    enum finddirectionresult finddirection(triface* searchtet, point tend);
+    void getsearchtet(point p1, point p2, triface* searchtet, point* tend);
+    bool isedgeencroached(point p1, point p2, point testpt, bool degflag);
+    point scoutrefpoint(triface* searchtet, point tend);
+    point getsegmentorigin(face* splitseg);
+    point getsplitpoint(face* splitseg, point refpoint);
+    void delaunizesegments();
+
+    // Constrained Delaunay triangulation routines.
+    bool insertsubface(face* insertsh, triface* searchtet);
+    bool tritritest(triface* checktet, point p1, point p2, point p3);
+    void initializecavity(list* floorlist, list* ceillist, list* floorptlist,
+                          list* ceilptlist, link* frontlink, link* ptlink);
+    bool reducecavity(link* frontlink, link* ptlink, queue* flipqueue);
+    bool reducecavity1(link* frontlink, queue* flipqueue);
+    void triangulatecavity(list* floorlist, list* ceillist, list* floorptlist,
+                           list* ceilptlist);
+    void formmissingregion(face* missingsh, list* missingshlist,
+                           list* equatptlist, int* worklist);
+    bool scoutcrossingedge(list* missingshlist, list* boundedgelist,
+                           list* crossedgelist, int* worklist);
+    void rearrangesubfaces(list* missingshlist, list* boundedgelist,
+                           list* equatptlist, int* worklist);
+    void recoversubfaces(list* missingshlist, list* crossedgelist,
+                         list* equatptlist, int* worklist);
+    void constrainedfacets();
+
+    // Carving out holes and concavities routines.
+    void indenthull();
+    void infecthull(memorypool *viri);
+    void plague(memorypool *viri);
+    void regionplague(memorypool *viri, REAL attribute, REAL volume);
+    void carveholes();
+
+    // Mesh update rotuines.
+    long reconstructmesh();
+    void insertaddpoints();
+
+    // Delaunay refinement routines.
+    void initializerpsarray(REAL* rpsarray);
+    void marksharpfacets(int*& idx2facetlist, REAL dihedbound);
+    void enqueuebadtet(triface *instet, REAL ratio, point insorg,
+                       point insdest, point insapex, point insoppo,
+                       point inscent);
+    badtetrahedron* dequeuebadtet();
+    bool checkseg4encroach(face* testseg, point testpt, bool enqueueflag);
+    bool checksub4encroach(face* testsub, point testpt, bool enqueueflag);
+    bool checksub4badqual(face* testsub);
+    bool checktet4badqual(triface* testtet);
+    bool checktet4illtet(triface* testtet, list* illtetlist);
+    bool checktet4sliver(triface* testtet, list* illtetlist);
+    bool checkseg4splitting(face* testseg, REAL* rpsarray, bool bqual);
+    bool checksub4splitting(face* testsub);
+    void doqualchecktetlist();
+    bool tallencsegs(point testpt, list *cavtetlist);
+    bool tallencsubs(point testpt, list *cavtetlist);
+    void tallbadtetrahedrons();
+    void tallilltets(list* illtetlist);
+    void tallslivers(list* illtetlist);
+    void removeilltets();
+    void removeslivers();
+    void repairencsegs(REAL* rpsarray, bool bqual, queue* flipqueue);
+    void repairencsubs(REAL* rpsarray, int* idx2facetlist, list* cavtetlist,
+                       queue* flipqueue);
+    void repairbadtets(REAL* rpsarray, int* idx2facetlist, list* cavtetlist,
+                       queue* flipqueue);
+    void enforcequality();
+
+    // I/O routines
+    void transfernodes();
+    void jettisonnodes();
+    void highorder();
+    void outnodes(tetgenio* out);
+    void outelements(tetgenio* out);
+    void outfaces(tetgenio* out);
+    void outhullfaces(tetgenio* out);
+    void outsubfaces(tetgenio* out);
+    void outsubsegments(tetgenio* out);
+    void outneighbors(tetgenio* out);
+    void outsmesh(char* smfilename);
+    void outmesh2medit(char* mfilename);
+    void outmesh2gid(char* gfilename);
+    void outmesh2off(char* ofilename);
+
+    // User interaction routines.
+    void internalerror();
+    void checkmesh();
+    void checkshells();
+    void checkdelaunay(queue* flipqueue);
+    void checkconforming();
+    void qualitystatistics();
+    void statistics();
+
+  public:
+
+    // Constructor and destructor.
+    tetgenmesh();
+    ~tetgenmesh();
+
+};                                               // End of class tetgenmesh.
+
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// tetrahedralize()    Interface for using TetGen's library to generate      //
+//                     Delaunay tetrahedralizations, constrained Delaunay    //
+//                     tetrahedralizations, quality tetrahedral meshes.      //
+//                                                                           //
+// Two functions (interfaces) are available. The difference is only the way  //
+// of passing switches.  One directly accepts an object of 'tetgenbehavior', //
+// the other accepts a string which is the same as one can used in command   //
+// line.  The latter may be more convenient for users who don't know the     //
+// 'tetgenbehavir' structure.                                                //
+//                                                                           //
+// 'in' is the input object containing a PLC or a list of points. It should  //
+// not be a NULL.  'out' is for outputting the mesh or tetrahedralization    //
+// created by TetGen. If it is NULL, the output will be redirect to file(s). //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out);
+void tetrahedralize(char *switches, tetgenio *in, tetgenio *out);
+
+#endif // #ifndef tetgenH
diff --git a/contrib/Triangle/Makefile b/contrib/Triangle/Makefile
new file mode 100644
index 0000000000..f032dbb4e9
--- /dev/null
+++ b/contrib/Triangle/Makefile
@@ -0,0 +1,55 @@
+# $Id: Makefile,v 1.1 2005-09-21 17:29:40 geuzaine Exp $
+#
+# Copyright (C) 1997-2005 C. Geuzaine, J.-F. Remacle
+#
+# 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.
+# 
+# Please report all bugs and problems to <gmsh@geuz.org>.
+
+include ../../variables
+
+LIB = ../../lib/libGmshTriangle.a
+
+# Don't optimize triangle: it crashes on Linux
+# CFLAGS = ${OPTIM} ${FLAGS} -DTRILIBRARY
+CFLAGS = ${FLAGS} -DTRILIBRARY
+
+SRC = triangle.c
+
+OBJ = ${SRC:.c=.o}
+
+.SUFFIXES: .o .c
+
+${LIB}: ${OBJ} 
+	${AR} ${LIB} ${OBJ} 
+	${RANLIB} ${LIB}
+
+.c.o:
+	${CC} ${CFLAGS} -c $<
+
+clean:
+	rm -f *.o
+
+depend:
+	(sed '/^# DO NOT DELETE THIS LINE/q' Makefile && \
+	${CC} -MM ${CFLAGS} ${SRC} \
+	) >Makefile.new
+	cp Makefile Makefile.bak
+	cp Makefile.new Makefile
+	rm -f Makefile.new
+
+# DO NOT DELETE THIS LINE
+triangle.o: triangle.c triangle.h
diff --git a/contrib/Triangle/README b/contrib/Triangle/README
new file mode 100644
index 0000000000..8570b1e278
--- /dev/null
+++ b/contrib/Triangle/README
@@ -0,0 +1,74 @@
+
+If you want to use Jonathan Shewchuk's Triangle as an alternative
+isotropic 2D mesh generator in Gmsh, please download Triangle from the
+author's web site at http://www.cs.cmu.edu/~quake/triangle.html,
+unpack the archive and copy the two files 'triangle.c' and
+'triangle.h' in this directory. Then run configure and rebuild Gmsh.
+
+Please note that by doing so, you agree to Triangle's licensing
+requirements (stated below). Most notably, you can only redistribute
+Gmsh if no compensation is received.
+
+==============================================================================
+
+Triangle
+A Two-Dimensional Quality Mesh Generator and Delaunay Triangulator.
+Version 1.5
+
+Copyright 1993, 1995, 1997, 1998, 2002, 2004 Jonathan Richard Shewchuk
+2360 Woolsey #H
+Berkeley, California  94705-1927
+Please send bugs and comments to jrs@cs.berkeley.edu
+
+Created as part of the Archimedes project (tools for parallel FEM).
+Supported in part by NSF Grant CMS-9318163 and an NSERC 1967 Scholarship.
+There is no warranty whatsoever.  Use at your own risk.
+
+
+Triangle generates exact Delaunay triangulations, constrained Delaunay
+triangulations, Voronoi diagrams, and quality conforming Delaunay
+triangulations.  The latter can be generated with no small angles, and are
+thus suitable for finite element analysis.  Show Me graphically displays
+the contents of the geometric files used by Triangle.  Show Me can also
+write images in PostScript form.
+
+Information on the algorithms used by Triangle, including complete
+references, can be found in the comments at the beginning of the triangle.c
+source file.  Another listing of these references, with PostScript copies
+of some of the papers, is available from the Web page
+
+    http://www.cs.cmu.edu/~quake/triangle.research.html
+
+------------------------------------------------------------------------------
+
+These programs may be freely redistributed under the condition that the
+copyright notices (including the copy of this notice in the code comments
+and the copyright notice printed when the `-h' switch is selected) are
+not removed, and no compensation is received.  Private, research, and
+institutional use is free.  You may distribute modified versions of this
+code UNDER THE CONDITION THAT THIS CODE AND ANY MODIFICATIONS MADE TO IT
+IN THE SAME FILE REMAIN UNDER COPYRIGHT OF THE ORIGINAL AUTHOR, BOTH
+SOURCE AND OBJECT CODE ARE MADE FREELY AVAILABLE WITHOUT CHARGE, AND
+CLEAR NOTICE IS GIVEN OF THE MODIFICATIONS.  Distribution of this code as
+part of a commercial system is permissible ONLY BY DIRECT ARRANGEMENT
+WITH THE AUTHOR.  (If you are not directly supplying this code to a
+customer, and you are instead telling them how they can obtain it for
+free, then you are not required to make any arrangement with me.)
+
+------------------------------------------------------------------------------
+
+If you use Triangle, and especially if you use it to accomplish real
+work, I would like very much to hear from you.  A short letter or email
+(to jrs@cs.cmu.edu) describing how you use Triangle will mean a lot to
+me.  The more people I know are using this program, the more easily I can
+justify spending time on improvements and on the three-dimensional
+successor to Triangle, which in turn will benefit you.  Also, I can put
+you on a list to receive email whenever a new version of Triangle is
+available.
+
+If you use a mesh generated by Triangle or plotted by Show Me in a
+publication, please include an acknowledgment as well.
+
+
+Jonathan Richard Shewchuk
+April 27, 2004
diff --git a/contrib/Triangle/triangle.c b/contrib/Triangle/triangle.c
new file mode 100644
index 0000000000..203100baca
--- /dev/null
+++ b/contrib/Triangle/triangle.c
@@ -0,0 +1,16008 @@
+/*****************************************************************************/
+/*                                                                           */
+/*      888888888        ,o,                          / 888                  */
+/*         888    88o88o  "    o8888o  88o8888o o88888o 888  o88888o         */
+/*         888    888    888       88b 888  888 888 888 888 d888  88b        */
+/*         888    888    888  o88^o888 888  888 "88888" 888 8888oo888        */
+/*         888    888    888 C888  888 888  888  /      888 q888             */
+/*         888    888    888  "88o^888 888  888 Cb      888  "88oooo"        */
+/*                                              "8oo8D                       */
+/*                                                                           */
+/*  A Two-Dimensional Quality Mesh Generator and Delaunay Triangulator.      */
+/*  (triangle.c)                                                             */
+/*                                                                           */
+/*  Version 1.5                                                              */
+/*  April 27, 2004                                                           */
+/*                                                                           */
+/*  Copyright 1993, 1995, 1997, 1998, 2002, 2004                             */
+/*  Jonathan Richard Shewchuk                                                */
+/*  2360 Woolsey #H                                                          */
+/*  Berkeley, California  94705-1927                                         */
+/*  jrs@cs.berkeley.edu                                                      */
+/*                                                                           */
+/*  This program may be freely redistributed under the condition that the    */
+/*    copyright notices (including this entire header and the copyright      */
+/*    notice printed when the `-h' switch is selected) are not removed, and  */
+/*    no compensation is received.  Private, research, and institutional     */
+/*    use is free.  You may distribute modified versions of this code UNDER  */
+/*    THE CONDITION THAT THIS CODE AND ANY MODIFICATIONS MADE TO IT IN THE   */
+/*    SAME FILE REMAIN UNDER COPYRIGHT OF THE ORIGINAL AUTHOR, BOTH SOURCE   */
+/*    AND OBJECT CODE ARE MADE FREELY AVAILABLE WITHOUT CHARGE, AND CLEAR    */
+/*    NOTICE IS GIVEN OF THE MODIFICATIONS.  Distribution of this code as    */
+/*    part of a commercial system is permissible ONLY BY DIRECT ARRANGEMENT  */
+/*    WITH THE AUTHOR.  (If you are not directly supplying this code to a    */
+/*    customer, and you are instead telling them how they can obtain it for  */
+/*    free, then you are not required to make any arrangement with me.)      */
+/*                                                                           */
+/*  Hypertext instructions for Triangle are available on the Web at          */
+/*                                                                           */
+/*      http://www.cs.cmu.edu/~quake/triangle.html                           */
+/*                                                                           */
+/*  Some of the references listed below are marked with an asterisk.  [*]    */
+/*    These references are available for downloading from the Web page       */
+/*                                                                           */
+/*      http://www.cs.cmu.edu/~quake/triangle.research.html                  */
+/*                                                                           */
+/*  Three papers discussing aspects of Triangle are available.  A short      */
+/*    overview appears in "Triangle:  Engineering a 2D Quality Mesh          */
+/*    Generator and Delaunay Triangulator," in Applied Computational         */
+/*    Geometry:  Towards Geometric Engineering, Ming C. Lin and Dinesh       */
+/*    Manocha, editors, Lecture Notes in Computer Science volume 1148,       */
+/*    pages 203-222, Springer-Verlag, Berlin, May 1996 (from the First ACM   */
+/*    Workshop on Applied Computational Geometry).  [*]                      */
+/*                                                                           */
+/*    The algorithms are discussed in the greatest detail in "Delaunay       */
+/*    Refinement Algorithms for Triangular Mesh Generation," Computational   */
+/*    Geometry:  Theory and Applications 22(1-3):21-74, May 2002.  [*]       */
+/*                                                                           */
+/*    More detail about the data structures may be found in my dissertation: */
+/*    "Delaunay Refinement Mesh Generation," Ph.D. thesis, Technical Report  */
+/*    CMU-CS-97-137, School of Computer Science, Carnegie Mellon University, */
+/*    Pittsburgh, Pennsylvania, 18 May 1997.  [*]                            */
+/*                                                                           */
+/*  Triangle was created as part of the Archimedes project in the School of  */
+/*    Computer Science at Carnegie Mellon University.  Archimedes is a       */
+/*    system for compiling parallel finite element solvers.  For further     */
+/*    information, see Hesheng Bao, Jacobo Bielak, Omar Ghattas, Loukas F.   */
+/*    Kallivokas, David R. O'Hallaron, Jonathan R. Shewchuk, and Jifeng Xu,  */
+/*    "Large-scale Simulation of Elastic Wave Propagation in Heterogeneous   */
+/*    Media on Parallel Computers," Computer Methods in Applied Mechanics    */
+/*    and Engineering 152(1-2):85-102, 22 January 1998.                      */
+/*                                                                           */
+/*  Triangle's Delaunay refinement algorithm for quality mesh generation is  */
+/*    a hybrid of one due to Jim Ruppert, "A Delaunay Refinement Algorithm   */
+/*    for Quality 2-Dimensional Mesh Generation," Journal of Algorithms      */
+/*    18(3):548-585, May 1995 [*], and one due to L. Paul Chew, "Guaranteed- */
+/*    Quality Mesh Generation for Curved Surfaces," Proceedings of the Ninth */
+/*    Annual Symposium on Computational Geometry (San Diego, California),    */
+/*    pages 274-280, Association for Computing Machinery, May 1993.          */
+/*                                                                           */
+/*  The Delaunay refinement algorithm has been modified so that it           */
+/*    consistently meshes domains with small input angles, as described in   */
+/*    my lengthy journal article listed above, or in abbreviated form in     */
+/*    Jonathan Richard Shewchuk, "Mesh Generation for Domains with Small     */
+/*    Angles," Proceedings of the Sixteenth Annual Symposium on              */
+/*    Computational Geometry (Hong Kong), pages 1-10, Association for        */
+/*    Computing Machinery, June 2000.  [*]                                   */
+/*                                                                           */
+/*  My implementation of the divide-and-conquer and incremental Delaunay     */
+/*    triangulation algorithms follows closely the presentation of Guibas    */
+/*    and Stolfi, even though I use a triangle-based data structure instead  */
+/*    of their quad-edge data structure.  (In fact, I originally implemented */
+/*    Triangle using the quad-edge data structure, but the switch to a       */
+/*    triangle-based data structure sped Triangle by a factor of two.)  The  */
+/*    mesh manipulation primitives and the two aforementioned Delaunay       */
+/*    triangulation algorithms are described by Leonidas J. Guibas and Jorge */
+/*    Stolfi, "Primitives for the Manipulation of General Subdivisions and   */
+/*    the Computation of Voronoi Diagrams," ACM Transactions on Graphics     */
+/*    4(2):74-123, April 1985.                                               */
+/*                                                                           */
+/*  Their O(n log n) divide-and-conquer algorithm is adapted from Der-Tsai   */
+/*    Lee and Bruce J. Schachter, "Two Algorithms for Constructing the       */
+/*    Delaunay Triangulation," International Journal of Computer and         */
+/*    Information Science 9(3):219-242, 1980.  Triangle's improvement of the */
+/*    divide-and-conquer algorithm by alternating between vertical and       */
+/*    horizontal cuts was introduced by Rex A. Dwyer, "A Faster Divide-and-  */
+/*    Conquer Algorithm for Constructing Delaunay Triangulations,"           */
+/*    Algorithmica 2(2):137-151, 1987.                                       */
+/*                                                                           */
+/*  The incremental insertion algorithm was first proposed by C. L. Lawson,  */
+/*    "Software for C1 Surface Interpolation," in Mathematical Software III, */
+/*    John R. Rice, editor, Academic Press, New York, pp. 161-194, 1977.     */
+/*    For point location, I use the algorithm of Ernst P. Mucke, Isaac       */
+/*    Saias, and Binhai Zhu, "Fast Randomized Point Location Without         */
+/*    Preprocessing in Two- and Three-Dimensional Delaunay Triangulations,"  */
+/*    Proceedings of the Twelfth Annual Symposium on Computational Geometry, */
+/*    ACM, May 1996.  [*]  If I were to randomize the order of vertex        */
+/*    insertion (I currently don't bother), their result combined with the   */
+/*    result of Kenneth L. Clarkson and Peter W. Shor, "Applications of      */
+/*    Random Sampling in Computational Geometry II," Discrete &              */
+/*    Computational Geometry 4(1):387-421, 1989, would yield an expected     */
+/*    O(n^{4/3}) bound on running time.                                      */
+/*                                                                           */
+/*  The O(n log n) sweepline Delaunay triangulation algorithm is taken from  */
+/*    Steven Fortune, "A Sweepline Algorithm for Voronoi Diagrams",          */
+/*    Algorithmica 2(2):153-174, 1987.  A random sample of edges on the      */
+/*    boundary of the triangulation are maintained in a splay tree for the   */
+/*    purpose of point location.  Splay trees are described by Daniel        */
+/*    Dominic Sleator and Robert Endre Tarjan, "Self-Adjusting Binary Search */
+/*    Trees," Journal of the ACM 32(3):652-686, July 1985.                   */
+/*                                                                           */
+/*  The algorithms for exact computation of the signs of determinants are    */
+/*    described in Jonathan Richard Shewchuk, "Adaptive Precision Floating-  */
+/*    Point Arithmetic and Fast Robust Geometric Predicates," Discrete &     */
+/*    Computational Geometry 18(3):305-363, October 1997.  (Also available   */
+/*    as Technical Report CMU-CS-96-140, School of Computer Science,         */
+/*    Carnegie Mellon University, Pittsburgh, Pennsylvania, May 1996.)  [*]  */
+/*    An abbreviated version appears as Jonathan Richard Shewchuk, "Robust   */
+/*    Adaptive Floating-Point Geometric Predicates," Proceedings of the      */
+/*    Twelfth Annual Symposium on Computational Geometry, ACM, May 1996. [*] */
+/*    Many of the ideas for my exact arithmetic routines originate with      */
+/*    Douglas M. Priest, "Algorithms for Arbitrary Precision Floating Point  */
+/*    Arithmetic," Tenth Symposium on Computer Arithmetic, pp. 132-143, IEEE */
+/*    Computer Society Press, 1991.  [*]  Many of the ideas for the correct  */
+/*    evaluation of the signs of determinants are taken from Steven Fortune  */
+/*    and Christopher J. Van Wyk, "Efficient Exact Arithmetic for Computa-   */
+/*    tional Geometry," Proceedings of the Ninth Annual Symposium on         */
+/*    Computational Geometry, ACM, pp. 163-172, May 1993, and from Steven    */
+/*    Fortune, "Numerical Stability of Algorithms for 2D Delaunay Triangu-   */
+/*    lations," International Journal of Computational Geometry & Applica-   */
+/*    tions 5(1-2):193-213, March-June 1995.                                 */
+/*                                                                           */
+/*  The method of inserting new vertices off-center (not precisely at the    */
+/*    circumcenter of every poor-quality triangle) is from Alper Ungor,      */
+/*    "Off-centers:  A New Type of Steiner Points for Computing Size-Optimal */
+/*    quality-guaranteed Delaunay triangulations," Proceedings of LATIN      */
+/*    2004 (Buenos Aires, Argentina), April 2004.                            */
+/*                                                                           */
+/*  For definitions of and results involving Delaunay triangulations,        */
+/*    constrained and conforming versions thereof, and other aspects of      */
+/*    triangular mesh generation, see the excellent survey by Marshall Bern  */
+/*    and David Eppstein, "Mesh Generation and Optimal Triangulation," in    */
+/*    Computing and Euclidean Geometry, Ding-Zhu Du and Frank Hwang,         */
+/*    editors, World Scientific, Singapore, pp. 23-90, 1992.  [*]            */
+/*                                                                           */
+/*  The time for incrementally adding PSLG (planar straight line graph)      */
+/*    segments to create a constrained Delaunay triangulation is probably    */
+/*    O(t^2) per segment in the worst case and O(t) per segment in the       */
+/*    common case, where t is the number of triangles that intersect the     */
+/*    segment before it is inserted.  This doesn't count point location,     */
+/*    which can be much more expensive.  I could improve this to O(d log d)  */
+/*    time, but d is usually quite small, so it's not worth the bother.      */
+/*    (This note does not apply to conforming Delaunay triangulations, for   */
+/*    which a different method is used to insert segments.)                  */
+/*                                                                           */
+/*  The time for adding segments to a conforming Delaunay triangulation is   */
+/*    not clear, but does not depend upon t alone.  In some cases, very      */
+/*    small features (like a vertex lying next to a segment) can cause a     */
+/*    single segment to be split an arbitrary number of times.  Of course,   */
+/*    floating-point precision is a practical barrier to how much this can   */
+/*    happen.                                                                */
+/*                                                                           */
+/*  The time for deleting a vertex from a Delaunay triangulation is O(d^2)   */
+/*    in the worst case and O(d) in the common case, where d is the degree   */
+/*    of the vertex being deleted.  I could improve this to O(d log d) time, */
+/*    but d is usually quite small, so it's not worth the bother.            */
+/*                                                                           */
+/*  Ruppert's Delaunay refinement algorithm typically generates triangles    */
+/*    at a linear rate (constant time per triangle) after the initial        */
+/*    triangulation is formed.  There may be pathological cases where        */
+/*    quadratic time is required, but these never arise in practice.         */
+/*                                                                           */
+/*  The geometric predicates (circumcenter calculations, segment             */
+/*    intersection formulae, etc.) appear in my "Lecture Notes on Geometric  */
+/*    Robustness" at http://www.cs.berkeley.edu/~jrs/mesh.html .             */
+/*                                                                           */
+/*  If you make any improvements to this code, please please please let me   */
+/*    know, so that I may obtain the improvements.  Even if you don't change */
+/*    the code, I'd still love to hear what it's being used for.             */
+/*                                                                           */
+/*  Disclaimer:  Neither I nor Carnegie Mellon warrant this code in any way  */
+/*    whatsoever.  This code is provided "as-is".  Use at your own risk.     */
+/*                                                                           */
+/*****************************************************************************/
+
+/* For single precision (which will save some memory and reduce paging),     */
+/*   define the symbol SINGLE by using the -DSINGLE compiler switch or by    */
+/*   writing "#define SINGLE" below.                                         */
+/*                                                                           */
+/* For double precision (which will allow you to refine meshes to a smaller  */
+/*   edge length), leave SINGLE undefined.                                   */
+/*                                                                           */
+/* Double precision uses more memory, but improves the resolution of the     */
+/*   meshes you can generate with Triangle.  It also reduces the likelihood  */
+/*   of a floating exception due to overflow.  Finally, it is much faster    */
+/*   than single precision on 64-bit architectures like the DEC Alpha.  I    */
+/*   recommend double precision unless you want to generate a mesh for which */
+/*   you do not have enough memory.                                          */
+
+/* #define SINGLE */
+
+#ifdef SINGLE
+#define REAL float
+#else /* not SINGLE */
+#define REAL double
+#endif /* not SINGLE */
+
+/* If yours is not a Unix system, define the NO_TIMER compiler switch to     */
+/*   remove the Unix-specific timing code.                                   */
+
+/* #define NO_TIMER */
+
+/* To insert lots of self-checks for internal errors, define the SELF_CHECK  */
+/*   symbol.  This will slow down the program significantly.  It is best to  */
+/*   define the symbol using the -DSELF_CHECK compiler switch, but you could */
+/*   write "#define SELF_CHECK" below.  If you are modifying this code, I    */
+/*   recommend you turn self-checks on until your work is debugged.          */
+
+/* #define SELF_CHECK */
+
+/* To compile Triangle as a callable object library (triangle.o), define the */
+/*   TRILIBRARY symbol.  Read the file triangle.h for details on how to call */
+/*   the procedure triangulate() that results.                               */
+
+/* #define TRILIBRARY */
+
+/* It is possible to generate a smaller version of Triangle using one or     */
+/*   both of the following symbols.  Define the REDUCED symbol to eliminate  */
+/*   all features that are primarily of research interest; specifically, the */
+/*   -i, -F, -s, and -C switches.  Define the CDT_ONLY symbol to eliminate   */
+/*   all meshing algorithms above and beyond constrained Delaunay            */
+/*   triangulation; specifically, the -r, -q, -a, -S, and -s switches.       */
+/*   These reductions are most likely to be useful when generating an object */
+/*   library (triangle.o) by defining the TRILIBRARY symbol.                 */
+
+/* #define REDUCED */
+/* #define CDT_ONLY */
+
+/* On some machines, my exact arithmetic routines might be defeated by the   */
+/*   use of internal extended precision floating-point registers.  The best  */
+/*   way to solve this problem is to set the floating-point registers to use */
+/*   single or double precision internally.  On 80x86 processors, this may   */
+/*   be accomplished by setting the CPU86 symbol for the Microsoft C         */
+/*   compiler, or the LINUX symbol for the gcc compiler running on Linux.    */
+/*                                                                           */
+/* An inferior solution is to declare certain values as `volatile', thus     */
+/*   forcing them to be stored to memory and rounded off.  Unfortunately,    */
+/*   this solution might slow Triangle down quite a bit.  To use volatile    */
+/*   values, write "#define INEXACT volatile" below.  Normally, however,     */
+/*   INEXACT should be defined to be nothing.  ("#define INEXACT".)          */
+/*                                                                           */
+/* For more discussion, see http://www.cs.cmu.edu/~quake/robust.pc.html .    */
+/*   For yet more discussion, see Section 5 of my paper, "Adaptive Precision */
+/*   Floating-Point Arithmetic and Fast Robust Geometric Predicates" (also   */
+/*   available as Section 6.6 of my dissertation).                           */
+
+/* #define CPU86 */
+/* #define LINUX */
+
+#define INEXACT /* Nothing */
+/* #define INEXACT volatile */
+
+/* Maximum number of characters in a file name (including the null).         */
+
+#define FILENAMESIZE 512
+
+/* Maximum number of characters in a line read from a file (including the    */
+/*   null).                                                                  */
+
+#define INPUTLINESIZE 512
+
+/* For efficiency, a variety of data structures are allocated in bulk.  The  */
+/*   following constants determine how many of each structure is allocated   */
+/*   at once.                                                                */
+
+#define TRIPERBLOCK 4092           /* Number of triangles allocated at once. */
+#define SUBSEGPERBLOCK 508       /* Number of subsegments allocated at once. */
+#define VERTEXPERBLOCK 4092         /* Number of vertices allocated at once. */
+#define VIRUSPERBLOCK 1020   /* Number of virus triangles allocated at once. */
+/* Number of encroached subsegments allocated at once. */
+#define BADSUBSEGPERBLOCK 252
+/* Number of skinny triangles allocated at once. */
+#define BADTRIPERBLOCK 4092
+/* Number of flipped triangles allocated at once. */
+#define FLIPSTACKERPERBLOCK 252
+/* Number of splay tree nodes allocated at once. */
+#define SPLAYNODEPERBLOCK 508
+
+/* The vertex types.   A DEADVERTEX has been deleted entirely.  An           */
+/*   UNDEADVERTEX is not part of the mesh, but is written to the output      */
+/*   .node file and affects the node indexing in the other output files.     */
+
+#define INPUTVERTEX 0
+#define SEGMENTVERTEX 1
+#define FREEVERTEX 2
+#define DEADVERTEX -32768
+#define UNDEADVERTEX -32767
+
+/* The next line is used to outsmart some very stupid compilers.  If your    */
+/*   compiler is smarter, feel free to replace the "int" with "void".        */
+/*   Not that it matters.                                                    */
+
+#define VOID int
+
+/* Two constants for algorithms based on random sampling.  Both constants    */
+/*   have been chosen empirically to optimize their respective algorithms.   */
+
+/* Used for the point location scheme of Mucke, Saias, and Zhu, to decide    */
+/*   how large a random sample of triangles to inspect.                      */
+
+#define SAMPLEFACTOR 11
+
+/* Used in Fortune's sweepline Delaunay algorithm to determine what fraction */
+/*   of boundary edges should be maintained in the splay tree for point      */
+/*   location on the front.                                                  */
+
+#define SAMPLERATE 10
+
+/* A number that speaks for itself, every kissable digit.                    */
+
+#define PI 3.141592653589793238462643383279502884197169399375105820974944592308
+
+/* Another fave.                                                             */
+
+#define SQUAREROOTTWO 1.4142135623730950488016887242096980785696718753769480732
+
+/* And here's one for those of you who are intimidated by math.              */
+
+#define ONETHIRD 0.333333333333333333333333333333333333333333333333333333333333
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#ifndef NO_TIMER
+#include <sys/time.h>
+#endif /* not NO_TIMER */
+#ifdef CPU86
+#include <float.h>
+#endif /* CPU86 */
+#ifdef LINUX
+#include <fpu_control.h>
+#endif /* LINUX */
+#ifdef TRILIBRARY
+#include "triangle.h"
+#endif /* TRILIBRARY */
+
+/* A few forward declarations.                                               */
+
+#ifndef TRILIBRARY
+char *readline();
+char *findfield();
+#endif /* not TRILIBRARY */
+
+/* Labels that signify whether a record consists primarily of pointers or of */
+/*   floating-point words.  Used to make decisions about data alignment.     */
+
+enum wordtype {POINTER, FLOATINGPOINT};
+
+/* Labels that signify the result of point location.  The result of a        */
+/*   search indicates that the point falls in the interior of a triangle, on */
+/*   an edge, on a vertex, or outside the mesh.                              */
+
+enum locateresult {INTRIANGLE, ONEDGE, ONVERTEX, OUTSIDE};
+
+/* Labels that signify the result of vertex insertion.  The result indicates */
+/*   that the vertex was inserted with complete success, was inserted but    */
+/*   encroaches upon a subsegment, was not inserted because it lies on a     */
+/*   segment, or was not inserted because another vertex occupies the same   */
+/*   location.                                                               */
+
+enum insertvertexresult {SUCCESSFULVERTEX, ENCROACHINGVERTEX, VIOLATINGVERTEX,
+                         DUPLICATEVERTEX};
+
+/* Labels that signify the result of direction finding.  The result          */
+/*   indicates that a segment connecting the two query points falls within   */
+/*   the direction triangle, along the left edge of the direction triangle,  */
+/*   or along the right edge of the direction triangle.                      */
+
+enum finddirectionresult {WITHIN, LEFTCOLLINEAR, RIGHTCOLLINEAR};
+
+/*****************************************************************************/
+/*                                                                           */
+/*  The basic mesh data structures                                           */
+/*                                                                           */
+/*  There are three:  vertices, triangles, and subsegments (abbreviated      */
+/*  `subseg').  These three data structures, linked by pointers, comprise    */
+/*  the mesh.  A vertex simply represents a mesh vertex and its properties.  */
+/*  A triangle is a triangle.  A subsegment is a special data structure used */
+/*  to represent an impenetrable edge of the mesh (perhaps on the outer      */
+/*  boundary, on the boundary of a hole, or part of an internal boundary     */
+/*  separating two triangulated regions).  Subsegments represent boundaries, */
+/*  defined by the user, that triangles may not lie across.                  */
+/*                                                                           */
+/*  A triangle consists of a list of three vertices, a list of three         */
+/*  adjoining triangles, a list of three adjoining subsegments (when         */
+/*  segments exist), an arbitrary number of optional user-defined            */
+/*  floating-point attributes, and an optional area constraint.  The latter  */
+/*  is an upper bound on the permissible area of each triangle in a region,  */
+/*  used for mesh refinement.                                                */
+/*                                                                           */
+/*  For a triangle on a boundary of the mesh, some or all of the neighboring */
+/*  triangles may not be present.  For a triangle in the interior of the     */
+/*  mesh, often no neighboring subsegments are present.  Such absent         */
+/*  triangles and subsegments are never represented by NULL pointers; they   */
+/*  are represented by two special records:  `dummytri', the triangle that   */
+/*  fills "outer space", and `dummysub', the omnipresent subsegment.         */
+/*  `dummytri' and `dummysub' are used for several reasons; for instance,    */
+/*  they can be dereferenced and their contents examined without violating   */
+/*  protected memory.                                                        */
+/*                                                                           */
+/*  However, it is important to understand that a triangle includes other    */
+/*  information as well.  The pointers to adjoining vertices, triangles, and */
+/*  subsegments are ordered in a way that indicates their geometric relation */
+/*  to each other.  Furthermore, each of these pointers contains orientation */
+/*  information.  Each pointer to an adjoining triangle indicates which face */
+/*  of that triangle is contacted.  Similarly, each pointer to an adjoining  */
+/*  subsegment indicates which side of that subsegment is contacted, and how */
+/*  the subsegment is oriented relative to the triangle.                     */
+/*                                                                           */
+/*  The data structure representing a subsegment may be thought to be        */
+/*  abutting the edge of one or two triangle data structures:  either        */
+/*  sandwiched between two triangles, or resting against one triangle on an  */
+/*  exterior boundary or hole boundary.                                      */
+/*                                                                           */
+/*  A subsegment consists of a list of two vertices, a list of two           */
+/*  adjoining subsegments, and a list of two adjoining triangles.  One of    */
+/*  the two adjoining triangles may not be present (though there should      */
+/*  always be one), and neighboring subsegments might not be present.        */
+/*  Subsegments also store a user-defined integer "boundary marker".         */
+/*  Typically, this integer is used to indicate what boundary conditions are */
+/*  to be applied at that location in a finite element simulation.           */
+/*                                                                           */
+/*  Like triangles, subsegments maintain information about the relative      */
+/*  orientation of neighboring objects.                                      */
+/*                                                                           */
+/*  Vertices are relatively simple.  A vertex is a list of floating-point    */
+/*  numbers, starting with the x, and y coordinates, followed by an          */
+/*  arbitrary number of optional user-defined floating-point attributes,     */
+/*  followed by an integer boundary marker.  During the segment insertion    */
+/*  phase, there is also a pointer from each vertex to a triangle that may   */
+/*  contain it.  Each pointer is not always correct, but when one is, it     */
+/*  speeds up segment insertion.  These pointers are assigned values once    */
+/*  at the beginning of the segment insertion phase, and are not used or     */
+/*  updated except during this phase.  Edge flipping during segment          */
+/*  insertion will render some of them incorrect.  Hence, don't rely upon    */
+/*  them for anything.                                                       */
+/*                                                                           */
+/*  Other than the exception mentioned above, vertices have no information   */
+/*  about what triangles, subfacets, or subsegments they are linked to.      */
+/*                                                                           */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  Handles                                                                  */
+/*                                                                           */
+/*  The oriented triangle (`otri') and oriented subsegment (`osub') data     */
+/*  structures defined below do not themselves store any part of the mesh.   */
+/*  The mesh itself is made of `triangle's, `subseg's, and `vertex's.        */
+/*                                                                           */
+/*  Oriented triangles and oriented subsegments will usually be referred to  */
+/*  as "handles."  A handle is essentially a pointer into the mesh; it       */
+/*  allows you to "hold" one particular part of the mesh.  Handles are used  */
+/*  to specify the regions in which one is traversing and modifying the mesh.*/
+/*  A single `triangle' may be held by many handles, or none at all.  (The   */
+/*  latter case is not a memory leak, because the triangle is still          */
+/*  connected to other triangles in the mesh.)                               */
+/*                                                                           */
+/*  An `otri' is a handle that holds a triangle.  It holds a specific edge   */
+/*  of the triangle.  An `osub' is a handle that holds a subsegment.  It     */
+/*  holds either the left or right side of the subsegment.                   */
+/*                                                                           */
+/*  Navigation about the mesh is accomplished through a set of mesh          */
+/*  manipulation primitives, further below.  Many of these primitives take   */
+/*  a handle and produce a new handle that holds the mesh near the first     */
+/*  handle.  Other primitives take two handles and glue the corresponding    */
+/*  parts of the mesh together.  The orientation of the handles is           */
+/*  important.  For instance, when two triangles are glued together by the   */
+/*  bond() primitive, they are glued at the edges on which the handles lie.  */
+/*                                                                           */
+/*  Because vertices have no information about which triangles they are      */
+/*  attached to, I commonly represent a vertex by use of a handle whose      */
+/*  origin is the vertex.  A single handle can simultaneously represent a    */
+/*  triangle, an edge, and a vertex.                                         */
+/*                                                                           */
+/*****************************************************************************/
+
+/* The triangle data structure.  Each triangle contains three pointers to    */
+/*   adjoining triangles, plus three pointers to vertices, plus three        */
+/*   pointers to subsegments (declared below; these pointers are usually     */
+/*   `dummysub').  It may or may not also contain user-defined attributes    */
+/*   and/or a floating-point "area constraint."  It may also contain extra   */
+/*   pointers for nodes, when the user asks for high-order elements.         */
+/*   Because the size and structure of a `triangle' is not decided until     */
+/*   runtime, I haven't simply declared the type `triangle' as a struct.     */
+
+typedef REAL **triangle;            /* Really:  typedef triangle *triangle   */
+
+/* An oriented triangle:  includes a pointer to a triangle and orientation.  */
+/*   The orientation denotes an edge of the triangle.  Hence, there are      */
+/*   three possible orientations.  By convention, each edge always points    */
+/*   counterclockwise about the corresponding triangle.                      */
+
+struct otri {
+  triangle *tri;
+  int orient;                                         /* Ranges from 0 to 2. */
+};
+
+/* The subsegment data structure.  Each subsegment contains two pointers to  */
+/*   adjoining subsegments, plus two pointers to vertices, plus two pointers */
+/*   to adjoining triangles, plus one boundary marker.                       */
+
+typedef REAL **subseg;                  /* Really:  typedef subseg *subseg   */
+
+/* An oriented subsegment:  includes a pointer to a subsegment and an        */
+/*   orientation.  The orientation denotes a side of the edge.  Hence, there */
+/*   are two possible orientations.  By convention, the edge is always       */
+/*   directed so that the "side" denoted is the right side of the edge.      */
+
+struct osub {
+  subseg *ss;
+  int ssorient;                                       /* Ranges from 0 to 1. */
+};
+
+/* The vertex data structure.  Each vertex is actually an array of REALs.    */
+/*   The number of REALs is unknown until runtime.  An integer boundary      */
+/*   marker, and sometimes a pointer to a triangle, is appended after the    */
+/*   REALs.                                                                  */
+
+typedef REAL *vertex;
+
+/* A queue used to store encroached subsegments.  Each subsegment's vertices */
+/*   are stored so that we can check whether a subsegment is still the same. */
+
+struct badsubseg {
+  subseg encsubseg;                             /* An encroached subsegment. */
+  vertex subsegorg, subsegdest;                         /* Its two vertices. */
+};
+
+/* A queue used to store bad triangles.  The key is the square of the cosine */
+/*   of the smallest angle of the triangle.  Each triangle's vertices are    */
+/*   stored so that one can check whether a triangle is still the same.      */
+
+struct badtriang {
+  triangle poortri;                       /* A skinny or too-large triangle. */
+  REAL key;                             /* cos^2 of smallest (apical) angle. */
+  vertex triangorg, triangdest, triangapex;           /* Its three vertices. */
+  struct badtriang *nexttriang;             /* Pointer to next bad triangle. */
+};
+
+/* A stack of triangles flipped during the most recent vertex insertion.     */
+/*   The stack is used to undo the vertex insertion if the vertex encroaches */
+/*   upon a subsegment.                                                      */
+
+struct flipstacker {
+  triangle flippedtri;                       /* A recently flipped triangle. */
+  struct flipstacker *prevflip;               /* Previous flip in the stack. */
+};
+
+/* A node in a heap used to store events for the sweepline Delaunay          */
+/*   algorithm.  Nodes do not point directly to their parents or children in */
+/*   the heap.  Instead, each node knows its position in the heap, and can   */
+/*   look up its parent and children in a separate array.  The `eventptr'    */
+/*   points either to a `vertex' or to a triangle (in encoded format, so     */
+/*   that an orientation is included).  In the latter case, the origin of    */
+/*   the oriented triangle is the apex of a "circle event" of the sweepline  */
+/*   algorithm.  To distinguish site events from circle events, all circle   */
+/*   events are given an invalid (smaller than `xmin') x-coordinate `xkey'.  */
+
+struct event {
+  REAL xkey, ykey;                              /* Coordinates of the event. */
+  VOID *eventptr;      /* Can be a vertex or the location of a circle event. */
+  int heapposition;              /* Marks this event's position in the heap. */
+};
+
+/* A node in the splay tree.  Each node holds an oriented ghost triangle     */
+/*   that represents a boundary edge of the growing triangulation.  When a   */
+/*   circle event covers two boundary edges with a triangle, so that they    */
+/*   are no longer boundary edges, those edges are not immediately deleted   */
+/*   from the tree; rather, they are lazily deleted when they are next       */
+/*   encountered.  (Since only a random sample of boundary edges are kept    */
+/*   in the tree, lazy deletion is faster.)  `keydest' is used to verify     */
+/*   that a triangle is still the same as when it entered the splay tree; if */
+/*   it has been rotated (due to a circle event), it no longer represents a  */
+/*   boundary edge and should be deleted.                                    */
+
+struct splaynode {
+  struct otri keyedge;                     /* Lprev of an edge on the front. */
+  vertex keydest;           /* Used to verify that splay node is still live. */
+  struct splaynode *lchild, *rchild;              /* Children in splay tree. */
+};
+
+/* A type used to allocate memory.  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.                                              */
+/*                                                                           */
+/* itemwordtype is set to POINTER or FLOATINGPOINT, and is used to suggest   */
+/*   what sort of word the record is primarily made up of.  alignbytes       */
+/*   determines how new records should be aligned in memory.  itembytes and  */
+/*   itemwords are the length of a record in bytes (after rounding up) and   */
+/*   words.  itemsperblock is the number of items allocated at once in a     */
+/*   single block.  itemsfirstblock is the number of items in the first      */
+/*   block, which can vary from the others.  items is the number of          */
+/*   currently allocated items.  maxitems is the maximum number of items     */
+/*   that have been allocated at once; it is the current number of items     */
+/*   plus the number of records kept on deaditemstack.                       */
+
+struct memorypool {
+  VOID **firstblock, **nowblock;
+  VOID *nextitem;
+  VOID *deaditemstack;
+  VOID **pathblock;
+  VOID *pathitem;
+  enum wordtype itemwordtype;
+  int alignbytes;
+  int itembytes, itemwords;
+  int itemsperblock;
+  int itemsfirstblock;
+  long items, maxitems;
+  int unallocateditems;
+  int pathitemsleft;
+};
+
+
+/* Global constants.                                                         */
+
+REAL splitter;       /* Used to split REAL factors for exact multiplication. */
+REAL epsilon;                             /* Floating-point machine epsilon. */
+REAL resulterrbound;
+REAL ccwerrboundA, ccwerrboundB, ccwerrboundC;
+REAL iccerrboundA, iccerrboundB, iccerrboundC;
+REAL o3derrboundA, o3derrboundB, o3derrboundC;
+
+/* Random number seed is not constant, but I've made it global anyway.       */
+
+unsigned long randomseed;                     /* Current random number seed. */
+
+
+/* Mesh data structure.  Triangle operates on only one mesh, but the mesh    */
+/*   structure is used (instead of global variables) to allow reentrancy.    */
+
+struct mesh {
+
+/* Variables used to allocate memory for triangles, subsegments, vertices,   */
+/*   viri (triangles being eaten), encroached segments, bad (skinny or too   */
+/*   large) triangles, and splay tree nodes.                                 */
+
+  struct memorypool triangles;
+  struct memorypool subsegs;
+  struct memorypool vertices;
+  struct memorypool viri;
+  struct memorypool badsubsegs;
+  struct memorypool badtriangles;
+  struct memorypool flipstackers;
+  struct memorypool splaynodes;
+
+/* Variables that maintain the bad triangle queues.  The queues are          */
+/*   ordered from 63 (highest priority) to 0 (lowest priority).              */
+
+  struct badtriang *queuefront[64];
+  struct badtriang *queuetail[64];
+  int nextnonemptyq[64];
+  int firstnonemptyq;
+
+/* Variable that maintains the stack of recently flipped triangles.          */
+
+  struct flipstacker *lastflip;
+
+/* Other variables. */
+
+  REAL xmin, xmax, ymin, ymax;                            /* x and y bounds. */
+  REAL xminextreme;      /* Nonexistent x value used as a flag in sweepline. */
+  int invertices;                               /* Number of input vertices. */
+  int inelements;                              /* Number of input triangles. */
+  int insegments;                               /* Number of input segments. */
+  int holes;                                       /* Number of input holes. */
+  int regions;                                   /* Number of input regions. */
+  int undeads;    /* Number of input vertices that don't appear in the mesh. */
+  long edges;                                     /* Number of output edges. */
+  int mesh_dim;                                /* Dimension (ought to be 2). */
+  int nextras;                           /* Number of attributes per vertex. */
+  int eextras;                         /* Number of attributes per triangle. */
+  long hullsize;                          /* Number of edges in convex hull. */
+  int steinerleft;                 /* Number of Steiner points not yet used. */
+  int vertexmarkindex;         /* Index to find boundary marker of a vertex. */
+  int vertex2triindex;     /* Index to find a triangle adjacent to a vertex. */
+  int highorderindex;  /* Index to find extra nodes for high-order elements. */
+  int elemattribindex;            /* Index to find attributes of a triangle. */
+  int areaboundindex;             /* Index to find area bound of a triangle. */
+  int checksegments;         /* Are there segments in the triangulation yet? */
+  int checkquality;                  /* Has quality triangulation begun yet? */
+  int readnodefile;                           /* Has a .node file been read? */
+  long samples;              /* Number of random samples for point location. */
+
+  long incirclecount;                 /* Number of incircle tests performed. */
+  long counterclockcount;     /* Number of counterclockwise tests performed. */
+  long orient3dcount;           /* Number of 3D orientation tests performed. */
+  long hyperbolacount;      /* Number of right-of-hyperbola tests performed. */
+  long circumcentercount;  /* Number of circumcenter calculations performed. */
+  long circletopcount;       /* Number of circle top calculations performed. */
+
+/* Triangular bounding box vertices.                                         */
+
+  vertex infvertex1, infvertex2, infvertex3;
+
+/* Pointer to the `triangle' that occupies all of "outer space."             */
+
+  triangle *dummytri;
+  triangle *dummytribase;    /* Keep base address so we can free() it later. */
+
+/* Pointer to the omnipresent subsegment.  Referenced by any triangle or     */
+/*   subsegment that isn't really connected to a subsegment at that          */
+/*   location.                                                               */
+
+  subseg *dummysub;
+  subseg *dummysubbase;      /* Keep base address so we can free() it later. */
+
+/* Pointer to a recently visited triangle.  Improves point location if       */
+/*   proximate vertices are inserted sequentially.                           */
+
+  struct otri recenttri;
+
+};                                                  /* End of `struct mesh'. */
+
+
+/* Data structure for command line switches and file names.  This structure  */
+/*   is used (instead of global variables) to allow reentrancy.              */
+
+struct behavior {
+
+/* Switches for the triangulator.                                            */
+/*   poly: -p switch.  refine: -r switch.                                    */
+/*   quality: -q switch.                                                     */
+/*     minangle: minimum angle bound, specified after -q switch.             */
+/*     goodangle: cosine squared of minangle.                                */
+/*     offconstant: constant used to place off-center Steiner points.        */
+/*   vararea: -a switch without number.                                      */
+/*   fixedarea: -a switch with number.                                       */
+/*     maxarea: maximum area bound, specified after -a switch.               */
+/*   usertest: -u switch.                                                    */
+/*   regionattrib: -A switch.  convex: -c switch.                            */
+/*   weighted: 1 for -w switch, 2 for -W switch.  jettison: -j switch        */
+/*   firstnumber: inverse of -z switch.  All items are numbered starting     */
+/*     from `firstnumber'.                                                   */
+/*   edgesout: -e switch.  voronoi: -v switch.                               */
+/*   neighbors: -n switch.  geomview: -g switch.                             */
+/*   nobound: -B switch.  nopolywritten: -P switch.                          */
+/*   nonodewritten: -N switch.  noelewritten: -E switch.                     */
+/*   noiterationnum: -I switch.  noholes: -O switch.                         */
+/*   noexact: -X switch.                                                     */
+/*   order: element order, specified after -o switch.                        */
+/*   nobisect: count of how often -Y switch is selected.                     */
+/*   steiner: maximum number of Steiner points, specified after -S switch.   */
+/*   incremental: -i switch.  sweepline: -F switch.                          */
+/*   dwyer: inverse of -l switch.                                            */
+/*   splitseg: -s switch.                                                    */
+/*   nolenses: -L switch.  docheck: -C switch.                               */
+/*   quiet: -Q switch.  verbose: count of how often -V switch is selected.   */
+/*   usesegments: -p, -r, -q, or -c switch; determines whether segments are  */
+/*     used at all.                                                          */
+/*                                                                           */
+/* Read the instructions to find out the meaning of these switches.          */
+
+  int poly, refine, quality, vararea, fixedarea, usertest;
+  int regionattrib, convex, weighted, jettison;
+  int firstnumber;
+  int edgesout, voronoi, neighbors, geomview;
+  int nobound, nopolywritten, nonodewritten, noelewritten, noiterationnum;
+  int noholes, noexact, nolenses;
+  int incremental, sweepline, dwyer;
+  int splitseg;
+  int docheck;
+  int quiet, verbose;
+  int usesegments;
+  int order;
+  int nobisect;
+  int steiner;
+  REAL minangle, goodangle, offconstant;
+  REAL maxarea;
+
+/* Variables for file names.                                                 */
+
+#ifndef TRILIBRARY
+  char innodefilename[FILENAMESIZE];
+  char inelefilename[FILENAMESIZE];
+  char inpolyfilename[FILENAMESIZE];
+  char areafilename[FILENAMESIZE];
+  char outnodefilename[FILENAMESIZE];
+  char outelefilename[FILENAMESIZE];
+  char outpolyfilename[FILENAMESIZE];
+  char edgefilename[FILENAMESIZE];
+  char vnodefilename[FILENAMESIZE];
+  char vedgefilename[FILENAMESIZE];
+  char neighborfilename[FILENAMESIZE];
+  char offfilename[FILENAMESIZE];
+#endif /* not TRILIBRARY */
+
+};                                              /* End of `struct behavior'. */
+
+
+/*****************************************************************************/
+/*                                                                           */
+/*  Mesh manipulation primitives.  Each triangle contains three pointers to  */
+/*  other triangles, with orientations.  Each pointer points not to the      */
+/*  first byte of a triangle, but to one of the first three bytes of a       */
+/*  triangle.  It is necessary to extract both the triangle itself and the   */
+/*  orientation.  To save memory, I keep both pieces of information in one   */
+/*  pointer.  To make this possible, I assume that all triangles are aligned */
+/*  to four-byte boundaries.  The decode() routine below decodes a pointer,  */
+/*  extracting an orientation (in the range 0 to 2) and a pointer to the     */
+/*  beginning of a triangle.  The encode() routine compresses a pointer to a */
+/*  triangle and an orientation into a single pointer.  My assumptions that  */
+/*  triangles are four-byte-aligned and that the `unsigned long' type is     */
+/*  long enough to hold a pointer are two of the few kludges in this program.*/
+/*                                                                           */
+/*  Subsegments are manipulated similarly.  A pointer to a subsegment        */
+/*  carries both an address and an orientation in the range 0 to 1.          */
+/*                                                                           */
+/*  The other primitives take an oriented triangle or oriented subsegment,   */
+/*  and return an oriented triangle or oriented subsegment or vertex; or     */
+/*  they change the connections in the data structure.                       */
+/*                                                                           */
+/*  Below, triangles and subsegments are denoted by their vertices.  The     */
+/*  triangle abc has origin (org) a, destination (dest) b, and apex (apex)   */
+/*  c.  These vertices occur in counterclockwise order about the triangle.   */
+/*  The handle abc may simultaneously denote vertex a, edge ab, and triangle */
+/*  abc.                                                                     */
+/*                                                                           */
+/*  Similarly, the subsegment ab has origin (sorg) a and destination (sdest) */
+/*  b.  If ab is thought to be directed upward (with b directly above a),    */
+/*  then the handle ab is thought to grasp the right side of ab, and may     */
+/*  simultaneously denote vertex a and edge ab.                              */
+/*                                                                           */
+/*  An asterisk (*) denotes a vertex whose identity is unknown.              */
+/*                                                                           */
+/*  Given this notation, a partial list of mesh manipulation primitives      */
+/*  follows.                                                                 */
+/*                                                                           */
+/*                                                                           */
+/*  For triangles:                                                           */
+/*                                                                           */
+/*  sym:  Find the abutting triangle; same edge.                             */
+/*  sym(abc) -> ba*                                                          */
+/*                                                                           */
+/*  lnext:  Find the next edge (counterclockwise) of a triangle.             */
+/*  lnext(abc) -> bca                                                        */
+/*                                                                           */
+/*  lprev:  Find the previous edge (clockwise) of a triangle.                */
+/*  lprev(abc) -> cab                                                        */
+/*                                                                           */
+/*  onext:  Find the next edge counterclockwise with the same origin.        */
+/*  onext(abc) -> ac*                                                        */
+/*                                                                           */
+/*  oprev:  Find the next edge clockwise with the same origin.               */
+/*  oprev(abc) -> a*b                                                        */
+/*                                                                           */
+/*  dnext:  Find the next edge counterclockwise with the same destination.   */
+/*  dnext(abc) -> *ba                                                        */
+/*                                                                           */
+/*  dprev:  Find the next edge clockwise with the same destination.          */
+/*  dprev(abc) -> cb*                                                        */
+/*                                                                           */
+/*  rnext:  Find the next edge (counterclockwise) of the adjacent triangle.  */
+/*  rnext(abc) -> *a*                                                        */
+/*                                                                           */
+/*  rprev:  Find the previous edge (clockwise) of the adjacent triangle.     */
+/*  rprev(abc) -> b**                                                        */
+/*                                                                           */
+/*  org:  Origin          dest:  Destination          apex:  Apex            */
+/*  org(abc) -> a         dest(abc) -> b              apex(abc) -> c         */
+/*                                                                           */
+/*  bond:  Bond two triangles together at the resepective handles.           */
+/*  bond(abc, bad)                                                           */
+/*                                                                           */
+/*                                                                           */
+/*  For subsegments:                                                         */
+/*                                                                           */
+/*  ssym:  Reverse the orientation of a subsegment.                          */
+/*  ssym(ab) -> ba                                                           */
+/*                                                                           */
+/*  spivot:  Find adjoining subsegment with the same origin.                 */
+/*  spivot(ab) -> a*                                                         */
+/*                                                                           */
+/*  snext:  Find next subsegment in sequence.                                */
+/*  snext(ab) -> b*                                                          */
+/*                                                                           */
+/*  sorg:  Origin                      sdest:  Destination                   */
+/*  sorg(ab) -> a                      sdest(ab) -> b                        */
+/*                                                                           */
+/*  sbond:  Bond two subsegments together at the respective origins.         */
+/*  sbond(ab, ac)                                                            */
+/*                                                                           */
+/*                                                                           */
+/*  For interacting tetrahedra and subfacets:                                */
+/*                                                                           */
+/*  tspivot:  Find a subsegment abutting a triangle.                         */
+/*  tspivot(abc) -> ba                                                       */
+/*                                                                           */
+/*  stpivot:  Find a triangle abutting a subsegment.                         */
+/*  stpivot(ab) -> ba*                                                       */
+/*                                                                           */
+/*  tsbond:  Bond a triangle to a subsegment.                                */
+/*  tsbond(abc, ba)                                                          */
+/*                                                                           */
+/*****************************************************************************/
+
+/********* Mesh manipulation primitives begin here                   *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/* Fast lookup arrays to speed some of the mesh manipulation primitives.     */
+
+int plus1mod3[3] = {1, 2, 0};
+int minus1mod3[3] = {2, 0, 1};
+
+/********* Primitives for triangles                                  *********/
+/*                                                                           */
+/*                                                                           */
+
+/* decode() converts a pointer to an oriented triangle.  The orientation is  */
+/*   extracted from the two least significant bits of the pointer.           */
+
+#define decode(ptr, otri)                                                     \
+  (otri).orient = (int) ((unsigned long) (ptr) & (unsigned long) 3l);         \
+  (otri).tri = (triangle *)                                                   \
+                  ((unsigned long) (ptr) ^ (unsigned long) (otri).orient)
+
+/* encode() compresses an oriented triangle into a single pointer.  It       */
+/*   relies on the assumption that all triangles are aligned to four-byte    */
+/*   boundaries, so the two least significant bits of (otri).tri are zero.   */
+
+#define encode(otri)                                                          \
+  (triangle) ((unsigned long) (otri).tri | (unsigned long) (otri).orient)
+
+/* The following handle manipulation primitives are all described by Guibas  */
+/*   and Stolfi.  However, Guibas and Stolfi use an edge-based data          */
+/*   structure, whereas I use a triangle-based data structure.               */
+
+/* sym() finds the abutting triangle, on the same edge.  Note that the edge  */
+/*   direction is necessarily reversed, because the handle specified by an   */
+/*   oriented triangle is directed counterclockwise around the triangle.     */
+
+#define sym(otri1, otri2)                                                     \
+  ptr = (otri1).tri[(otri1).orient];                                          \
+  decode(ptr, otri2);
+
+#define symself(otri)                                                         \
+  ptr = (otri).tri[(otri).orient];                                            \
+  decode(ptr, otri);
+
+/* lnext() finds the next edge (counterclockwise) of a triangle.             */
+
+#define lnext(otri1, otri2)                                                   \
+  (otri2).tri = (otri1).tri;                                                  \
+  (otri2).orient = plus1mod3[(otri1).orient]
+
+#define lnextself(otri)                                                       \
+  (otri).orient = plus1mod3[(otri).orient]
+
+/* lprev() finds the previous edge (clockwise) of a triangle.                */
+
+#define lprev(otri1, otri2)                                                   \
+  (otri2).tri = (otri1).tri;                                                  \
+  (otri2).orient = minus1mod3[(otri1).orient]
+
+#define lprevself(otri)                                                       \
+  (otri).orient = minus1mod3[(otri).orient]
+
+/* onext() spins counterclockwise around a vertex; that is, it finds the     */
+/*   next edge with the same origin in the counterclockwise direction.  This */
+/*   edge is part of a different triangle.                                   */
+
+#define onext(otri1, otri2)                                                   \
+  lprev(otri1, otri2);                                                        \
+  symself(otri2);
+
+#define onextself(otri)                                                       \
+  lprevself(otri);                                                            \
+  symself(otri);
+
+/* oprev() spins clockwise around a vertex; that is, it finds the next edge  */
+/*   with the same origin in the clockwise direction.  This edge is part of  */
+/*   a different triangle.                                                   */
+
+#define oprev(otri1, otri2)                                                   \
+  sym(otri1, otri2);                                                          \
+  lnextself(otri2);
+
+#define oprevself(otri)                                                       \
+  symself(otri);                                                              \
+  lnextself(otri);
+
+/* dnext() spins counterclockwise around a vertex; that is, it finds the     */
+/*   next edge with the same destination in the counterclockwise direction.  */
+/*   This edge is part of a different triangle.                              */
+
+#define dnext(otri1, otri2)                                                   \
+  sym(otri1, otri2);                                                          \
+  lprevself(otri2);
+
+#define dnextself(otri)                                                       \
+  symself(otri);                                                              \
+  lprevself(otri);
+
+/* dprev() spins clockwise around a vertex; that is, it finds the next edge  */
+/*   with the same destination in the clockwise direction.  This edge is     */
+/*   part of a different triangle.                                           */
+
+#define dprev(otri1, otri2)                                                   \
+  lnext(otri1, otri2);                                                        \
+  symself(otri2);
+
+#define dprevself(otri)                                                       \
+  lnextself(otri);                                                            \
+  symself(otri);
+
+/* rnext() moves one edge counterclockwise about the adjacent triangle.      */
+/*   (It's best understood by reading Guibas and Stolfi.  It involves        */
+/*   changing triangles twice.)                                              */
+
+#define rnext(otri1, otri2)                                                   \
+  sym(otri1, otri2);                                                          \
+  lnextself(otri2);                                                           \
+  symself(otri2);
+
+#define rnextself(otri)                                                       \
+  symself(otri);                                                              \
+  lnextself(otri);                                                            \
+  symself(otri);
+
+/* rprev() moves one edge clockwise about the adjacent triangle.             */
+/*   (It's best understood by reading Guibas and Stolfi.  It involves        */
+/*   changing triangles twice.)                                              */
+
+#define rprev(otri1, otri2)                                                   \
+  sym(otri1, otri2);                                                          \
+  lprevself(otri2);                                                           \
+  symself(otri2);
+
+#define rprevself(otri)                                                       \
+  symself(otri);                                                              \
+  lprevself(otri);                                                            \
+  symself(otri);
+
+/* These primitives determine or set the origin, destination, or apex of a   */
+/* triangle.                                                                 */
+
+#define org(otri, vertexptr)                                                  \
+  vertexptr = (vertex) (otri).tri[plus1mod3[(otri).orient] + 3]
+
+#define dest(otri, vertexptr)                                                 \
+  vertexptr = (vertex) (otri).tri[minus1mod3[(otri).orient] + 3]
+
+#define apex(otri, vertexptr)                                                 \
+  vertexptr = (vertex) (otri).tri[(otri).orient + 3]
+
+#define setorg(otri, vertexptr)                                               \
+  (otri).tri[plus1mod3[(otri).orient] + 3] = (triangle) vertexptr
+
+#define setdest(otri, vertexptr)                                              \
+  (otri).tri[minus1mod3[(otri).orient] + 3] = (triangle) vertexptr
+
+#define setapex(otri, vertexptr)                                              \
+  (otri).tri[(otri).orient + 3] = (triangle) vertexptr
+
+/* Bond two triangles together.                                              */
+
+#define bond(otri1, otri2)                                                    \
+  (otri1).tri[(otri1).orient] = encode(otri2);                                \
+  (otri2).tri[(otri2).orient] = encode(otri1)
+
+/* Dissolve a bond (from one side).  Note that the other triangle will still */
+/*   think it's connected to this triangle.  Usually, however, the other     */
+/*   triangle is being deleted entirely, or bonded to another triangle, so   */
+/*   it doesn't matter.                                                      */
+
+#define dissolve(otri)                                                        \
+  (otri).tri[(otri).orient] = (triangle) m->dummytri
+
+/* Copy an oriented triangle.                                                */
+
+#define otricopy(otri1, otri2)                                                \
+  (otri2).tri = (otri1).tri;                                                  \
+  (otri2).orient = (otri1).orient
+
+/* Test for equality of oriented triangles.                                  */
+
+#define otriequal(otri1, otri2)                                               \
+  (((otri1).tri == (otri2).tri) &&                                            \
+   ((otri1).orient == (otri2).orient))
+
+/* Primitives to infect or cure a triangle with the virus.  These rely on    */
+/*   the assumption that all subsegments are aligned to four-byte boundaries.*/
+
+#define infect(otri)                                                          \
+  (otri).tri[6] = (triangle)                                                  \
+                    ((unsigned long) (otri).tri[6] | (unsigned long) 2l)
+
+#define uninfect(otri)                                                        \
+  (otri).tri[6] = (triangle)                                                  \
+                    ((unsigned long) (otri).tri[6] & ~ (unsigned long) 2l)
+
+/* Test a triangle for viral infection.                                      */
+
+#define infected(otri)                                                        \
+  (((unsigned long) (otri).tri[6] & (unsigned long) 2l) != 0l)
+
+/* Check or set a triangle's attributes.                                     */
+
+#define elemattribute(otri, attnum)                                           \
+  ((REAL *) (otri).tri)[m->elemattribindex + (attnum)]
+
+#define setelemattribute(otri, attnum, value)                                 \
+  ((REAL *) (otri).tri)[m->elemattribindex + (attnum)] = value
+
+/* Check or set a triangle's maximum area bound.                             */
+
+#define areabound(otri)  ((REAL *) (otri).tri)[m->areaboundindex]
+
+#define setareabound(otri, value)                                             \
+  ((REAL *) (otri).tri)[m->areaboundindex] = value
+
+/* Check or set a triangle's deallocation.  Its second pointer is set to     */
+/*   NULL to indicate that it is not allocated.  (Its first pointer is used  */
+/*   for the stack of dead items.)  Its fourth pointer (its first vertex)    */
+/*   is set to NULL in case a `badtriang' structure points to it.            */
+
+#define deadtri(tria)  ((tria)[1] == (triangle) NULL)
+
+#define killtri(tria)                                                         \
+  (tria)[1] = (triangle) NULL;                                                \
+  (tria)[3] = (triangle) NULL
+
+/********* Primitives for subsegments                                *********/
+/*                                                                           */
+/*                                                                           */
+
+/* sdecode() converts a pointer to an oriented subsegment.  The orientation  */
+/*   is extracted from the least significant bit of the pointer.  The two    */
+/*   least significant bits (one for orientation, one for viral infection)   */
+/*   are masked out to produce the real pointer.                             */
+
+#define sdecode(sptr, osub)                                                   \
+  (osub).ssorient = (int) ((unsigned long) (sptr) & (unsigned long) 1l);      \
+  (osub).ss = (subseg *)                                                      \
+              ((unsigned long) (sptr) & ~ (unsigned long) 3l)
+
+/* sencode() compresses an oriented subsegment into a single pointer.  It    */
+/*   relies on the assumption that all subsegments are aligned to two-byte   */
+/*   boundaries, so the least significant bit of (osub).ss is zero.          */
+
+#define sencode(osub)                                                         \
+  (subseg) ((unsigned long) (osub).ss | (unsigned long) (osub).ssorient)
+
+/* ssym() toggles the orientation of a subsegment.                           */
+
+#define ssym(osub1, osub2)                                                    \
+  (osub2).ss = (osub1).ss;                                                    \
+  (osub2).ssorient = 1 - (osub1).ssorient
+
+#define ssymself(osub)                                                        \
+  (osub).ssorient = 1 - (osub).ssorient
+
+/* spivot() finds the other subsegment (from the same segment) that shares   */
+/*   the same origin.                                                        */
+
+#define spivot(osub1, osub2)                                                  \
+  sptr = (osub1).ss[(osub1).ssorient];                                        \
+  sdecode(sptr, osub2)
+
+#define spivotself(osub)                                                      \
+  sptr = (osub).ss[(osub).ssorient];                                          \
+  sdecode(sptr, osub)
+
+/* snext() finds the next subsegment (from the same segment) in sequence;    */
+/*   one whose origin is the input subsegment's destination.                 */
+
+#define snext(osub1, osub2)                                                   \
+  sptr = (osub1).ss[1 - (osub1).ssorient];                                    \
+  sdecode(sptr, osub2)
+
+#define snextself(osub)                                                       \
+  sptr = (osub).ss[1 - (osub).ssorient];                                      \
+  sdecode(sptr, osub)
+
+/* These primitives determine or set the origin or destination of a          */
+/*   subsegment.                                                             */
+
+#define sorg(osub, vertexptr)                                                 \
+  vertexptr = (vertex) (osub).ss[2 + (osub).ssorient]
+
+#define sdest(osub, vertexptr)                                                \
+  vertexptr = (vertex) (osub).ss[3 - (osub).ssorient]
+
+#define setsorg(osub, vertexptr)                                              \
+  (osub).ss[2 + (osub).ssorient] = (subseg) vertexptr
+
+#define setsdest(osub, vertexptr)                                             \
+  (osub).ss[3 - (osub).ssorient] = (subseg) vertexptr
+
+/* These primitives read or set a boundary marker.  Boundary markers are     */
+/*   used to hold user-defined tags for setting boundary conditions in       */
+/*   finite element solvers.                                                 */
+
+#define mark(osub)  (* (int *) ((osub).ss + 6))
+
+#define setmark(osub, value)                                                  \
+  * (int *) ((osub).ss + 6) = value
+
+/* Bond two subsegments together.                                            */
+
+#define sbond(osub1, osub2)                                                   \
+  (osub1).ss[(osub1).ssorient] = sencode(osub2);                              \
+  (osub2).ss[(osub2).ssorient] = sencode(osub1)
+
+/* Dissolve a subsegment bond (from one side).  Note that the other          */
+/*   subsegment will still think it's connected to this subsegment.          */
+
+#define sdissolve(osub)                                                       \
+  (osub).ss[(osub).ssorient] = (subseg) m->dummysub
+
+/* Copy a subsegment.                                                        */
+
+#define subsegcopy(osub1, osub2)                                              \
+  (osub2).ss = (osub1).ss;                                                    \
+  (osub2).ssorient = (osub1).ssorient
+
+/* Test for equality of subsegments.                                         */
+
+#define subsegequal(osub1, osub2)                                             \
+  (((osub1).ss == (osub2).ss) &&                                              \
+   ((osub1).ssorient == (osub2).ssorient))
+
+/* Check or set a subsegment's deallocation.  Its second pointer is set to   */
+/*   NULL to indicate that it is not allocated.  (Its first pointer is used  */
+/*   for the stack of dead items.)  Its third pointer (its first vertex)     */
+/*   is set to NULL in case a `badsubseg' structure points to it.            */
+
+#define deadsubseg(sub)  ((sub)[1] == (subseg) NULL)
+
+#define killsubseg(sub)                                                       \
+  (sub)[1] = (subseg) NULL;                                                   \
+  (sub)[2] = (subseg) NULL
+
+/********* Primitives for interacting triangles and subsegments      *********/
+/*                                                                           */
+/*                                                                           */
+
+/* tspivot() finds a subsegment abutting a triangle.                         */
+
+#define tspivot(otri, osub)                                                   \
+  sptr = (subseg) (otri).tri[6 + (otri).orient];                              \
+  sdecode(sptr, osub)
+
+/* stpivot() finds a triangle abutting a subsegment.  It requires that the   */
+/*   variable `ptr' of type `triangle' be defined.                           */
+
+#define stpivot(osub, otri)                                                   \
+  ptr = (triangle) (osub).ss[4 + (osub).ssorient];                            \
+  decode(ptr, otri)
+
+/* Bond a triangle to a subsegment.                                          */
+
+#define tsbond(otri, osub)                                                    \
+  (otri).tri[6 + (otri).orient] = (triangle) sencode(osub);                   \
+  (osub).ss[4 + (osub).ssorient] = (subseg) encode(otri)
+
+/* Dissolve a bond (from the triangle side).                                 */
+
+#define tsdissolve(otri)                                                      \
+  (otri).tri[6 + (otri).orient] = (triangle) m->dummysub
+
+/* Dissolve a bond (from the subsegment side).                               */
+
+#define stdissolve(osub)                                                      \
+  (osub).ss[4 + (osub).ssorient] = (subseg) m->dummytri
+
+/********* Primitives for vertices                                   *********/
+/*                                                                           */
+/*                                                                           */
+
+#define vertexmark(vx)  ((int *) (vx))[m->vertexmarkindex]
+
+#define setvertexmark(vx, value)                                              \
+  ((int *) (vx))[m->vertexmarkindex] = value
+
+#define vertextype(vx)  ((int *) (vx))[m->vertexmarkindex + 1]
+
+#define setvertextype(vx, value)                                              \
+  ((int *) (vx))[m->vertexmarkindex + 1] = value
+
+#define vertex2tri(vx)  ((triangle *) (vx))[m->vertex2triindex]
+
+#define setvertex2tri(vx, value)                                              \
+  ((triangle *) (vx))[m->vertex2triindex] = value
+
+/**                                                                         **/
+/**                                                                         **/
+/********* Mesh manipulation primitives end here                     *********/
+
+/********* User-defined triangle evaluation routine begins here      *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  triunsuitable()   Determine if a triangle is unsuitable, and thus must   */
+/*                    be further refined.                                    */
+/*                                                                           */
+/*  You may write your own procedure that decides whether or not a selected  */
+/*  triangle is too big (and needs to be refined).  There are two ways to do */
+/*  this.                                                                    */
+/*                                                                           */
+/*  (1)  Modify the procedure `triunsuitable' below, then recompile          */
+/*  Triangle.                                                                */
+/*                                                                           */
+/*  (2)  Define the symbol EXTERNAL_TEST (either by adding the definition    */
+/*  to this file, or by using the appropriate compiler switch).  This way,   */
+/*  you can compile triangle.c separately from your test.  Write your own    */
+/*  `triunsuitable' procedure in a separate C file (using the same prototype */
+/*  as below).  Compile it and link the object code with triangle.o.         */
+/*                                                                           */
+/*  This procedure returns 1 if the triangle is too large and should be      */
+/*  refined; 0 otherwise.                                                    */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef EXTERNAL_TEST
+
+int triunsuitable();
+
+#else /* not EXTERNAL_TEST */
+
+int triunsuitable(triorg, tridest, triapex, area)
+vertex triorg;                              /* The triangle's origin vertex. */
+vertex tridest;                        /* The triangle's destination vertex. */
+vertex triapex;                               /* The triangle's apex vertex. */
+REAL area;                                      /* The area of the triangle. */
+{
+  REAL dxoa, dxda, dxod;
+  REAL dyoa, dyda, dyod;
+  REAL oalen, dalen, odlen;
+  REAL maxlen;
+
+  dxoa = triorg[0] - triapex[0];
+  dyoa = triorg[1] - triapex[1];
+  dxda = tridest[0] - triapex[0];
+  dyda = tridest[1] - triapex[1];
+  dxod = triorg[0] - tridest[0];
+  dyod = triorg[1] - tridest[1];
+  /* Find the squares of the lengths of the triangle's three edges. */
+  oalen = dxoa * dxoa + dyoa * dyoa;
+  dalen = dxda * dxda + dyda * dyda;
+  odlen = dxod * dxod + dyod * dyod;
+  /* Find the square of the length of the longest edge. */
+  maxlen = (dalen > oalen) ? dalen : oalen;
+  maxlen = (odlen > maxlen) ? odlen : maxlen;
+
+  if (maxlen > 0.05 * (triorg[0] * triorg[0] + triorg[1] * triorg[1]) + 0.02) {
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+#endif /* not EXTERNAL_TEST */
+
+/**                                                                         **/
+/**                                                                         **/
+/********* User-defined triangle evaluation routine ends here        *********/
+
+/********* Memory allocation wrappers begin here                     *********/
+/**                                                                         **/
+/**                                                                         **/
+
+#ifdef ANSI_DECLARATORS
+VOID *trimalloc(int size)
+#else /* not ANSI_DECLARATORS */
+VOID *trimalloc(size)
+int size;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  VOID *memptr;
+
+  memptr = malloc((unsigned int) size);
+  if (memptr == (VOID *) NULL) {
+    printf("Error:  Out of memory.\n");
+    exit(1);
+  }
+  return(memptr);
+}
+
+#ifdef ANSI_DECLARATORS
+void trifree(VOID *memptr)
+#else /* not ANSI_DECLARATORS */
+void trifree(memptr)
+VOID *memptr;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  free(memptr);
+}
+
+/**                                                                         **/
+/**                                                                         **/
+/********* Memory allocation wrappers end here                       *********/
+
+/********* User interaction routines begin here                      *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  syntax()   Print list of command line switches.                          */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef TRILIBRARY
+
+void syntax()
+{
+#ifdef CDT_ONLY
+#ifdef REDUCED
+  printf("triangle [-pAcjevngBPNEIOXzo_lQVh] input_file\n");
+#else /* not REDUCED */
+  printf("triangle [-pAcjevngBPNEIOXzo_iFlCQVh] input_file\n");
+#endif /* not REDUCED */
+#else /* not CDT_ONLY */
+#ifdef REDUCED
+  printf("triangle [-prq__a__uAcjevngBPNEIOXzo_YS__LlQVh] input_file\n");
+#else /* not REDUCED */
+  printf("triangle [-prq__a__uAcjevngBPNEIOXzo_YS__LiFlsCQVh] input_file\n");
+#endif /* not REDUCED */
+#endif /* not CDT_ONLY */
+
+  printf("    -p  Triangulates a Planar Straight Line Graph (.poly file).\n");
+#ifndef CDT_ONLY
+  printf("    -r  Refines a previously generated mesh.\n");
+  printf(
+    "    -q  Quality mesh generation.  A minimum angle may be specified.\n");
+  printf("    -a  Applies a maximum triangle area constraint.\n");
+  printf("    -u  Applies a user-defined triangle constraint.\n");
+#endif /* not CDT_ONLY */
+  printf(
+    "    -A  Applies attributes to identify triangles in certain regions.\n");
+  printf("    -c  Encloses the convex hull with segments.\n");
+  printf("    -w  Weighted Delaunay triangulation.\n");
+  printf("    -W  Regular triangulation (lower hull of a height field).\n");
+  printf("    -j  Jettison unused vertices from output .node file.\n");
+  printf("    -e  Generates an edge list.\n");
+  printf("    -v  Generates a Voronoi diagram.\n");
+  printf("    -n  Generates a list of triangle neighbors.\n");
+  printf("    -g  Generates an .off file for Geomview.\n");
+  printf("    -B  Suppresses output of boundary information.\n");
+  printf("    -P  Suppresses output of .poly file.\n");
+  printf("    -N  Suppresses output of .node file.\n");
+  printf("    -E  Suppresses output of .ele file.\n");
+  printf("    -I  Suppresses mesh iteration numbers.\n");
+  printf("    -O  Ignores holes in .poly file.\n");
+  printf("    -X  Suppresses use of exact arithmetic.\n");
+  printf("    -z  Numbers all items starting from zero (rather than one).\n");
+  printf("    -o2 Generates second-order subparametric elements.\n");
+#ifndef CDT_ONLY
+  printf("    -Y  Suppresses boundary segment splitting.\n");
+  printf("    -S  Specifies maximum number of added Steiner points.\n");
+  printf("    -L  Uses equatorial circles, not equatorial lenses.\n");
+#endif /* not CDT_ONLY */
+#ifndef REDUCED
+  printf("    -i  Uses incremental method, rather than divide-and-conquer.\n");
+  printf("    -F  Uses Fortune's sweepline algorithm, rather than d-and-c.\n");
+#endif /* not REDUCED */
+  printf("    -l  Uses vertical cuts only, rather than alternating cuts.\n");
+#ifndef REDUCED
+#ifndef CDT_ONLY
+  printf(
+    "    -s  Force segments into mesh by splitting (instead of using CDT).\n");
+  printf("    -L  Uses Ruppert's diametral spheres, not diametral lenses.\n");
+#endif /* not CDT_ONLY */
+  printf("    -C  Check consistency of final mesh.\n");
+#endif /* not REDUCED */
+  printf("    -Q  Quiet:  No terminal output except errors.\n");
+  printf("    -V  Verbose:  Detailed information on what I'm doing.\n");
+  printf("    -h  Help:  Detailed instructions for Triangle.\n");
+  exit(0);
+}
+
+#endif /* not TRILIBRARY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  info()   Print out complete instructions.                                */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef TRILIBRARY
+
+void info()
+{
+  printf("Triangle\n");
+  printf(
+"A Two-Dimensional Quality Mesh Generator and Delaunay Triangulator.\n");
+  printf("Version 1.5\n\n");
+  printf(
+"Copyright 1993, 1995, 1997, 1998, 2002, 2004 Jonathan Richard Shewchuk\n");
+  printf("2360 Woolsey #H / Berkeley, California 94705-1927\n");
+  printf("Bugs/comments to jrs@cs.berkeley.edu\n");
+  printf(
+"Created as part of the Archimedes project (tools for parallel FEM).\n");
+  printf(
+"Supported in part by NSF Grant CMS-9318163 and an NSERC 1967 Scholarship.\n");
+  printf("There is no warranty whatsoever.  Use at your own risk.\n");
+#ifdef SINGLE
+  printf("This executable is compiled for single precision arithmetic.\n\n\n");
+#else /* not SINGLE */
+  printf("This executable is compiled for double precision arithmetic.\n\n\n");
+#endif /* not SINGLE */
+  printf(
+"Triangle generates exact Delaunay triangulations, constrained Delaunay\n");
+  printf(
+"triangulations, Voronoi diagrams, and quality conforming Delaunay\n");
+  printf(
+"triangulations.  The latter can be generated with no small angles, and are\n"
+);
+  printf(
+"thus suitable for finite element analysis.  If no command line switches are\n"
+);
+  printf(
+"specified, your .node input file is read, and the Delaunay triangulation is\n"
+);
+  printf("returned in .node and .ele output files.  The command syntax is:\n");
+  printf("\n");
+  printf("triangle [-prq__a__uAcjevngBPNEIOXzo_YS__LiFlsCQVh] input_file\n");
+  printf("\n");
+  printf(
+"Underscores indicate that numbers may optionally follow certain switches.\n");
+  printf(
+"Do not leave any space between a switch and its numeric parameter.\n");
+  printf(
+"input_file must be a file with extension .node, or extension .poly if the\n");
+  printf(
+"-p switch is used.  If -r is used, you must supply .node and .ele files,\n");
+  printf(
+"and possibly a .poly file and an .area file as well.  The formats of these\n"
+);
+  printf("files are described below.\n\n");
+  printf("Command Line Switches:\n\n");
+  printf(
+"    -p  Reads a Planar Straight Line Graph (.poly file), which can specify\n"
+);
+  printf("        vertices, segments, holes, regional attributes, and area\n");
+  printf(
+"        constraints.  Generates a constrained Delaunay triangulation (CDT)\n"
+);
+  printf(
+"        fitting the input; or, if -s, -q, -a, or -u is used, a conforming\n");
+  printf(
+"        constrained Delaunay triangulation (CCDT).  If -p is not used,\n");
+  printf("        Triangle reads a .node file by default.\n");
+  printf(
+"    -r  Refines a previously generated mesh.  The mesh is read from a .node\n"
+);
+  printf(
+"        file and an .ele file.  If -p is also used, a .poly file is read\n");
+  printf(
+"        and used to constrain segments in the mesh.  If -a is also used\n");
+  printf(
+"        (with no number following), an .area file is read and used to\n");
+  printf(
+"        impose area constraints on the mesh.  Further details on refinement\n"
+);
+  printf("        are given below.\n");
+  printf(
+"    -q  Quality mesh generation by my variant of Jim Ruppert's Delaunay\n");
+  printf(
+"        refinement algorithm.  Adds vertices to the mesh to ensure that no\n"
+);
+  printf(
+"        angles smaller than 20 degrees occur.  An alternative minimum angle\n"
+);
+  printf(
+"        may be specified after the `q'.  If the minimum angle is 20.7\n");
+  printf(
+"        degrees or smaller, the triangulation algorithm is mathematically\n");
+  printf(
+"        guaranteed to terminate (assuming infinite precision arithmetic--\n");
+  printf(
+"        Triangle may fail to terminate if you run out of precision).  In\n");
+  printf(
+"        practice, the algorithm often succeeds for minimum angles up to\n");
+  printf(
+"        33.8 degrees.  For some meshes, however, it may be necessary to\n");
+  printf(
+"        reduce the minimum angle to avoid problems associated with\n");
+  printf(
+"        insufficient floating-point precision.  The specified angle may\n");
+  printf("        include a decimal point.\n");
+  printf(
+"    -a  Imposes a maximum triangle area.  If a number follows the `a', no\n");
+  printf(
+"        triangle is generated whose area is larger than that number.  If no\n"
+);
+  printf(
+"        number is specified, an .area file (if -r is used) or .poly file\n");
+  printf(
+"        (if -r is not used) specifies a set of maximum area constraints.\n");
+  printf(
+"        An .area file contains a separate area constraint for each\n");
+  printf(
+"        triangle, and is useful for refining a finite element mesh based on\n"
+);
+  printf(
+"        a posteriori error estimates.  A .poly file can optionally contain\n"
+);
+  printf(
+"        an area constraint for each segment-bounded region, thereby\n");
+  printf(
+"        controlling triangle densities in a first triangulation of a PSLG.\n"
+);
+  printf(
+"        You can impose both a fixed area constraint and a varying area\n");
+  printf(
+"        constraint by invoking the -a switch twice, once with and once\n");
+  printf(
+"        without a number following.  Each area specified may include a\n");
+  printf("        decimal point.\n");
+  printf(
+"    -u  Imposes a user-defined constraint on triangle size.  There are two\n"
+);
+  printf(
+"        ways to use this feature.  One is to edit the triunsuitable()\n");
+  printf(
+"        procedure in triangle.c to encode any constraint you like, then\n");
+  printf(
+"        recompile Triangle.  The other is to compile triangle.c with the\n");
+  printf(
+"        EXTERNAL_TEST symbol set (compiler switch -DEXTERNAL_TEST), then\n");
+  printf(
+"        link Triangle against a separate object file that implements\n");
+  printf(
+"        triunsuitable().  In either case, the -u switch causes the user-\n");
+  printf("        defined test to be applied to every triangle.\n");
+  printf(
+"    -A  Assigns an additional attribute to each triangle that identifies\n");
+  printf(
+"        what segment-bounded region each triangle belongs to.  Attributes\n");
+  printf(
+"        are assigned to regions by the .poly file.  If a region is not\n");
+  printf(
+"        explicitly marked by the .poly file, triangles in that region are\n");
+  printf(
+"        assigned an attribute of zero.  The -A switch has an effect only\n");
+  printf("        when the -p switch is used and the -r switch is not.\n");
+  printf(
+"    -c  Creates segments on the convex hull of the triangulation.  If you\n");
+  printf(
+"        are triangulating a vertex set, this switch causes a .poly file to\n"
+);
+  printf(
+"        be written, containing all edges in the convex hull.  If you are\n");
+  printf(
+"        triangulating a PSLG, this switch specifies that the whole convex\n");
+  printf(
+"        hull of the PSLG should be triangulated, regardless of what\n");
+  printf(
+"        segments the PSLG has.  If you do not use this switch when\n");
+  printf(
+"        triangulating a PSLG, it is assumed that you have identified the\n");
+  printf(
+"        region to be triangulated by surrounding it with segments of the\n");
+  printf(
+"        input PSLG.  Beware:  if you are not careful, this switch can cause\n"
+);
+  printf(
+"        the introduction of an extremely thin angle between a PSLG segment\n"
+);
+  printf(
+"        and a convex hull segment, which can cause overrefinement (and\n");
+  printf(
+"        possibly failure if Triangle runs out of precision).  If you are\n");
+  printf(
+"        refining a mesh, the -c switch works differently; it generates the\n"
+);
+  printf(
+"        set of boundary edges of the mesh (useful if no .poly file was\n");
+  printf("        read).\n");
+  printf(
+"    -j  Jettisons vertices that are not part of the final triangulation\n");
+  printf(
+"        from the output .node file.  By default, Triangle copies all\n");
+  printf(
+"        vertices in the input .node file to the output .node file, in the\n");
+  printf(
+"        same order, so their indices do not change.  The -j switch prevents\n"
+);
+  printf(
+"        duplicated input vertices from appearing in the output .node file;\n"
+);
+  printf(
+"        hence, if two input vertices have exactly the same coordinates,\n");
+  printf(
+"        only the first appears in the output.  If any vertices are\n");
+  printf(
+"        jettisoned, the vertex numbering in the output .node file differs\n");
+  printf("        from that of the input .node file.\n");
+  printf(
+"    -e  Outputs (to an .edge file) a list of edges of the triangulation.\n");
+  printf(
+"    -v  Outputs the Voronoi diagram associated with the triangulation.\n");
+  printf(
+"        Does not attempt to detect degeneracies, so some Voronoi vertices\n");
+  printf(
+"        may be duplicated.  See the discussion of Voronoi diagrams below.\n");
+  printf(
+"    -n  Outputs (to a .neigh file) a list of triangles neighboring each\n");
+  printf("        triangle.\n");
+  printf(
+"    -g  Outputs the mesh to an Object File Format (.off) file, suitable for\n"
+);
+  printf("        viewing with the Geometry Center's Geomview package.\n");
+  printf(
+"    -B  No boundary markers in the output .node, .poly, and .edge output\n");
+  printf(
+"        files.  See the detailed discussion of boundary markers below.\n");
+  printf(
+"    -P  No output .poly file.  Saves disk space, but you lose the ability\n");
+  printf(
+"        to maintain constraining segments on later refinements of the mesh.\n"
+);
+  printf("    -N  No output .node file.\n");
+  printf("    -E  No output .ele file.\n");
+  printf(
+"    -I  No iteration numbers.  Suppresses the output of .node and .poly\n");
+  printf(
+"        files, so your input files won't be overwritten.  (If your input is\n"
+);
+  printf(
+"        a .poly file only, a .node file is written.)  Cannot be used with\n");
+  printf(
+"        the -r switch, because that would overwrite your input .ele file.\n");
+  printf(
+"        Shouldn't be used with the -q, -a, -u, or -s switch if you are\n");
+  printf(
+"        using a .node file for input, because no .node file is written, so\n"
+);
+  printf("        there is no record of any added Steiner points.\n");
+  printf("    -O  No holes.  Ignores the holes in the .poly file.\n");
+  printf(
+"    -X  No exact arithmetic.  Normally, Triangle uses exact floating-point\n"
+);
+  printf(
+"        arithmetic for certain tests if it thinks the inexact tests are not\n"
+);
+  printf(
+"        accurate enough.  Exact arithmetic ensures the robustness of the\n");
+  printf(
+"        triangulation algorithms, despite floating-point roundoff error.\n");
+  printf(
+"        Disabling exact arithmetic with the -X switch causes a small\n");
+  printf(
+"        improvement in speed and creates the possibility (albeit small)\n");
+  printf(
+"        that Triangle will fail to produce a valid mesh.  Not recommended.\n"
+);
+  printf(
+"    -z  Numbers all items starting from zero (rather than one).  Note that\n"
+);
+  printf(
+"        this switch is normally overrided by the value used to number the\n");
+  printf(
+"        first vertex of the input .node or .poly file.  However, this\n");
+  printf(
+"        switch is useful when calling Triangle from another program.\n");
+  printf(
+"    -o2 Generates second-order subparametric elements with six nodes each.\n"
+);
+  printf(
+"    -Y  No new vertices on the boundary.  This switch is useful when the\n");
+  printf(
+"        mesh boundary must be preserved so that it conforms to some\n");
+  printf(
+"        adjacent mesh.  Be forewarned that you will probably sacrifice some\n"
+);
+  printf(
+"        of the quality of the mesh; Triangle will try, but the resulting\n");
+  printf(
+"        mesh may contain triangles of poor aspect ratio.  Works well if all\n"
+);
+  printf(
+"        the boundary vertices are closely spaced.  Specify this switch\n");
+  printf(
+"        twice (`-YY') to prevent all segment splitting, including internal\n"
+);
+  printf("        boundaries.\n");
+  printf(
+"    -S  Specifies the maximum number of Steiner points (vertices that are\n");
+  printf(
+"        not in the input, but are added to meet the constraints on minimum\n"
+);
+  printf(
+"        angle and maximum area).  The default is to allow an unlimited\n");
+  printf(
+"        number.  If you specify this switch with no number after it,\n");
+  printf(
+"        the limit is set to zero.  Triangle always adds vertices at segment\n"
+);
+  printf(
+"        intersections, even if it needs to use more vertices than the limit\n"
+);
+  printf(
+"        you set.  When Triangle inserts segments by splitting (-s), it\n");
+  printf(
+"        always adds enough vertices to ensure that all the segments of the\n"
+);
+  printf("        PLSG are recovered, ignoring the limit if necessary.\n");
+  printf(
+"    -L  Do not use diametral lenses to determine whether subsegments are\n");
+  printf(
+"        encroached; use diametral circles instead (as in Ruppert's\n");
+  printf(
+"        algorithm).  Use this switch if you want all triangles in the mesh\n"
+);
+  printf(
+"        to be Delaunay, and not just constrained Delaunay; or if you want\n");
+  printf(
+"        to ensure that all Voronoi vertices lie within the triangulation.\n");
+  printf(
+"        (Applications such as some finite volume methods may have this\n");
+  printf(
+"        requirement.)  This switch may increase the number of vertices in\n");
+  printf("        the mesh to meet these constraints.\n");
+  printf(
+"    -i  Uses an incremental rather than divide-and-conquer algorithm to\n");
+  printf(
+"        form a Delaunay triangulation.  Try it if the divide-and-conquer\n");
+  printf("        algorithm fails.\n");
+  printf(
+"    -F  Uses Steven Fortune's sweepline algorithm to form a Delaunay\n");
+  printf(
+"        triangulation.  Warning:  does not use exact arithmetic for all\n");
+  printf("        calculations.  An exact result is not guaranteed.\n");
+  printf(
+"    -l  Uses only vertical cuts in the divide-and-conquer algorithm.  By\n");
+  printf(
+"        default, Triangle uses alternating vertical and horizontal cuts,\n");
+  printf(
+"        which usually improve the speed except with vertex sets that are\n");
+  printf(
+"        small or short and wide.  This switch is primarily of theoretical\n");
+  printf("        interest.\n");
+  printf(
+"    -s  Specifies that segments should be forced into the triangulation by\n"
+);
+  printf(
+"        recursively splitting them at their midpoints, rather than by\n");
+  printf(
+"        generating a constrained Delaunay triangulation.  Segment splitting\n"
+);
+  printf(
+"        is true to Ruppert's original algorithm, but can create needlessly\n"
+);
+  printf(
+"        small triangles.  This switch is primarily of theoretical interest.\n"
+);
+  printf(
+"    -C  Check the consistency of the final mesh.  Uses exact arithmetic for\n"
+);
+  printf(
+"        checking, even if the -X switch is used.  Useful if you suspect\n");
+  printf("        Triangle is buggy.\n");
+  printf(
+"    -Q  Quiet:  Suppresses all explanation of what Triangle is doing,\n");
+  printf("        unless an error occurs.\n");
+  printf(
+"    -V  Verbose:  Gives detailed information about what Triangle is doing.\n"
+);
+  printf(
+"        Add more `V's for increasing amount of detail.  `-V' gives\n");
+  printf(
+"        information on algorithmic progress and more detailed statistics.\n");
+  printf(
+"        `-VV' gives vertex-by-vertex details, and prints so much that\n");
+  printf(
+"        Triangle runs much more slowly.  `-VVVV' gives information only\n");
+  printf("        a debugger could love.\n");
+  printf("    -h  Help:  Displays these instructions.\n");
+  printf("\n");
+  printf("Definitions:\n");
+  printf("\n");
+  printf(
+"  A Delaunay triangulation of a vertex set is a triangulation whose\n");
+  printf(
+"  vertices are the vertex set, wherein no vertex in the vertex set falls in\n"
+);
+  printf(
+"  the interior of the circumcircle (circle that passes through all three\n");
+  printf("  vertices) of any triangle in the triangulation.\n\n");
+  printf(
+"  A Voronoi diagram of a vertex set is a subdivision of the plane into\n");
+  printf(
+"  polygonal regions (some of which may be infinite), where each region is\n");
+  printf(
+"  the set of points in the plane that are closer to some input vertex than\n"
+);
+  printf(
+"  to any other input vertex.  (The Voronoi diagram is the geometric dual of\n"
+);
+  printf("  the Delaunay triangulation.)\n\n");
+  printf(
+"  A Planar Straight Line Graph (PSLG) is a set of vertices and segments.\n");
+  printf(
+"  Segments are simply edges, whose endpoints are vertices in the PSLG.\n");
+  printf(
+"  Segments may intersect each other only at their endpoints.  The file\n");
+  printf("  format for PSLGs (.poly files) is described below.\n\n");
+  printf(
+"  A constrained Delaunay triangulation (CDT) of a PSLG is similar to a\n");
+  printf(
+"  Delaunay triangulation, but each PSLG segment is present as a single edge\n"
+);
+  printf(
+"  in the triangulation.  (A constrained Delaunay triangulation is not truly\n"
+);
+  printf(
+"  a Delaunay triangulation.)  By definition, a CDT does not have any\n");
+  printf("  vertices other than those specified in the input PSLG.\n\n");
+  printf(
+"  A conforming Delaunay triangulation of a PSLG is a true Delaunay\n");
+  printf(
+"  triangulation in which each PSLG segment is represented by a linear\n");
+  printf(
+"  contiguous sequence of edges in the triangulation.  Each input segment\n");
+  printf(
+"  may have been subdivided into shorter subsegments by the insertion of\n");
+  printf(
+"  additional vertices.  These inserted vertices are necessary to maintain\n");
+  printf(
+"  the Delaunay property while ensuring that every segment is represented.\n");
+  printf("\n");
+  printf("File Formats:\n");
+  printf("\n");
+  printf(
+"  All files may contain comments prefixed by the character '#'.  Vertices,\n"
+);
+  printf(
+"  triangles, edges, holes, and maximum area constraints must be numbered\n");
+  printf(
+"  consecutively, starting from either 1 or 0.  Whichever you choose, all\n");
+  printf(
+"  input files must be consistent; if the vertices are numbered from 1, so\n");
+  printf(
+"  must be all other objects.  Triangle automatically detects your choice\n");
+  printf(
+"  while reading the .node (or .poly) file.  (When calling Triangle from\n");
+  printf(
+"  another program, use the -z switch if you wish to number objects from\n");
+  printf("  zero.)  Examples of these file formats are given below.\n\n");
+  printf("  .node files:\n");
+  printf(
+"    First line:  <# of vertices> <dimension (must be 2)> <# of attributes>\n"
+);
+  printf(
+"                                           <# of boundary markers (0 or 1)>\n"
+);
+  printf(
+"    Remaining lines:  <vertex #> <x> <y> [attributes] [boundary marker]\n");
+  printf("\n");
+  printf(
+"    The attributes, which are typically floating-point values of physical\n");
+  printf(
+"    quantities (such as mass or conductivity) associated with the nodes of\n"
+);
+  printf(
+"    a finite element mesh, are copied unchanged to the output mesh.  If -q,\n"
+);
+  printf(
+"    -a, -u, or -s is selected, each new Steiner point added to the mesh\n");
+  printf("    has attributes assigned to it by linear interpolation.\n\n");
+  printf(
+"    If the fourth entry of the first line is `1', the last column of the\n");
+  printf(
+"    remainder of the file is assumed to contain boundary markers.  Boundary\n"
+);
+  printf(
+"    markers are used to identify boundary vertices and vertices resting on\n"
+);
+  printf(
+"    PSLG segments; a complete description appears in a section below.  The\n"
+);
+  printf(
+"    .node file produced by Triangle contains boundary markers in the last\n");
+  printf("    column unless they are suppressed by the -B switch.\n\n");
+  printf("  .ele files:\n");
+  printf(
+"    First line:  <# of triangles> <nodes per triangle> <# of attributes>\n");
+  printf(
+"    Remaining lines:  <triangle #> <node> <node> <node> ... [attributes]\n");
+  printf("\n");
+  printf(
+"    Nodes are indices into the corresponding .node file.  The first three\n");
+  printf(
+"    nodes are the corner vertices, and are listed in counterclockwise order\n"
+);
+  printf(
+"    around each triangle.  (The remaining nodes, if any, depend on the type\n"
+);
+  printf("    of finite element used.)\n\n");
+  printf(
+"    The attributes are just like those of .node files.  Because there is no\n"
+);
+  printf(
+"    simple mapping from input to output triangles, an attempt is made to\n");
+  printf(
+"    interpolate attributes, which may result in a good deal of diffusion of\n"
+);
+  printf(
+"    attributes among nearby triangles as the triangulation is refined.\n");
+  printf(
+"    Attributes do not diffuse across segments, so attributes used to\n");
+  printf("    identify segment-bounded regions remain intact.\n\n");
+  printf(
+"    In .ele files produced by Triangle, each triangular element has three\n");
+  printf(
+"    nodes (vertices) unless the -o2 switch is used, in which case\n");
+  printf(
+"    subparametric quadratic elements with six nodes each are generated.\n");
+  printf(
+"    The first three nodes are the corners in counterclockwise order, and\n");
+  printf(
+"    the fourth, fifth, and sixth nodes lie on the midpoints of the edges\n");
+  printf(
+"    opposite the first, second, and third vertices, respectively.\n");
+  printf("\n");
+  printf("  .poly files:\n");
+  printf(
+"    First line:  <# of vertices> <dimension (must be 2)> <# of attributes>\n"
+);
+  printf(
+"                                           <# of boundary markers (0 or 1)>\n"
+);
+  printf(
+"    Following lines:  <vertex #> <x> <y> [attributes] [boundary marker]\n");
+  printf("    One line:  <# of segments> <# of boundary markers (0 or 1)>\n");
+  printf(
+"    Following lines:  <segment #> <endpoint> <endpoint> [boundary marker]\n");
+  printf("    One line:  <# of holes>\n");
+  printf("    Following lines:  <hole #> <x> <y>\n");
+  printf(
+"    Optional line:  <# of regional attributes and/or area constraints>\n");
+  printf(
+"    Optional following lines:  <region #> <x> <y> <attribute> <max area>\n");
+  printf("\n");
+  printf(
+"    A .poly file represents a PSLG, as well as some additional information.\n"
+);
+  printf(
+"    The first section lists all the vertices, and is identical to the\n");
+  printf(
+"    format of .node files.  <# of vertices> may be set to zero to indicate\n"
+);
+  printf(
+"    that the vertices are listed in a separate .node file; .poly files\n");
+  printf(
+"    produced by Triangle always have this format.  A vertex set represented\n"
+);
+  printf(
+"    this way has the advantage that it may easily be triangulated with or\n");
+  printf(
+"    without segments (depending on whether the .poly or .node file is\n");
+  printf("    read).\n\n");
+  printf(
+"    The second section lists the segments.  Segments are edges whose\n");
+  printf(
+"    presence in the triangulation is enforced (although each segment may be\n"
+);
+  printf(
+"    subdivided into smaller edges).  Each segment is specified by listing\n");
+  printf(
+"    the indices of its two endpoints.  This means that you must include its\n"
+);
+  printf(
+"    endpoints in the vertex list.  Each segment, like each point, may have\n"
+);
+  printf("    a boundary marker.\n\n");
+  printf(
+"    If -q, -a, -u, and -s are not selected, Triangle produces a constrained\n"
+);
+  printf(
+"    Delaunay triangulation (CDT), in which each segment appears as a single\n"
+);
+  printf(
+"    edge in the triangulation.  If -q, -a, -u, or -s is selected, Triangle\n"
+);
+  printf(
+"    produces a conforming constrained Delaunay triangulation (CCDT), in\n");
+  printf(
+"    which segments may be subdivided into smaller edges.  If -L is selected\n"
+);
+  printf(
+"    as well, Triangle produces a conforming Delaunay triangulation, so\n");
+  printf(
+"    every triangle is Delaunay, and not just constrained Delaunay.\n");
+  printf("\n");
+  printf(
+"    The third section lists holes (and concavities, if -c is selected) in\n");
+  printf(
+"    the triangulation.  Holes are specified by identifying a point inside\n");
+  printf(
+"    each hole.  After the triangulation is formed, Triangle creates holes\n");
+  printf(
+"    by eating triangles, spreading out from each hole point until its\n");
+  printf(
+"    progress is blocked by PSLG segments; you must be careful to enclose\n");
+  printf(
+"    each hole in segments, or your whole triangulation might be eaten away.\n"
+);
+  printf(
+"    If the two triangles abutting a segment are eaten, the segment itself\n");
+  printf(
+"    is also eaten.  Do not place a hole directly on a segment; if you do,\n");
+  printf("    Triangle chooses one side of the segment arbitrarily.\n\n");
+  printf(
+"    The optional fourth section lists regional attributes (to be assigned\n");
+  printf(
+"    to all triangles in a region) and regional constraints on the maximum\n");
+  printf(
+"    triangle area.  Triangle reads this section only if the -A switch is\n");
+  printf(
+"    used or the -a switch is used without a number following it, and the -r\n"
+);
+  printf(
+"    switch is not used.  Regional attributes and area constraints are\n");
+  printf(
+"    propagated in the same manner as holes; you specify a point for each\n");
+  printf(
+"    attribute and/or constraint, and the attribute and/or constraint\n");
+  printf(
+"    affects the whole region (bounded by segments) containing the point.\n");
+  printf(
+"    If two values are written on a line after the x and y coordinate, the\n");
+  printf(
+"    first such value is assumed to be a regional attribute (but is only\n");
+  printf(
+"    applied if the -A switch is selected), and the second value is assumed\n"
+);
+  printf(
+"    to be a regional area constraint (but is only applied if the -a switch\n"
+);
+  printf(
+"    is selected).  You may specify just one value after the coordinates,\n");
+  printf(
+"    which can serve as both an attribute and an area constraint, depending\n"
+);
+  printf(
+"    on the choice of switches.  If you are using the -A and -a switches\n");
+  printf(
+"    simultaneously and wish to assign an attribute to some region without\n");
+  printf("    imposing an area constraint, use a negative maximum area.\n\n");
+  printf(
+"    When a triangulation is created from a .poly file, you must either\n");
+  printf(
+"    enclose the entire region to be triangulated in PSLG segments, or\n");
+  printf(
+"    use the -c switch, which encloses the convex hull of the input vertex\n");
+  printf(
+"    set.  If you do not use the -c switch, Triangle eats all triangles that\n"
+);
+  printf(
+"    are not enclosed by segments; if you are not careful, your whole\n");
+  printf(
+"    triangulation may be eaten away.  If you do use the -c switch, you can\n"
+);
+  printf(
+"    still produce concavities by the appropriate placement of holes just\n");
+  printf("    within the convex hull.\n\n");
+  printf(
+"    An ideal PSLG has no intersecting segments, nor any vertices that lie\n");
+  printf(
+"    upon segments (except, of course, the endpoints of each segment.)  You\n"
+);
+  printf(
+"    aren't required to make your .poly files ideal, but you should be aware\n"
+);
+  printf(
+"    of what can go wrong.  Segment intersections are relatively safe--\n");
+  printf(
+"    Triangle calculates the intersection points for you and adds them to\n");
+  printf(
+"    the triangulation--as long as your machine's floating-point precision\n");
+  printf(
+"    doesn't become a problem.  You are tempting the fates if you have three\n"
+);
+  printf(
+"    segments that cross at the same location, and expect Triangle to figure\n"
+);
+  printf(
+"    out where the intersection point is.  Thanks to floating-point roundoff\n"
+);
+  printf(
+"    error, Triangle will probably decide that the three segments intersect\n"
+);
+  printf(
+"    at three different points, and you will find a minuscule triangle in\n");
+  printf(
+"    your output--unless Triangle tries to refine the tiny triangle, uses\n");
+  printf(
+"    up the last bit of machine precision, and fails to terminate at all.\n");
+  printf(
+"    You're better off putting the intersection point in the input files,\n");
+  printf(
+"    and manually breaking up each segment into two.  Similarly, if you\n");
+  printf(
+"    place a vertex at the middle of a segment, and hope that Triangle will\n"
+);
+  printf(
+"    break up the segment at that vertex, you might get lucky.  On the other\n"
+);
+  printf(
+"    hand, Triangle might decide that the vertex doesn't lie precisely on\n");
+  printf(
+"    the segment, and you'll have a needle-sharp triangle in your output--or\n"
+);
+  printf("    a lot of tiny triangles if you're generating a quality mesh.\n");
+  printf("\n");
+  printf(
+"    When Triangle reads a .poly file, it also writes a .poly file, which\n");
+  printf(
+"    includes all edges that are parts of input segments.  If the -c switch\n"
+);
+  printf(
+"    is used, the output .poly file also includes all of the edges on the\n");
+  printf(
+"    convex hull.  Hence, the output .poly file is useful for finding edges\n"
+);
+  printf(
+"    associated with input segments and for setting boundary conditions in\n");
+  printf(
+"    finite element simulations.  Moreover, you will need it if you plan to\n"
+);
+  printf(
+"    refine the output mesh, and don't want segments to be missing in later\n"
+);
+  printf("    triangulations.\n\n");
+  printf("  .area files:\n");
+  printf("    First line:  <# of triangles>\n");
+  printf("    Following lines:  <triangle #> <maximum area>\n\n");
+  printf(
+"    An .area file associates with each triangle a maximum area that is used\n"
+);
+  printf(
+"    for mesh refinement.  As with other file formats, every triangle must\n");
+  printf(
+"    be represented, and they must be numbered consecutively.  A triangle\n");
+  printf(
+"    may be left unconstrained by assigning it a negative maximum area.\n");
+  printf("\n");
+  printf("  .edge files:\n");
+  printf("    First line:  <# of edges> <# of boundary markers (0 or 1)>\n");
+  printf(
+"    Following lines:  <edge #> <endpoint> <endpoint> [boundary marker]\n");
+  printf("\n");
+  printf(
+"    Endpoints are indices into the corresponding .node file.  Triangle can\n"
+);
+  printf(
+"    produce .edge files (use the -e switch), but cannot read them.  The\n");
+  printf(
+"    optional column of boundary markers is suppressed by the -B switch.\n");
+  printf("\n");
+  printf(
+"    In Voronoi diagrams, one also finds a special kind of edge that is an\n");
+  printf(
+"    infinite ray with only one endpoint.  For these edges, a different\n");
+  printf("    format is used:\n\n");
+  printf("        <edge #> <endpoint> -1 <direction x> <direction y>\n\n");
+  printf(
+"    The `direction' is a floating-point vector that indicates the direction\n"
+);
+  printf("    of the infinite ray.\n\n");
+  printf("  .neigh files:\n");
+  printf(
+"    First line:  <# of triangles> <# of neighbors per triangle (always 3)>\n"
+);
+  printf(
+"    Following lines:  <triangle #> <neighbor> <neighbor> <neighbor>\n");
+  printf("\n");
+  printf(
+"    Neighbors are indices into the corresponding .ele file.  An index of -1\n"
+);
+  printf(
+"    indicates no neighbor (because the triangle is on an exterior\n");
+  printf(
+"    boundary).  The first neighbor of triangle i is opposite the first\n");
+  printf("    corner of triangle i, and so on.\n\n");
+  printf(
+"    Triangle can produce .neigh files (use the -n switch), but cannot read\n"
+);
+  printf("    them.\n\n");
+  printf("Boundary Markers:\n\n");
+  printf(
+"  Boundary markers are tags used mainly to identify which output vertices\n");
+  printf(
+"  and edges are associated with which PSLG segment, and to identify which\n");
+  printf(
+"  vertices and edges occur on a boundary of the triangulation.  A common\n");
+  printf(
+"  use is to determine where boundary conditions should be applied to a\n");
+  printf(
+"  finite element mesh.  You can prevent boundary markers from being written\n"
+);
+  printf("  into files produced by Triangle by using the -B switch.\n\n");
+  printf(
+"  The boundary marker associated with each segment in an output .poly file\n"
+);
+  printf("  and each edge in an output .edge file is chosen as follows:\n");
+  printf(
+"    - If an output edge is part or all of a PSLG segment with a nonzero\n");
+  printf(
+"      boundary marker, then the edge is assigned the same marker.\n");
+  printf(
+"    - Otherwise, if the edge occurs on a boundary of the triangulation\n");
+  printf(
+"      (including boundaries of holes), then the edge is assigned the marker\n"
+);
+  printf("      one (1).\n");
+  printf("    - Otherwise, the edge is assigned the marker zero (0).\n");
+  printf(
+"  The boundary marker associated with each vertex in an output .node file\n");
+  printf("  is chosen as follows:\n");
+  printf(
+"    - If a vertex is assigned a nonzero boundary marker in the input file,\n"
+);
+  printf(
+"      then it is assigned the same marker in the output .node file.\n");
+  printf(
+"    - Otherwise, if the vertex lies on a PSLG segment (including the\n");
+  printf(
+"      segment's endpoints) with a nonzero boundary marker, then the vertex\n"
+);
+  printf(
+"      is assigned the same marker.  If the vertex lies on several such\n");
+  printf("      segments, one of the markers is chosen arbitrarily.\n");
+  printf(
+"    - Otherwise, if the vertex occurs on a boundary of the triangulation,\n");
+  printf("      then the vertex is assigned the marker one (1).\n");
+  printf("    - Otherwise, the vertex is assigned the marker zero (0).\n");
+  printf("\n");
+  printf(
+"  If you want Triangle to determine for you which vertices and edges are on\n"
+);
+  printf(
+"  the boundary, assign them the boundary marker zero (or use no markers at\n"
+);
+  printf(
+"  all) in your input files.  In the output files, all boundary vertices,\n");
+  printf("  edges, and segments are assigned the value one.\n\n");
+  printf("Triangulation Iteration Numbers:\n\n");
+  printf(
+"  Because Triangle can read and refine its own triangulations, input\n");
+  printf(
+"  and output files have iteration numbers.  For instance, Triangle might\n");
+  printf(
+"  read the files mesh.3.node, mesh.3.ele, and mesh.3.poly, refine the\n");
+  printf(
+"  triangulation, and output the files mesh.4.node, mesh.4.ele, and\n");
+  printf("  mesh.4.poly.  Files with no iteration number are treated as if\n");
+  printf(
+"  their iteration number is zero; hence, Triangle might read the file\n");
+  printf(
+"  points.node, triangulate it, and produce the files points.1.node and\n");
+  printf("  points.1.ele.\n\n");
+  printf(
+"  Iteration numbers allow you to create a sequence of successively finer\n");
+  printf(
+"  meshes suitable for multigrid methods.  They also allow you to produce a\n"
+);
+  printf(
+"  sequence of meshes using error estimate-driven mesh refinement.\n");
+  printf("\n");
+  printf(
+"  If you're not using refinement or quality meshing, and you don't like\n");
+  printf(
+"  iteration numbers, use the -I switch to disable them.  This switch also\n");
+  printf(
+"  disables output of .node and .poly files to prevent your input files from\n"
+);
+  printf(
+"  being overwritten.  (If the input is a .poly file that contains its own\n");
+  printf("  points, a .node file is written.)\n\n");
+  printf("Examples of How to Use Triangle:\n\n");
+  printf(
+"  `triangle dots' reads vertices from dots.node, and writes their Delaunay\n"
+);
+  printf(
+"  triangulation to dots.1.node and dots.1.ele.  (dots.1.node is identical\n");
+  printf(
+"  to dots.node.)  `triangle -I dots' writes the triangulation to dots.ele\n");
+  printf(
+"  instead.  (No additional .node file is needed, so none is written.)\n");
+  printf("\n");
+  printf(
+"  `triangle -pe object.1' reads a PSLG from object.1.poly (and possibly\n");
+  printf(
+"  object.1.node, if the vertices are omitted from object.1.poly) and writes\n"
+);
+  printf(
+"  its constrained Delaunay triangulation to object.2.node and object.2.ele.\n"
+);
+  printf(
+"  The segments are copied to object.2.poly, and all edges are written to\n");
+  printf("  object.2.edge.\n\n");
+  printf(
+"  `triangle -pq31.5a.1 object' reads a PSLG from object.poly (and possibly\n"
+);
+  printf(
+"  object.node), generates a mesh whose angles are all 31.5 degrees or\n");
+  printf(
+"  greater and whose triangles all have areas of 0.1 or less, and writes the\n"
+);
+  printf(
+"  mesh to object.1.node and object.1.ele.  Each segment may be broken up\n");
+  printf("  into multiple subsegments; these are written to object.1.poly.\n");
+  printf("\n");
+  printf(
+"  Here is a sample file `box.poly' describing a square with a square hole:\n"
+);
+  printf("\n");
+  printf(
+"    # A box with eight vertices in 2D, no attributes, one boundary marker.\n"
+);
+  printf("    8 2 0 1\n");
+  printf("     # Outer box has these vertices:\n");
+  printf("     1   0 0   0\n");
+  printf("     2   0 3   0\n");
+  printf("     3   3 0   0\n");
+  printf("     4   3 3   33     # A special marker for this vertex.\n");
+  printf("     # Inner square has these vertices:\n");
+  printf("     5   1 1   0\n");
+  printf("     6   1 2   0\n");
+  printf("     7   2 1   0\n");
+  printf("     8   2 2   0\n");
+  printf("    # Five segments with boundary markers.\n");
+  printf("    5 1\n");
+  printf("     1   1 2   5      # Left side of outer box.\n");
+  printf("     # Square hole has these segments:\n");
+  printf("     2   5 7   0\n");
+  printf("     3   7 8   0\n");
+  printf("     4   8 6   10\n");
+  printf("     5   6 5   0\n");
+  printf("    # One hole in the middle of the inner square.\n");
+  printf("    1\n");
+  printf("     1   1.5 1.5\n");
+  printf("\n");
+  printf(
+"  Note that some segments are missing from the outer square, so one must\n");
+  printf(
+"  use the `-c' switch.  After `triangle -pqc box.poly', here is the output\n"
+);
+  printf(
+"  file `box.1.node', with twelve vertices.  The last four vertices were\n");
+  printf(
+"  added to meet the angle constraint.  Vertices 1, 2, and 9 have markers\n");
+  printf(
+"  from segment 1.  Vertices 6 and 8 have markers from segment 4.  All the\n");
+  printf(
+"  other vertices but 4 have been marked to indicate that they lie on a\n");
+  printf("  boundary.\n\n");
+  printf("    12  2  0  1\n");
+  printf("       1    0   0      5\n");
+  printf("       2    0   3      5\n");
+  printf("       3    3   0      1\n");
+  printf("       4    3   3     33\n");
+  printf("       5    1   1      1\n");
+  printf("       6    1   2     10\n");
+  printf("       7    2   1      1\n");
+  printf("       8    2   2     10\n");
+  printf("       9    0   1.5    5\n");
+  printf("      10    1.5   0    1\n");
+  printf("      11    3   1.5    1\n");
+  printf("      12    1.5   3    1\n");
+  printf("    # Generated by triangle -pqc box.poly\n");
+  printf("\n");
+  printf("  Here is the output file `box.1.ele', with twelve triangles.\n");
+  printf("\n");
+  printf("    12  3  0\n");
+  printf("       1     5   6   9\n");
+  printf("       2    10   3   7\n");
+  printf("       3     6   8  12\n");
+  printf("       4     9   1   5\n");
+  printf("       5     6   2   9\n");
+  printf("       6     7   3  11\n");
+  printf("       7    11   4   8\n");
+  printf("       8     7   5  10\n");
+  printf("       9    12   2   6\n");
+  printf("      10     8   7  11\n");
+  printf("      11     5   1  10\n");
+  printf("      12     8   4  12\n");
+  printf("    # Generated by triangle -pqc box.poly\n\n");
+  printf(
+"  Here is the output file `box.1.poly'.  Note that segments have been added\n"
+);
+  printf(
+"  to represent the convex hull, and some segments have been split by newly\n"
+);
+  printf(
+"  added vertices.  Note also that <# of vertices> is set to zero to\n");
+  printf("  indicate that the vertices should be read from the .node file.\n");
+  printf("\n");
+  printf("    0  2  0  1\n");
+  printf("    12  1\n");
+  printf("       1     1   9     5\n");
+  printf("       2     5   7     1\n");
+  printf("       3     8   7     1\n");
+  printf("       4     6   8    10\n");
+  printf("       5     5   6     1\n");
+  printf("       6     3  10     1\n");
+  printf("       7     4  11     1\n");
+  printf("       8     2  12     1\n");
+  printf("       9     9   2     5\n");
+  printf("      10    10   1     1\n");
+  printf("      11    11   3     1\n");
+  printf("      12    12   4     1\n");
+  printf("    1\n");
+  printf("       1   1.5 1.5\n");
+  printf("    # Generated by triangle -pqc box.poly\n");
+  printf("\n");
+  printf("Refinement and Area Constraints:\n");
+  printf("\n");
+  printf(
+"  The -r switch causes a mesh (.node and .ele files) to be read and\n");
+  printf(
+"  refined.  If the -p switch is also used, a .poly file is read and used to\n"
+);
+  printf(
+"  specify edges that are constrained and cannot be eliminated (although\n");
+  printf(
+"  they can be divided into smaller edges) by the refinement process.\n");
+  printf("\n");
+  printf(
+"  When you refine a mesh, you generally want to impose tighter quality\n");
+  printf(
+"  constraints.  One way to accomplish this is to use -q with a larger\n");
+  printf(
+"  angle, or -a followed by a smaller area than you used to generate the\n");
+  printf(
+"  mesh you are refining.  Another way to do this is to create an .area\n");
+  printf(
+"  file, which specifies a maximum area for each triangle, and use the -a\n");
+  printf(
+"  switch (without a number following).  Each triangle's area constraint is\n"
+);
+  printf(
+"  applied to that triangle.  Area constraints tend to diffuse as the mesh\n");
+  printf(
+"  is refined, so if there are large variations in area constraint between\n");
+  printf("  adjacent triangles, you may not get the results you want.\n\n");
+  printf(
+"  If you are refining a mesh composed of linear (three-node) elements, the\n"
+);
+  printf(
+"  output mesh contains all the nodes present in the input mesh, in the same\n"
+);
+  printf(
+"  order, with new nodes added at the end of the .node file.  However, the\n");
+  printf(
+"  refinement is not hierarchical: there is no guarantee that each output\n");
+  printf(
+"  element is contained in a single input element.  Often, output elements\n");
+  printf(
+"  overlap two input elements, and some input edges are not present in the\n");
+  printf(
+"  output mesh.  Hence, a sequence of refined meshes forms a hierarchy of\n");
+  printf(
+"  nodes, but not a hierarchy of elements.  If you refine a mesh of higher-\n"
+);
+  printf(
+"  order elements, the hierarchical property applies only to the nodes at\n");
+  printf(
+"  the corners of an element; other nodes may not be present in the refined\n"
+);
+  printf("  mesh.\n\n");
+  printf(
+"  Maximum area constraints in .poly files operate differently from those in\n"
+);
+  printf(
+"  .area files.  A maximum area in a .poly file applies to the whole\n");
+  printf(
+"  (segment-bounded) region in which a point falls, whereas a maximum area\n");
+  printf(
+"  in an .area file applies to only one triangle.  Area constraints in .poly\n"
+);
+  printf(
+"  files are used only when a mesh is first generated, whereas area\n");
+  printf(
+"  constraints in .area files are used only to refine an existing mesh, and\n"
+);
+  printf(
+"  are typically based on a posteriori error estimates resulting from a\n");
+  printf("  finite element simulation on that mesh.\n\n");
+  printf(
+"  `triangle -rq25 object.1' reads object.1.node and object.1.ele, then\n");
+  printf(
+"  refines the triangulation to enforce a 25 degree minimum angle, and then\n"
+);
+  printf(
+"  writes the refined triangulation to object.2.node and object.2.ele.\n");
+  printf("\n");
+  printf(
+"  `triangle -rpaa6.2 z.3' reads z.3.node, z.3.ele, z.3.poly, and z.3.area.\n"
+);
+  printf(
+"  After reconstructing the mesh and its subsegments, Triangle refines the\n");
+  printf(
+"  mesh so that no triangle has area greater than 6.2, and furthermore the\n");
+  printf(
+"  triangles satisfy the maximum area constraints in z.3.area.  No angle\n");
+  printf(
+"  bound is imposed at all.  The output is written to z.4.node, z.4.ele, and\n"
+);
+  printf("  z.4.poly.\n\n");
+  printf(
+"  The sequence `triangle -qa1 x', `triangle -rqa.3 x.1', `triangle -rqa.1\n");
+  printf(
+"  x.2' creates a sequence of successively finer meshes x.1, x.2, and x.3,\n");
+  printf("  suitable for multigrid.\n\n");
+  printf("Convex Hulls and Mesh Boundaries:\n\n");
+  printf(
+"  If the input is a vertex set (rather than a PSLG), Triangle produces its\n"
+);
+  printf(
+"  convex hull as a by-product in the output .poly file if you use the -c\n");
+  printf(
+"  switch.  There are faster algorithms for finding a two-dimensional convex\n"
+);
+  printf(
+"  hull than triangulation, of course, but this one comes for free.\n");
+  printf("\n");
+  printf(
+"  If the input is an unconstrained mesh (you are using the -r switch but\n");
+  printf(
+"  not the -p switch), Triangle produces a list of its boundary edges\n");
+  printf(
+"  (including hole boundaries) as a by-product when you use the -c switch.\n");
+  printf(
+"  If you also use the -p switch, the output .poly file contains all the\n");
+  printf("  segments from the input .poly file as well.\n\n");
+  printf("Voronoi Diagrams:\n\n");
+  printf(
+"  The -v switch produces a Voronoi diagram, in files suffixed .v.node and\n");
+  printf(
+"  .v.edge.  For example, `triangle -v points' reads points.node, produces\n");
+  printf(
+"  its Delaunay triangulation in points.1.node and points.1.ele, and\n");
+  printf(
+"  produces its Voronoi diagram in points.1.v.node and points.1.v.edge.  The\n"
+);
+  printf(
+"  .v.node file contains a list of all Voronoi vertices, and the .v.edge\n");
+  printf(
+"  file contains a list of all Voronoi edges, some of which may be infinite\n"
+);
+  printf(
+"  rays.  (The choice of filenames makes it easy to run the set of Voronoi\n");
+  printf("  vertices through Triangle, if so desired.)\n\n");
+  printf(
+"  This implementation does not use exact arithmetic to compute the Voronoi\n"
+);
+  printf(
+"  vertices, and does not check whether neighboring vertices are identical.\n"
+);
+  printf(
+"  Be forewarned that if the Delaunay triangulation is degenerate or\n");
+  printf(
+"  near-degenerate, the Voronoi diagram may have duplicate vertices,\n");
+  printf(
+"  crossing edges, or infinite rays whose direction vector is zero.\n");
+  printf("\n");
+  printf(
+"  The result is a valid Voronoi diagram only if Triangle's output is a true\n"
+);
+  printf(
+"  Delaunay triangulation.  The Voronoi output is usually meaningless (and\n");
+  printf(
+"  may contain crossing edges and other pathology) if the output is a CDT or\n"
+);
+  printf(
+"  CCDT, or if it has holes or concavities.  If the triangulation is convex\n"
+);
+  printf(
+"  and has no holes, this can be fixed by using the -L switch to ensure a\n");
+  printf("  conforming Delaunay triangulation is constructed.\n\n");
+  printf("Mesh Topology:\n\n");
+  printf(
+"  You may wish to know which triangles are adjacent to a certain Delaunay\n");
+  printf(
+"  edge in an .edge file, which Voronoi regions are adjacent to a certain\n");
+  printf(
+"  Voronoi edge in a .v.edge file, or which Voronoi regions are adjacent to\n"
+);
+  printf(
+"  each other.  All of this information can be found by cross-referencing\n");
+  printf(
+"  output files with the recollection that the Delaunay triangulation and\n");
+  printf("  the Voronoi diagram are planar duals.\n\n");
+  printf(
+"  Specifically, edge i of an .edge file is the dual of Voronoi edge i of\n");
+  printf(
+"  the corresponding .v.edge file, and is rotated 90 degrees counterclock-\n");
+  printf(
+"  wise from the Voronoi edge.  Triangle j of an .ele file is the dual of\n");
+  printf(
+"  vertex j of the corresponding .v.node file.  Voronoi region k is the dual\n"
+);
+  printf("  of vertex k of the corresponding .node file.\n\n");
+  printf(
+"  Hence, to find the triangles adjacent to a Delaunay edge, look at the\n");
+  printf(
+"  vertices of the corresponding Voronoi edge.  If the endpoints of a\n");
+  printf(
+"  Voronoi edge are Voronoi vertices 2 and 6 respectively, then triangles 2\n"
+);
+  printf(
+"  and 6 adjoin the left and right sides of the corresponding Delaunay edge,\n"
+);
+  printf(
+"  respectively.  To find the Voronoi regions adjacent to a Voronoi edge,\n");
+  printf(
+"  look at the endpoints of the corresponding Delaunay edge.  If the\n");
+  printf(
+"  endpoints of a Delaunay edge are input vertices 7 and 12, then Voronoi\n");
+  printf(
+"  regions 7 and 12 adjoin the right and left sides of the corresponding\n");
+  printf(
+"  Voronoi edge, respectively.  To find which Voronoi regions are adjacent\n");
+  printf("  to each other, just read the list of Delaunay edges.\n\n");
+  printf(
+"  Triangle does not write a list of Voronoi regions, but one can be\n");
+  printf(
+"  reconstructed straightforwardly.  For instance, to find all the edges of\n"
+);
+  printf(
+"  Voronoi region 1, search the output .edge file for every edge that has\n");
+  printf(
+"  input vertex 1 as an endpoint.  The corresponding dual edges in the\n");
+  printf("  output .v.edge file form the boundary of Voronoi region 1.\n\n");
+  printf("Quadratic Elements:\n\n");
+  printf(
+"  Triangle generates meshes with subparametric quadratic elements if the\n");
+  printf(
+"  -o2 switch is specified.  Quadratic elements have six nodes per element,\n"
+);
+  printf(
+"  rather than three.  `Subparametric' means that the edges of the triangles\n"
+);
+  printf(
+"  are always straight, so that subparametric quadratic elements are\n");
+  printf(
+"  geometrically identical to linear elements, even though they can be used\n"
+);
+  printf(
+"  with quadratic interpolating functions.  The three extra nodes of an\n");
+  printf(
+"  element fall at the midpoints of the three edges, with the fourth, fifth,\n"
+);
+  printf(
+"  and sixth nodes appearing opposite the first, second, and third corners\n");
+  printf("  respectively.\n\n");
+  printf("Statistics:\n\n");
+  printf(
+"  After generating a mesh, Triangle prints a count of the number of\n");
+  printf(
+"  vertices, triangles, edges, exterior boundary edges (including hole\n");
+  printf(
+"  boundaries), interior boundary edges, and segments in the output mesh.\n");
+  printf(
+"  If you've forgotten the statistics for an existing mesh, run Triangle on\n"
+);
+  printf(
+"  that mesh with the -rNEP switches to read the mesh and print the\n");
+  printf(
+"  statistics without writing any files.  Use -rpNEP if you've got a .poly\n");
+  printf("  file for the mesh.\n\n");
+  printf(
+"  The -V switch produces extended statistics, including a rough estimate\n");
+  printf(
+"  of memory use, the number of calls to geometric predicates, and\n");
+  printf("  histograms of triangle aspect ratios and angles in the mesh.\n\n");
+  printf("Exact Arithmetic:\n\n");
+  printf(
+"  Triangle uses adaptive exact arithmetic to perform what computational\n");
+  printf(
+"  geometers call the `orientation' and `incircle' tests.  If the floating-\n"
+);
+  printf(
+"  point arithmetic of your machine conforms to the IEEE 754 standard (as\n");
+  printf(
+"  most workstations do), and does not use extended precision internal\n");
+  printf(
+"  floating-point registers, then your output is guaranteed to be an\n");
+  printf(
+"  absolutely true Delaunay or constrained Delaunay triangulation, roundoff\n"
+);
+  printf(
+"  error notwithstanding.  The word `adaptive' implies that these arithmetic\n"
+);
+  printf(
+"  routines compute the result only to the precision necessary to guarantee\n"
+);
+  printf(
+"  correctness, so they are usually nearly as fast as their approximate\n");
+  printf("  counterparts.\n\n");
+  printf(
+"  Pentiums have extended precision floating-point registers.  These must be\n"
+);
+  printf(
+"  reconfigured so their precision is reduced to memory precision.  Triangle\n"
+);
+  printf(
+"  does this if it is compiled correctly.  See the makefile for details.\n");
+  printf("\n");
+  printf(
+"  The exact tests can be disabled with the -X switch.  On most inputs, this\n"
+);
+  printf(
+"  switch reduces the computation time by about eight percent--it's not\n");
+  printf(
+"  worth the risk.  There are rare difficult inputs (having many collinear\n");
+  printf(
+"  and cocircular vertices), however, for which the difference in speed\n");
+  printf(
+"  could be a factor of two.  Be forewarned that these are precisely the\n");
+  printf(
+"  inputs most likely to cause errors if you use the -X switch.  Hence, the\n"
+);
+  printf("  -X switch is not recommended.\n\n");
+  printf(
+"  Unfortunately, the exact tests don't solve every numerical problem.\n");
+  printf(
+"  Exact arithmetic is not used to compute the positions of new vertices,\n");
+  printf(
+"  because the bit complexity of vertex coordinates would grow without\n");
+  printf(
+"  bound.  Hence, segment intersections aren't computed exactly; in very\n");
+  printf(
+"  unusual cases, roundoff error in computing an intersection point might\n");
+  printf(
+"  actually lead to an inverted triangle and an invalid triangulation.\n");
+  printf(
+"  (This is one reason to compute your own intersection points in your .poly\n"
+);
+  printf(
+"  files.)  Similarly, exact arithmetic is not used to compute the vertices\n"
+);
+  printf("  of the Voronoi diagram.\n\n");
+  printf(
+"  Another pair of problems not solved by the exact arithmetic routines is\n");
+  printf(
+"  underflow and overflow.  If Triangle is compiled for double precision\n");
+  printf(
+"  arithmetic, I believe that Triangle's geometric predicates work correctly\n"
+);
+  printf(
+"  if the exponent of every input coordinate falls in the range [-148, 201].\n"
+);
+  printf(
+"  Underflow can silently prevent the orientation and incircle tests from\n");
+  printf(
+"  being performed exactly, while overflow typically causes a floating\n");
+  printf("  exception.\n\n");
+  printf("Calling Triangle from Another Program:\n\n");
+  printf("  Read the file triangle.h for details.\n\n");
+  printf("Troubleshooting:\n\n");
+  printf("  Please read this section before mailing me bugs.\n\n");
+  printf("  `My output mesh has no triangles!'\n\n");
+  printf(
+"    If you're using a PSLG, you've probably failed to specify a proper set\n"
+);
+  printf(
+"    of bounding segments, or forgotten to use the -c switch.  Or you may\n");
+  printf(
+"    have placed a hole badly, thereby eating all your triangles.  To test\n");
+  printf("    these possibilities, try again with the -c and -O switches.\n");
+  printf(
+"    Alternatively, all your input vertices may be collinear, in which case\n"
+);
+  printf("    you can hardly expect to triangulate them.\n\n");
+  printf("  `Triangle doesn't terminate, or just crashes.'\n\n");
+  printf(
+"    Bad things can happen when triangles get so small that the distance\n");
+  printf(
+"    between their vertices isn't much larger than the precision of your\n");
+  printf(
+"    machine's arithmetic.  If you've compiled Triangle for single-precision\n"
+);
+  printf(
+"    arithmetic, you might do better by recompiling it for double-precision.\n"
+);
+  printf(
+"    Then again, you might just have to settle for more lenient constraints\n"
+);
+  printf(
+"    on the minimum angle and the maximum area than you had planned.\n");
+  printf("\n");
+  printf(
+"    You can minimize precision problems by ensuring that the origin lies\n");
+  printf(
+"    inside your vertex set, or even inside the densest part of your\n");
+  printf(
+"    mesh.  If you're triangulating an object whose x coordinates all fall\n");
+  printf(
+"    between 6247133 and 6247134, you're not leaving much floating-point\n");
+  printf("    precision for Triangle to work with.\n\n");
+  printf(
+"    Precision problems can occur covertly if the input PSLG contains two\n");
+  printf(
+"    segments that meet (or intersect) at an extremely small angle, or if\n");
+  printf(
+"    such an angle is introduced by the -c switch.  If you don't realize\n");
+  printf(
+"    that a tiny angle is being formed, you might never discover why\n");
+  printf(
+"    Triangle is crashing.  To check for this possibility, use the -S switch\n"
+);
+  printf(
+"    (with an appropriate limit on the number of Steiner points, found by\n");
+  printf(
+"    trial-and-error) to stop Triangle early, and view the output .poly file\n"
+);
+  printf(
+"    with Show Me (described below).  Look carefully for regions where dense\n"
+);
+  printf(
+"    clusters of vertices are forming and for small angles between segments.\n"
+);
+  printf(
+"    Zoom in closely, as such segments might look like a single segment from\n"
+);
+  printf("    a distance.\n\n");
+  printf(
+"    If some of the input values are too large, Triangle may suffer a\n");
+  printf(
+"    floating exception due to overflow when attempting to perform an\n");
+  printf(
+"    orientation or incircle test.  (Read the section on exact arithmetic\n");
+  printf(
+"    above.)  Again, I recommend compiling Triangle for double (rather\n");
+  printf("    than single) precision arithmetic.\n\n");
+  printf(
+"    Unexpected problems can arise if you use quality meshing (-q, -a, or\n");
+  printf(
+"    -u) with an input that is not segment-bounded--that is, if your input\n");
+  printf(
+"    is a vertex set, or you're using the -c switch.  If the convex hull of\n"
+);
+  printf(
+"    your input vertices has collinear vertices on its boundary, an input\n");
+  printf(
+"    vertex that you think lies on the convex hull might actually lie just\n");
+  printf(
+"    inside the convex hull.  If so, an extremely thin triangle is formed by\n"
+);
+  printf(
+"    the vertex and the convex hull edge beside it.  When Triangle tries to\n"
+);
+  printf(
+"    refine the mesh to enforce angle and area constraints, extremely tiny\n");
+  printf(
+"    triangles may be formed, or Triangle may fail because of insufficient\n");
+  printf("    floating-point precision.\n\n");
+  printf(
+"  `The numbering of the output vertices doesn't match the input vertices.'\n"
+);
+  printf("\n");
+  printf(
+"    You may have had duplicate input vertices, or you may have eaten some\n");
+  printf(
+"    of your input vertices with a hole, or by placing them outside the area\n"
+);
+  printf(
+"    enclosed by segments.  In any case, you can solve the problem by not\n");
+  printf("    using the -j switch.\n\n");
+  printf(
+"  `Triangle executes without incident, but when I look at the resulting\n");
+  printf(
+"  mesh, it has overlapping triangles or other geometric inconsistencies.'\n");
+  printf("\n");
+  printf(
+"    If you select the -X switch, Triangle occasionally makes mistakes due\n");
+  printf(
+"    to floating-point roundoff error.  Although these errors are rare,\n");
+  printf(
+"    don't use the -X switch.  If you still have problems, please report the\n"
+);
+  printf("    bug.\n\n");
+  printf(
+"  Strange things can happen if you've taken liberties with your PSLG.  Do\n");
+  printf(
+"  you have a vertex lying in the middle of a segment?  Triangle sometimes\n");
+  printf(
+"  copes poorly with that sort of thing.  Do you want to lay out a collinear\n"
+);
+  printf(
+"  row of evenly spaced, segment-connected vertices?  Have you simply\n");
+  printf(
+"  defined one long segment connecting the leftmost vertex to the rightmost\n"
+);
+  printf(
+"  vertex, and a bunch of vertices lying along it?  This method occasionally\n"
+);
+  printf(
+"  works, especially with horizontal and vertical lines, but often it\n");
+  printf(
+"  doesn't, and you'll have to connect each adjacent pair of vertices with a\n"
+);
+  printf("  separate segment.  If you don't like it, tough.\n\n");
+  printf(
+"  Furthermore, if you have segments that intersect other than at their\n");
+  printf(
+"  endpoints, try not to let the intersections fall extremely close to PSLG\n"
+);
+  printf("  vertices or each other.\n\n");
+  printf(
+"  If you have problems refining a triangulation not produced by Triangle:\n");
+  printf(
+"  Are you sure the triangulation is geometrically valid?  Is it formatted\n");
+  printf(
+"  correctly for Triangle?  Are the triangles all listed so the first three\n"
+);
+  printf(
+"  vertices are their corners in counterclockwise order?  Are all of the\n");
+  printf(
+"  triangles constrained Delaunay?  Triangle's Delaunay refinement algorithm\n"
+);
+  printf("  assumes that it starts with a CDT.\n\n");
+  printf("Show Me:\n\n");
+  printf(
+"  Triangle comes with a separate program named `Show Me', whose primary\n");
+  printf(
+"  purpose is to draw meshes on your screen or in PostScript.  Its secondary\n"
+);
+  printf(
+"  purpose is to check the validity of your input files, and do so more\n");
+  printf(
+"  thoroughly than Triangle does.  Unlike Triangle, Show Me requires that\n");
+  printf("  you have the X Windows system.\n\n");
+  printf("Triangle on the Web:\n\n");
+  printf(
+"  To see an illustrated, updated version of these instructions, check out\n");
+  printf("\n");
+  printf("    http://www.cs.cmu.edu/~quake/triangle.html\n");
+  printf("\n");
+  printf("A Brief Plea:\n");
+  printf("\n");
+  printf(
+"  If you use Triangle, and especially if you use it to accomplish real\n");
+  printf(
+"  work, I would like very much to hear from you.  A short letter or email\n");
+  printf(
+"  (to jrs@cs.berkeley.edu) describing how you use Triangle will mean a lot\n"
+);
+  printf(
+"  to me.  The more people I know are using this program, the more easily I\n"
+);
+  printf(
+"  can justify spending time on improvements, which in turn will benefit\n");
+  printf(
+"  you.  Also, I can put you on a list to receive email whenever a new\n");
+  printf("  version of Triangle is available.\n\n");
+  printf(
+"  If you use a mesh generated by Triangle in a publication, please include\n"
+);
+  printf("  an acknowledgment as well.\n\n");
+  printf("Research credit:\n\n");
+  printf(
+"  Of course, I can take credit for only a fraction of the ideas that made\n");
+  printf(
+"  this mesh generator possible.  Triangle owes its existence to the efforts\n"
+);
+  printf(
+"  of many fine computational geometers and other researchers, including\n");
+  printf(
+"  Marshall Bern, L. Paul Chew, Boris Delaunay, Rex A. Dwyer, David\n");
+  printf(
+"  Eppstein, Steven Fortune, Leonidas J. Guibas, Donald E. Knuth, Charles L.\n"
+);
+  printf(
+"  Lawson, Der-Tsai Lee, Ernst P. Mucke, Douglas M. Priest, Jim Ruppert,\n");
+  printf(
+"  Isaac Saias, Bruce J. Schachter, Micha Sharir, Daniel D. Sleator, Jorge\n");
+  printf(
+"  Stolfi, Robert E. Tarjan, Alper Ungor, Christopher J. Van Wyk, and Binhai\n"
+);
+  printf("  Zhu.  See the comments at the beginning of the source code for\n");
+  printf("  references.\n\n");
+  exit(0);
+}
+
+#endif /* not TRILIBRARY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  internalerror()   Ask the user to send me the defective product.  Exit.  */
+/*                                                                           */
+/*****************************************************************************/
+
+void internalerror()
+{
+  printf("  Please report this bug to jrs@cs.berkeley.edu\n");
+  printf("  Include the message above, your input data set, and the exact\n");
+  printf("    command line you used to run Triangle.\n");
+  exit(1);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  parsecommandline()   Read the command line, identify switches, and set   */
+/*                       up options and file names.                          */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void parsecommandline(int argc, char **argv, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void parsecommandline(argc, argv, b)
+int argc;
+char **argv;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+#ifdef TRILIBRARY
+#define STARTINDEX 0
+#else /* not TRILIBRARY */
+#define STARTINDEX 1
+  int increment;
+  int meshnumber;
+#endif /* not TRILIBRARY */
+  int i, j, k;
+  char workstring[FILENAMESIZE];
+
+  b->poly = b->refine = b->quality = 0;
+  b->vararea = b->fixedarea = b->usertest = 0;
+  b->regionattrib = b->convex = b->weighted = b->jettison = 0;
+  b->firstnumber = 1;
+  b->edgesout = b->voronoi = b->neighbors = b->geomview = 0;
+  b->nobound = b->nopolywritten = b->nonodewritten = b->noelewritten = 0;
+  b->noiterationnum = 0;
+  b->noholes = b->noexact = 0;
+  b->incremental = b->sweepline = 0;
+  b->dwyer = 1;
+  b->splitseg = 0;
+  b->docheck = 0;
+  b->nobisect = 0;
+  b->nolenses = 0;
+  b->steiner = -1;
+  b->order = 1;
+  b->minangle = 0.0;
+  b->maxarea = -1.0;
+  b->quiet = b->verbose = 0;
+#ifndef TRILIBRARY
+  b->innodefilename[0] = '\0';
+#endif /* not TRILIBRARY */
+
+  for (i = STARTINDEX; i < argc; i++) {
+#ifndef TRILIBRARY
+    if (argv[i][0] == '-') {
+#endif /* not TRILIBRARY */
+      for (j = STARTINDEX; argv[i][j] != '\0'; j++) {
+        if (argv[i][j] == 'p') {
+          b->poly = 1;
+	}
+#ifndef CDT_ONLY
+        if (argv[i][j] == 'r') {
+          b->refine = 1;
+	}
+        if (argv[i][j] == 'q') {
+          b->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';
+            b->minangle = (REAL) strtod(workstring, (char **) NULL);
+	  } else {
+            b->minangle = 20.0;
+	  }
+	}
+        if (argv[i][j] == 'a') {
+          b->quality = 1;
+          if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+              (argv[i][j + 1] == '.')) {
+            b->fixedarea = 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';
+            b->maxarea = (REAL) strtod(workstring, (char **) NULL);
+            if (b->maxarea <= 0.0) {
+              printf("Error:  Maximum area must be greater than zero.\n");
+              exit(1);
+	    }
+	  } else {
+            b->vararea = 1;
+	  }
+	}
+        if (argv[i][j] == 'u') {
+          b->quality = 1;
+          b->usertest = 1;
+        }
+#endif /* not CDT_ONLY */
+        if (argv[i][j] == 'A') {
+          b->regionattrib = 1;
+        }
+        if (argv[i][j] == 'c') {
+          b->convex = 1;
+        }
+        if (argv[i][j] == 'w') {
+          b->weighted = 1;
+        }
+        if (argv[i][j] == 'W') {
+          b->weighted = 2;
+        }
+        if (argv[i][j] == 'j') {
+          b->jettison = 1;
+        }
+        if (argv[i][j] == 'z') {
+          b->firstnumber = 0;
+        }
+        if (argv[i][j] == 'e') {
+          b->edgesout = 1;
+	}
+        if (argv[i][j] == 'v') {
+          b->voronoi = 1;
+	}
+        if (argv[i][j] == 'n') {
+          b->neighbors = 1;
+	}
+        if (argv[i][j] == 'g') {
+          b->geomview = 1;
+	}
+        if (argv[i][j] == 'B') {
+          b->nobound = 1;
+	}
+        if (argv[i][j] == 'P') {
+          b->nopolywritten = 1;
+	}
+        if (argv[i][j] == 'N') {
+          b->nonodewritten = 1;
+	}
+        if (argv[i][j] == 'E') {
+          b->noelewritten = 1;
+	}
+#ifndef TRILIBRARY
+        if (argv[i][j] == 'I') {
+          b->noiterationnum = 1;
+	}
+#endif /* not TRILIBRARY */
+        if (argv[i][j] == 'O') {
+          b->noholes = 1;
+	}
+        if (argv[i][j] == 'X') {
+          b->noexact = 1;
+	}
+        if (argv[i][j] == 'o') {
+          if (argv[i][j + 1] == '2') {
+            j++;
+            b->order = 2;
+          }
+	}
+#ifndef CDT_ONLY
+        if (argv[i][j] == 'Y') {
+          b->nobisect++;
+	}
+        if (argv[i][j] == 'S') {
+          b->steiner = 0;
+          while ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) {
+            j++;
+            b->steiner = b->steiner * 10 + (int) (argv[i][j] - '0');
+          }
+        }
+#endif /* not CDT_ONLY */
+#ifndef REDUCED
+        if (argv[i][j] == 'i') {
+          b->incremental = 1;
+        }
+        if (argv[i][j] == 'F') {
+          b->sweepline = 1;
+        }
+#endif /* not REDUCED */
+        if (argv[i][j] == 'l') {
+          b->dwyer = 0;
+        }
+#ifndef REDUCED
+#ifndef CDT_ONLY
+        if (argv[i][j] == 's') {
+          b->splitseg = 1;
+        }
+        if (argv[i][j] == 'L') {
+          b->nolenses = 1;
+        }
+#endif /* not CDT_ONLY */
+        if (argv[i][j] == 'C') {
+          b->docheck = 1;
+        }
+#endif /* not REDUCED */
+        if (argv[i][j] == 'Q') {
+          b->quiet = 1;
+        }
+        if (argv[i][j] == 'V') {
+          b->verbose++;
+        }
+#ifndef TRILIBRARY
+        if ((argv[i][j] == 'h') || (argv[i][j] == 'H') ||
+            (argv[i][j] == '?')) {
+          info();
+	}
+#endif /* not TRILIBRARY */
+      }
+#ifndef TRILIBRARY
+    } else {
+      strncpy(b->innodefilename, argv[i], FILENAMESIZE - 1);
+      b->innodefilename[FILENAMESIZE - 1] = '\0';
+    }
+#endif /* not TRILIBRARY */
+  }
+#ifndef TRILIBRARY
+  if (b->innodefilename[0] == '\0') {
+    syntax();
+  }
+  if (!strcmp(&b->innodefilename[strlen(b->innodefilename) - 5], ".node")) {
+    b->innodefilename[strlen(b->innodefilename) - 5] = '\0';
+  }
+  if (!strcmp(&b->innodefilename[strlen(b->innodefilename) - 5], ".poly")) {
+    b->innodefilename[strlen(b->innodefilename) - 5] = '\0';
+    b->poly = 1;
+  }
+#ifndef CDT_ONLY
+  if (!strcmp(&b->innodefilename[strlen(b->innodefilename) - 4], ".ele")) {
+    b->innodefilename[strlen(b->innodefilename) - 4] = '\0';
+    b->refine = 1;
+  }
+  if (!strcmp(&b->innodefilename[strlen(b->innodefilename) - 5], ".area")) {
+    b->innodefilename[strlen(b->innodefilename) - 5] = '\0';
+    b->refine = 1;
+    b->quality = 1;
+    b->vararea = 1;
+  }
+#endif /* not CDT_ONLY */
+#endif /* not TRILIBRARY */
+  b->usesegments = b->poly || b->refine || b->quality || b->convex;
+  b->goodangle = cos(b->minangle * PI / 180.0);
+  if (b->goodangle == 1.0) {
+    b->offconstant = 0.0;
+  } else {
+    b->offconstant = 0.475 * sqrt((1.0 + b->goodangle) / (1.0 - b->goodangle));
+  }
+  b->goodangle *= b->goodangle;
+  if (b->refine && b->noiterationnum) {
+    printf(
+      "Error:  You cannot use the -I switch when refining a triangulation.\n");
+    exit(1);
+  }
+  /* Be careful not to allocate space for element area constraints that */
+  /*   will never be assigned any value (other than the default -1.0).  */
+  if (!b->refine && !b->poly) {
+    b->vararea = 0;
+  }
+  /* Be careful not to add an extra attribute to each element unless the */
+  /*   input supports it (PSLG in, but not refining a preexisting mesh). */
+  if (b->refine || !b->poly) {
+    b->regionattrib = 0;
+  }
+  /* Regular/weighted triangulations are incompatible with PSLGs */
+  /*   and meshing.                                              */
+  if (b->weighted && (b->poly || b->quality)) {
+    b->weighted = 0;
+    if (!b->quiet) {
+      printf("Warning:  weighted triangulations (-w, -W) are incompatible\n");
+      printf("  with PSLGs (-p) and meshing (-q, -a, -u).  Weights ignored.\n"
+             );
+    }
+  }
+  if (b->jettison && b->nonodewritten && !b->quiet) {
+    printf("Warning:  -j and -N switches are somewhat incompatible.\n");
+    printf("  If any vertices are jettisoned, you will need the output\n");
+    printf("  .node file to reconstruct the new node indices.");
+  }
+
+#ifndef TRILIBRARY
+  strcpy(b->inpolyfilename, b->innodefilename);
+  strcpy(b->inelefilename, b->innodefilename);
+  strcpy(b->areafilename, b->innodefilename);
+  increment = 0;
+  strcpy(workstring, b->innodefilename);
+  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 (b->noiterationnum) {
+    strcpy(b->outnodefilename, b->innodefilename);
+    strcpy(b->outelefilename, b->innodefilename);
+    strcpy(b->edgefilename, b->innodefilename);
+    strcpy(b->vnodefilename, b->innodefilename);
+    strcpy(b->vedgefilename, b->innodefilename);
+    strcpy(b->neighborfilename, b->innodefilename);
+    strcpy(b->offfilename, b->innodefilename);
+    strcat(b->outnodefilename, ".node");
+    strcat(b->outelefilename, ".ele");
+    strcat(b->edgefilename, ".edge");
+    strcat(b->vnodefilename, ".v.node");
+    strcat(b->vedgefilename, ".v.edge");
+    strcat(b->neighborfilename, ".neigh");
+    strcat(b->offfilename, ".off");
+  } else if (increment == 0) {
+    strcpy(b->outnodefilename, b->innodefilename);
+    strcpy(b->outpolyfilename, b->innodefilename);
+    strcpy(b->outelefilename, b->innodefilename);
+    strcpy(b->edgefilename, b->innodefilename);
+    strcpy(b->vnodefilename, b->innodefilename);
+    strcpy(b->vedgefilename, b->innodefilename);
+    strcpy(b->neighborfilename, b->innodefilename);
+    strcpy(b->offfilename, b->innodefilename);
+    strcat(b->outnodefilename, ".1.node");
+    strcat(b->outpolyfilename, ".1.poly");
+    strcat(b->outelefilename, ".1.ele");
+    strcat(b->edgefilename, ".1.edge");
+    strcat(b->vnodefilename, ".1.v.node");
+    strcat(b->vedgefilename, ".1.v.edge");
+    strcat(b->neighborfilename, ".1.neigh");
+    strcat(b->offfilename, ".1.off");
+  } else {
+    workstring[increment] = '%';
+    workstring[increment + 1] = 'd';
+    workstring[increment + 2] = '\0';
+    sprintf(b->outnodefilename, workstring, meshnumber + 1);
+    strcpy(b->outpolyfilename, b->outnodefilename);
+    strcpy(b->outelefilename, b->outnodefilename);
+    strcpy(b->edgefilename, b->outnodefilename);
+    strcpy(b->vnodefilename, b->outnodefilename);
+    strcpy(b->vedgefilename, b->outnodefilename);
+    strcpy(b->neighborfilename, b->outnodefilename);
+    strcpy(b->offfilename, b->outnodefilename);
+    strcat(b->outnodefilename, ".node");
+    strcat(b->outpolyfilename, ".poly");
+    strcat(b->outelefilename, ".ele");
+    strcat(b->edgefilename, ".edge");
+    strcat(b->vnodefilename, ".v.node");
+    strcat(b->vedgefilename, ".v.edge");
+    strcat(b->neighborfilename, ".neigh");
+    strcat(b->offfilename, ".off");
+  }
+  strcat(b->innodefilename, ".node");
+  strcat(b->inpolyfilename, ".poly");
+  strcat(b->inelefilename, ".ele");
+  strcat(b->areafilename, ".area");
+#endif /* not TRILIBRARY */
+}
+
+/**                                                                         **/
+/**                                                                         **/
+/********* User interaction routines begin here                      *********/
+
+/********* Debugging routines begin here                             *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  printtriangle()   Print out the details of an oriented triangle.         */
+/*                                                                           */
+/*  I originally wrote this procedure to simplify debugging; it can be       */
+/*  called directly from the debugger, and presents information about an     */
+/*  oriented triangle in digestible form.  It's also used when the           */
+/*  highest level of verbosity (`-VVV') is specified.                        */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void printtriangle(struct mesh *m, struct behavior *b, struct otri *t)
+#else /* not ANSI_DECLARATORS */
+void printtriangle(m, b, t)
+struct mesh *m;
+struct behavior *b;
+struct otri *t;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri printtri;
+  struct osub printsh;
+  vertex printvertex;
+
+  printf("triangle x%lx with orientation %d:\n", (unsigned long) t->tri,
+         t->orient);
+  decode(t->tri[0], printtri);
+  if (printtri.tri == m->dummytri) {
+    printf("    [0] = Outer space\n");
+  } else {
+    printf("    [0] = x%lx  %d\n", (unsigned long) printtri.tri,
+           printtri.orient);
+  }
+  decode(t->tri[1], printtri);
+  if (printtri.tri == m->dummytri) {
+    printf("    [1] = Outer space\n");
+  } else {
+    printf("    [1] = x%lx  %d\n", (unsigned long) printtri.tri,
+           printtri.orient);
+  }
+  decode(t->tri[2], printtri);
+  if (printtri.tri == m->dummytri) {
+    printf("    [2] = Outer space\n");
+  } else {
+    printf("    [2] = x%lx  %d\n", (unsigned long) printtri.tri,
+           printtri.orient);
+  }
+
+  org(*t, printvertex);
+  if (printvertex == (vertex) NULL)
+    printf("    Origin[%d] = NULL\n", (t->orient + 1) % 3 + 3);
+  else
+    printf("    Origin[%d] = x%lx  (%.12g, %.12g)\n",
+           (t->orient + 1) % 3 + 3, (unsigned long) printvertex,
+           printvertex[0], printvertex[1]);
+  dest(*t, printvertex);
+  if (printvertex == (vertex) NULL)
+    printf("    Dest  [%d] = NULL\n", (t->orient + 2) % 3 + 3);
+  else
+    printf("    Dest  [%d] = x%lx  (%.12g, %.12g)\n",
+           (t->orient + 2) % 3 + 3, (unsigned long) printvertex,
+           printvertex[0], printvertex[1]);
+  apex(*t, printvertex);
+  if (printvertex == (vertex) NULL)
+    printf("    Apex  [%d] = NULL\n", t->orient + 3);
+  else
+    printf("    Apex  [%d] = x%lx  (%.12g, %.12g)\n",
+           t->orient + 3, (unsigned long) printvertex,
+           printvertex[0], printvertex[1]);
+
+  if (b->usesegments) {
+    sdecode(t->tri[6], printsh);
+    if (printsh.ss != m->dummysub) {
+      printf("    [6] = x%lx  %d\n", (unsigned long) printsh.ss,
+             printsh.ssorient);
+    }
+    sdecode(t->tri[7], printsh);
+    if (printsh.ss != m->dummysub) {
+      printf("    [7] = x%lx  %d\n", (unsigned long) printsh.ss,
+             printsh.ssorient);
+    }
+    sdecode(t->tri[8], printsh);
+    if (printsh.ss != m->dummysub) {
+      printf("    [8] = x%lx  %d\n", (unsigned long) printsh.ss,
+             printsh.ssorient);
+    }
+  }
+
+  if (b->vararea) {
+    printf("    Area constraint:  %.4g\n", areabound(*t));
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  printsubseg()   Print out the details of an oriented subsegment.         */
+/*                                                                           */
+/*  I originally wrote this procedure to simplify debugging; it can be       */
+/*  called directly from the debugger, and presents information about an     */
+/*  oriented subsegment in digestible form.  It's also used when the highest */
+/*  level of verbosity (`-VVV') is specified.                                */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void printsubseg(struct mesh *m, struct behavior *b, struct osub *s)
+#else /* not ANSI_DECLARATORS */
+void printsubseg(m, b, s)
+struct mesh *m;
+struct behavior *b;
+struct osub *s;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct osub printsh;
+  struct otri printtri;
+  vertex printvertex;
+
+  printf("subsegment x%lx with orientation %d and mark %d:\n",
+         (unsigned long) s->ss, s->ssorient, mark(*s));
+  sdecode(s->ss[0], printsh);
+  if (printsh.ss == m->dummysub) {
+    printf("    [0] = No subsegment\n");
+  } else {
+    printf("    [0] = x%lx  %d\n", (unsigned long) printsh.ss,
+           printsh.ssorient);
+  }
+  sdecode(s->ss[1], printsh);
+  if (printsh.ss == m->dummysub) {
+    printf("    [1] = No subsegment\n");
+  } else {
+    printf("    [1] = x%lx  %d\n", (unsigned long) printsh.ss,
+           printsh.ssorient);
+  }
+
+  sorg(*s, printvertex);
+  if (printvertex == (vertex) NULL)
+    printf("    Origin[%d] = NULL\n", 2 + s->ssorient);
+  else
+    printf("    Origin[%d] = x%lx  (%.12g, %.12g)\n",
+           2 + s->ssorient, (unsigned long) printvertex,
+           printvertex[0], printvertex[1]);
+  sdest(*s, printvertex);
+  if (printvertex == (vertex) NULL)
+    printf("    Dest  [%d] = NULL\n", 3 - s->ssorient);
+  else
+    printf("    Dest  [%d] = x%lx  (%.12g, %.12g)\n",
+           3 - s->ssorient, (unsigned long) printvertex,
+           printvertex[0], printvertex[1]);
+
+  decode(s->ss[4], printtri);
+  if (printtri.tri == m->dummytri) {
+    printf("    [4] = Outer space\n");
+  } else {
+    printf("    [4] = x%lx  %d\n", (unsigned long) printtri.tri,
+           printtri.orient);
+  }
+  decode(s->ss[5], printtri);
+  if (printtri.tri == m->dummytri) {
+    printf("    [5] = Outer space\n");
+  } else {
+    printf("    [5] = x%lx  %d\n", (unsigned long) printtri.tri,
+           printtri.orient);
+  }
+}
+
+/**                                                                         **/
+/**                                                                         **/
+/********* Debugging routines end here                               *********/
+
+/********* Memory management routines begin here                     *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  poolrestart()   Deallocate all items in a 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.                                                  */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void poolrestart(struct memorypool *pool)
+#else /* not ANSI_DECLARATORS */
+void poolrestart(pool)
+struct memorypool *pool;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  unsigned long alignptr;
+
+  pool->items = 0;
+  pool->maxitems = 0;
+
+  /* Set the currently active block. */
+  pool->nowblock = pool->firstblock;
+  /* Find the first item in the pool.  Increment by the size of (VOID *). */
+  alignptr = (unsigned long) (pool->nowblock + 1);
+  /* Align the item on an `alignbytes'-byte boundary. */
+  pool->nextitem = (VOID *)
+    (alignptr + (unsigned long) pool->alignbytes -
+     (alignptr % (unsigned long) pool->alignbytes));
+  /* There are lots of unallocated items left in this block. */
+  pool->unallocateditems = pool->itemsfirstblock;
+  /* The stack of deallocated items is empty. */
+  pool->deaditemstack = (VOID *) NULL;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  poolinit()   Initialize a pool of memory for allocation of items.        */
+/*                                                                           */
+/*  This routine initializes the machinery for allocating 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.              */
+/*                                                                           */
+/*  Don't change this routine unless you understand it.                      */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void poolinit(struct memorypool *pool, int bytecount, int itemcount,
+              int firstitemcount, enum wordtype wtype, int alignment)
+#else /* not ANSI_DECLARATORS */
+void poolinit(pool, bytecount, itemcount, firstitemcount, wtype, alignment)
+struct memorypool *pool;
+int bytecount;
+int itemcount;
+int firstitemcount;
+enum wordtype wtype;
+int alignment;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  int wordsize;
+
+  /* Initialize values in the pool. */
+  pool->itemwordtype = wtype;
+  wordsize = (pool->itemwordtype == POINTER) ? sizeof(VOID *) : sizeof(REAL);
+  /* 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) {
+    pool->alignbytes = alignment;
+  } else {
+    pool->alignbytes = wordsize;
+  }
+  if (sizeof(VOID *) > pool->alignbytes) {
+    pool->alignbytes = sizeof(VOID *);
+  }
+  pool->itemwords = ((bytecount + pool->alignbytes - 1) / pool->alignbytes)
+                  * (pool->alignbytes / wordsize);
+  pool->itembytes = pool->itemwords * wordsize;
+  pool->itemsperblock = itemcount;
+  if (firstitemcount == 0) {
+    pool->itemsfirstblock = itemcount;
+  } else {
+    pool->itemsfirstblock = firstitemcount;
+  }
+
+  /* Allocate a block of items.  Space for `itemsfirstblock' items and one  */
+  /*   pointer (to point to the next block) are allocated, as well as space */
+  /*   to ensure alignment of the items.                                    */
+  pool->firstblock = (VOID **)
+    trimalloc(pool->itemsfirstblock * pool->itembytes + (int) sizeof(VOID *) +
+              pool->alignbytes);
+  /* Set the next block pointer to NULL. */
+  *(pool->firstblock) = (VOID *) NULL;
+  poolrestart(pool);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  pooldeinit()   Free to the operating system all memory taken by a pool.  */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void pooldeinit(struct memorypool *pool)
+#else /* not ANSI_DECLARATORS */
+void pooldeinit(pool)
+struct memorypool *pool;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  while (pool->firstblock != (VOID **) NULL) {
+    pool->nowblock = (VOID **) *(pool->firstblock);
+    trifree((VOID *) pool->firstblock);
+    pool->firstblock = pool->nowblock;
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  poolalloc()   Allocate space for an item.                                */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+VOID *poolalloc(struct memorypool *pool)
+#else /* not ANSI_DECLARATORS */
+VOID *poolalloc(pool)
+struct memorypool *pool;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  VOID *newitem;
+  VOID **newblock;
+  unsigned long 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 (pool->deaditemstack != (VOID *) NULL) {
+    newitem = pool->deaditemstack;               /* Take first item in list. */
+    pool->deaditemstack = * (VOID **) pool->deaditemstack;
+  } else {
+    /* Check if there are any free items left in the current block. */
+    if (pool->unallocateditems == 0) {
+      /* Check if another block must be allocated. */
+      if (*(pool->nowblock) == (VOID *) NULL) {
+        /* Allocate a new block of items, pointed to by the previous block. */
+        newblock = (VOID **) trimalloc(pool->itemsperblock * pool->itembytes +
+                                       (int) sizeof(VOID *) +
+                                       pool->alignbytes);
+        *(pool->nowblock) = (VOID *) newblock;
+        /* The next block pointer is NULL. */
+        *newblock = (VOID *) NULL;
+      }
+
+      /* Move to the new block. */
+      pool->nowblock = (VOID **) *(pool->nowblock);
+      /* Find the first item in the block.    */
+      /*   Increment by the size of (VOID *). */
+      alignptr = (unsigned long) (pool->nowblock + 1);
+      /* Align the item on an `alignbytes'-byte boundary. */
+      pool->nextitem = (VOID *)
+        (alignptr + (unsigned long) pool->alignbytes -
+         (alignptr % (unsigned long) pool->alignbytes));
+      /* There are lots of unallocated items left in this block. */
+      pool->unallocateditems = pool->itemsperblock;
+    }
+
+    /* Allocate a new item. */
+    newitem = pool->nextitem;
+    /* Advance `nextitem' pointer to next free item in block. */
+    if (pool->itemwordtype == POINTER) {
+      pool->nextitem = (VOID *) ((VOID **) pool->nextitem + pool->itemwords);
+    } else {
+      pool->nextitem = (VOID *) ((REAL *) pool->nextitem + pool->itemwords);
+    }
+    pool->unallocateditems--;
+    pool->maxitems++;
+  }
+  pool->items++;
+  return newitem;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  pooldealloc()   Deallocate space for an item.                            */
+/*                                                                           */
+/*  The deallocated space is stored in a queue for later reuse.              */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void pooldealloc(struct memorypool *pool, VOID *dyingitem)
+#else /* not ANSI_DECLARATORS */
+void pooldealloc(pool, dyingitem)
+struct memorypool *pool;
+VOID *dyingitem;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  /* Push freshly killed item onto stack. */
+  *((VOID **) dyingitem) = pool->deaditemstack;
+  pool->deaditemstack = dyingitem;
+  pool->items--;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  traversalinit()   Prepare to traverse the entire list of items.          */
+/*                                                                           */
+/*  This routine is used in conjunction with traverse().                     */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void traversalinit(struct memorypool *pool)
+#else /* not ANSI_DECLARATORS */
+void traversalinit(pool)
+struct memorypool *pool;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  unsigned long alignptr;
+
+  /* Begin the traversal in the first block. */
+  pool->pathblock = pool->firstblock;
+  /* Find the first item in the block.  Increment by the size of (VOID *). */
+  alignptr = (unsigned long) (pool->pathblock + 1);
+  /* Align with item on an `alignbytes'-byte boundary. */
+  pool->pathitem = (VOID *)
+    (alignptr + (unsigned long) pool->alignbytes -
+     (alignptr % (unsigned long) pool->alignbytes));
+  /* Set the number of items left in the current block. */
+  pool->pathitemsleft = pool->itemsfirstblock;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  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.  Why?  I don't want to allocate extra      */
+/*  space just to demarcate dead items.  It can usually be done more         */
+/*  space-efficiently by a routine that knows something about the structure  */
+/*  of the item.                                                             */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+VOID *traverse(struct memorypool *pool)
+#else /* not ANSI_DECLARATORS */
+VOID *traverse(pool)
+struct memorypool *pool;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  VOID *newitem;
+  unsigned long alignptr;
+
+  /* Stop upon exhausting the list of items. */
+  if (pool->pathitem == pool->nextitem) {
+    return (VOID *) NULL;
+  }
+
+  /* Check whether any untraversed items remain in the current block. */
+  if (pool->pathitemsleft == 0) {
+    /* Find the next block. */
+    pool->pathblock = (VOID **) *(pool->pathblock);
+    /* Find the first item in the block.  Increment by the size of (VOID *). */
+    alignptr = (unsigned long) (pool->pathblock + 1);
+    /* Align with item on an `alignbytes'-byte boundary. */
+    pool->pathitem = (VOID *)
+      (alignptr + (unsigned long) pool->alignbytes -
+       (alignptr % (unsigned long) pool->alignbytes));
+    /* Set the number of items left in the current block. */
+    pool->pathitemsleft = pool->itemsperblock;
+  }
+
+  newitem = pool->pathitem;
+  /* Find the next item in the block. */
+  if (pool->itemwordtype == POINTER) {
+    pool->pathitem = (VOID *) ((VOID **) pool->pathitem + pool->itemwords);
+  } else {
+    pool->pathitem = (VOID *) ((REAL *) pool->pathitem + pool->itemwords);
+  }
+  pool->pathitemsleft--;
+  return newitem;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  dummyinit()   Initialize the triangle that fills "outer space" and the   */
+/*                omnipresent subsegment.                                    */
+/*                                                                           */
+/*  The triangle that fills "outer space," called `dummytri', is pointed to  */
+/*  by every triangle and subsegment on a boundary (be it outer or inner) of */
+/*  the triangulation.  Also, `dummytri' points to one of the triangles on   */
+/*  the convex hull (until the holes and concavities are carved), making it  */
+/*  possible to find a starting triangle for point location.                 */
+/*                                                                           */
+/*  The omnipresent subsegment, `dummysub', is pointed to by every triangle  */
+/*  or subsegment that doesn't have a full complement of real subsegments    */
+/*  to point to.                                                             */
+/*                                                                           */
+/*  `dummytri' and `dummysub' are generally required to fulfill only a few   */
+/*  invariants:  their vertices must remain NULL and `dummytri' must always  */
+/*  be bonded (at offset zero) to some triangle on the convex hull of the    */
+/*  mesh, via a boundary edge.  Otherwise, the connections of `dummytri' and */
+/*  `dummysub' may change willy-nilly.  This makes it possible to avoid      */
+/*  writing a good deal of special-case code (in the edge flip, for example) */
+/*  for dealing with the boundary of the mesh, places where no subsegment is */
+/*  present, and so forth.  Other entities are frequently bonded to          */
+/*  `dummytri' and `dummysub' as if they were real mesh entities, with no    */
+/*  harm done.                                                               */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void dummyinit(struct mesh *m, struct behavior *b, int trianglewords,
+               int subsegwords)
+#else /* not ANSI_DECLARATORS */
+void dummyinit(m, b, trianglewords, subsegwords)
+struct mesh *m;
+struct behavior *b;
+int trianglewords;
+int subsegwords;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  unsigned long alignptr;
+
+  /* Set up `dummytri', the `triangle' that occupies "outer space." */
+  m->dummytribase = (triangle *)
+                    trimalloc(trianglewords * (int) sizeof(triangle) +
+                              m->triangles.alignbytes);
+  /* Align `dummytri' on a `triangles.alignbytes'-byte boundary. */
+  alignptr = (unsigned long) m->dummytribase;
+  m->dummytri = (triangle *)
+    (alignptr + (unsigned long) m->triangles.alignbytes -
+     (alignptr % (unsigned long) m->triangles.alignbytes));
+  /* Initialize the three adjoining triangles to be "outer space."  These  */
+  /*   will eventually be changed by various bonding operations, but their */
+  /*   values don't really matter, as long as they can legally be          */
+  /*   dereferenced.                                                       */
+  m->dummytri[0] = (triangle) m->dummytri;
+  m->dummytri[1] = (triangle) m->dummytri;
+  m->dummytri[2] = (triangle) m->dummytri;
+  /* Three NULL vertices. */
+  m->dummytri[3] = (triangle) NULL;
+  m->dummytri[4] = (triangle) NULL;
+  m->dummytri[5] = (triangle) NULL;
+
+  if (b->usesegments) {
+    /* Set up `dummysub', the omnipresent subsegment pointed to by any */
+    /*   triangle side or subsegment end that isn't attached to a real */
+    /*   subsegment.                                                   */
+    m->dummysubbase = (subseg *) trimalloc(subsegwords * (int) sizeof(subseg) +
+                                           m->subsegs.alignbytes);
+    /* Align `dummysub' on a `subsegs.alignbytes'-byte boundary. */
+    alignptr = (unsigned long) m->dummysubbase;
+    m->dummysub = (subseg *)
+      (alignptr + (unsigned long) m->subsegs.alignbytes -
+       (alignptr % (unsigned long) m->subsegs.alignbytes));
+    /* Initialize the two adjoining subsegments to be the omnipresent      */
+    /*   subsegment.  These will eventually be changed by various bonding  */
+    /*   operations, but their values don't really matter, as long as they */
+    /*   can legally be dereferenced.                                      */
+    m->dummysub[0] = (subseg) m->dummysub;
+    m->dummysub[1] = (subseg) m->dummysub;
+    /* Two NULL vertices. */
+    m->dummysub[2] = (subseg) NULL;
+    m->dummysub[3] = (subseg) NULL;
+    /* Initialize the two adjoining triangles to be "outer space." */
+    m->dummysub[4] = (subseg) m->dummytri;
+    m->dummysub[5] = (subseg) m->dummytri;
+    /* Set the boundary marker to zero. */
+    * (int *) (m->dummysub + 6) = 0;
+
+    /* Initialize the three adjoining subsegments of `dummytri' to be */
+    /*   the omnipresent subsegment.                                  */
+    m->dummytri[6] = (triangle) m->dummysub;
+    m->dummytri[7] = (triangle) m->dummysub;
+    m->dummytri[8] = (triangle) m->dummysub;
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  initializevertexpool()   Calculate the size of the vertex data structure */
+/*                           and initialize its memory pool.                 */
+/*                                                                           */
+/*  This routine also computes the `vertexmarkindex' and `vertex2triindex'   */
+/*  indices used to find values within each vertex.                          */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void initializevertexpool(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void initializevertexpool(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  int vertexsize;
+
+  /* The index within each vertex at which the boundary marker is found,    */
+  /*   followed by the vertex type.  Ensure the vertex marker is aligned to */
+  /*   a sizeof(int)-byte address.                                          */
+  m->vertexmarkindex = ((m->mesh_dim + m->nextras) * sizeof(REAL) +
+                        sizeof(int) - 1) /
+                       sizeof(int);
+  vertexsize = (m->vertexmarkindex + 2) * sizeof(int);
+  if (b->poly) {
+    /* The index within each vertex at which a triangle pointer is found.  */
+    /*   Ensure the pointer is aligned to a sizeof(triangle)-byte address. */
+    m->vertex2triindex = (vertexsize + sizeof(triangle) - 1) /
+                         sizeof(triangle);
+    vertexsize = (m->vertex2triindex + 1) * sizeof(triangle);
+  }
+
+  /* Initialize the pool of vertices. */
+  poolinit(&m->vertices, vertexsize, VERTEXPERBLOCK,
+           m->invertices > VERTEXPERBLOCK ? m->invertices : VERTEXPERBLOCK,
+           (sizeof(REAL) >= sizeof(triangle)) ? FLOATINGPOINT : POINTER, 0);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  initializetrisubpools()   Calculate the sizes of the triangle and        */
+/*                            subsegment data structures and initialize      */
+/*                            their memory pools.                            */
+/*                                                                           */
+/*  This routine also computes the `highorderindex', `elemattribindex', and  */
+/*  `areaboundindex' indices used to find values within each triangle.       */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void initializetrisubpools(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void initializetrisubpools(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  int trisize;
+
+  /* The index within each triangle at which the extra nodes (above three)  */
+  /*   associated with high order elements are found.  There are three      */
+  /*   pointers to other triangles, three pointers to corners, and possibly */
+  /*   three pointers to subsegments before the extra nodes.                */
+  m->highorderindex = 6 + (b->usesegments * 3);
+  /* The number of bytes occupied by a triangle. */
+  trisize = ((b->order + 1) * (b->order + 2) / 2 + (m->highorderindex - 3)) *
+            sizeof(triangle);
+  /* The index within each triangle at which its attributes are found, */
+  /*   where the index is measured in REALs.                           */
+  m->elemattribindex = (trisize + sizeof(REAL) - 1) / sizeof(REAL);
+  /* The index within each triangle at which the maximum area constraint  */
+  /*   is found, where the index is measured in REALs.  Note that if the  */
+  /*   `regionattrib' flag is set, an additional attribute will be added. */
+  m->areaboundindex = m->elemattribindex + m->eextras + b->regionattrib;
+  /* If triangle attributes or an area bound are needed, increase the number */
+  /*   of bytes occupied by a triangle.                                      */
+  if (b->vararea) {
+    trisize = (m->areaboundindex + 1) * sizeof(REAL);
+  } else if (m->eextras + b->regionattrib > 0) {
+    trisize = m->areaboundindex * sizeof(REAL);
+  }
+  /* If a Voronoi diagram or triangle neighbor graph is requested, make    */
+  /*   sure there's room to store an integer index in each triangle.  This */
+  /*   integer index can occupy the same space as the subsegment pointers  */
+  /*   or attributes or area constraint or extra nodes.                    */
+  if ((b->voronoi || b->neighbors) &&
+      (trisize < 6 * sizeof(triangle) + sizeof(int))) {
+    trisize = 6 * sizeof(triangle) + sizeof(int);
+  }
+
+  /* Having determined the memory size of a triangle, initialize the pool. */
+  poolinit(&m->triangles, trisize, TRIPERBLOCK,
+           (2 * m->invertices - 2) > TRIPERBLOCK ? (2 * m->invertices - 2) :
+           TRIPERBLOCK, POINTER, 4);
+
+  if (b->usesegments) {
+    /* Initialize the pool of subsegments.  Take into account all six */
+    /*   pointers and one boundary marker.                            */
+    poolinit(&m->subsegs, 6 * sizeof(triangle) + sizeof(int), SUBSEGPERBLOCK,
+             SUBSEGPERBLOCK, POINTER, 4);
+
+    /* Initialize the "outer space" triangle and omnipresent subsegment. */
+    dummyinit(m, b, m->triangles.itemwords, m->subsegs.itemwords);
+  } else {
+    /* Initialize the "outer space" triangle. */
+    dummyinit(m, b, m->triangles.itemwords, 0);
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  triangledealloc()   Deallocate space for a triangle, marking it dead.    */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void triangledealloc(struct mesh *m, triangle *dyingtriangle)
+#else /* not ANSI_DECLARATORS */
+void triangledealloc(m, dyingtriangle)
+struct mesh *m;
+triangle *dyingtriangle;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  /* Mark the triangle as dead.  This makes it possible to detect dead */
+  /*   triangles when traversing the list of all triangles.            */
+  killtri(dyingtriangle);
+  pooldealloc(&m->triangles, (VOID *) dyingtriangle);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  triangletraverse()   Traverse the triangles, skipping dead ones.         */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+triangle *triangletraverse(struct mesh *m)
+#else /* not ANSI_DECLARATORS */
+triangle *triangletraverse(m)
+struct mesh *m;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  triangle *newtriangle;
+
+  do {
+    newtriangle = (triangle *) traverse(&m->triangles);
+    if (newtriangle == (triangle *) NULL) {
+      return (triangle *) NULL;
+    }
+  } while (deadtri(newtriangle));                         /* Skip dead ones. */
+  return newtriangle;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  subsegdealloc()   Deallocate space for a subsegment, marking it dead.    */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void subsegdealloc(struct mesh *m, subseg *dyingsubseg)
+#else /* not ANSI_DECLARATORS */
+void subsegdealloc(m, dyingsubseg)
+struct mesh *m;
+subseg *dyingsubseg;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  /* Mark the subsegment as dead.  This makes it possible to detect dead */
+  /*   subsegments when traversing the list of all subsegments.          */
+  killsubseg(dyingsubseg);
+  pooldealloc(&m->subsegs, (VOID *) dyingsubseg);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  subsegtraverse()   Traverse the subsegments, skipping dead ones.         */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+subseg *subsegtraverse(struct mesh *m)
+#else /* not ANSI_DECLARATORS */
+subseg *subsegtraverse(m)
+struct mesh *m;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  subseg *newsubseg;
+
+  do {
+    newsubseg = (subseg *) traverse(&m->subsegs);
+    if (newsubseg == (subseg *) NULL) {
+      return (subseg *) NULL;
+    }
+  } while (deadsubseg(newsubseg));                        /* Skip dead ones. */
+  return newsubseg;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  vertexdealloc()   Deallocate space for a vertex, marking it dead.        */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void vertexdealloc(struct mesh *m, vertex dyingvertex)
+#else /* not ANSI_DECLARATORS */
+void vertexdealloc(m, dyingvertex)
+struct mesh *m;
+vertex dyingvertex;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  /* Mark the vertex as dead.  This makes it possible to detect dead */
+  /*   vertices when traversing the list of all vertices.            */
+  setvertextype(dyingvertex, DEADVERTEX);
+  pooldealloc(&m->vertices, (VOID *) dyingvertex);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  vertextraverse()   Traverse the vertices, skipping dead ones.            */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+vertex vertextraverse(struct mesh *m)
+#else /* not ANSI_DECLARATORS */
+vertex vertextraverse(m)
+struct mesh *m;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  vertex newvertex;
+
+  do {
+    newvertex = (vertex) traverse(&m->vertices);
+    if (newvertex == (vertex) NULL) {
+      return (vertex) NULL;
+    }
+  } while (vertextype(newvertex) == DEADVERTEX);          /* Skip dead ones. */
+  return newvertex;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  badsubsegdealloc()   Deallocate space for a bad subsegment, marking it   */
+/*                       dead.                                               */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void badsubsegdealloc(struct mesh *m, struct badsubseg *dyingseg)
+#else /* not ANSI_DECLARATORS */
+void badsubsegdealloc(m, dyingseg)
+struct mesh *m;
+struct badsubseg *dyingseg;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  /* Set subsegment's origin to NULL.  This makes it possible to detect dead */
+  /*   subsegments when traversing the list of all encroached subsegments.   */
+  dyingseg->subsegorg = (vertex) NULL;
+  pooldealloc(&m->badsubsegs, (VOID *) dyingseg);
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  badsubsegtraverse()   Traverse the bad subsegments, skipping dead ones.  */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+struct badsubseg *badsubsegtraverse(struct mesh *m)
+#else /* not ANSI_DECLARATORS */
+struct badsubseg *badsubsegtraverse(m)
+struct mesh *m;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct badsubseg *newseg;
+
+  do {
+    newseg = (struct badsubseg *) traverse(&m->badsubsegs);
+    if (newseg == (struct badsubseg *) NULL) {
+      return (struct badsubseg *) NULL;
+    }
+  } while (newseg->subsegorg == (vertex) NULL);           /* Skip dead ones. */
+  return newseg;
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  getvertex()   Get a specific vertex, by number, from the list.           */
+/*                                                                           */
+/*  The first vertex is number 'firstnumber'.                                */
+/*                                                                           */
+/*  Note that this takes O(n) time (with a small constant, if VERTEXPERBLOCK */
+/*  is large).  I don't care to take the trouble to make it work in constant */
+/*  time.                                                                    */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+vertex getvertex(struct mesh *m, struct behavior *b, int number)
+#else /* not ANSI_DECLARATORS */
+vertex getvertex(m, b, number)
+struct mesh *m;
+struct behavior *b;
+int number;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  VOID **getblock;
+  vertex foundvertex;
+  unsigned long alignptr;
+  int current;
+
+  getblock = m->vertices.firstblock;
+  current = b->firstnumber;
+
+  /* Find the right block. */
+  if (current + m->vertices.itemsfirstblock <= number) {
+    getblock = (VOID **) *getblock;
+    current += m->vertices.itemsfirstblock;
+    while (current + m->vertices.itemsperblock <= number) {
+      getblock = (VOID **) *getblock;
+      current += m->vertices.itemsperblock;
+    }
+  }
+
+  /* Now find the right vertex. */
+  alignptr = (unsigned long) (getblock + 1);
+  foundvertex = (vertex) (alignptr + (unsigned long) m->vertices.alignbytes -
+                          (alignptr % (unsigned long) m->vertices.alignbytes));
+  while (current < number) {
+    foundvertex += m->vertices.itemwords;
+    current++;
+  }
+  return foundvertex;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  triangledeinit()   Free all remaining allocated memory.                  */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void triangledeinit(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void triangledeinit(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  pooldeinit(&m->triangles);
+  trifree((VOID *) m->dummytribase);
+  if (b->usesegments) {
+    pooldeinit(&m->subsegs);
+    trifree((VOID *) m->dummysubbase);
+  }
+  pooldeinit(&m->vertices);
+#ifndef CDT_ONLY
+  if (b->quality) {
+    pooldeinit(&m->badsubsegs);
+    if ((b->minangle > 0.0) || b->vararea || b->fixedarea || b->usertest) {
+      pooldeinit(&m->badtriangles);
+      pooldeinit(&m->flipstackers);
+    }
+  }
+#endif /* not CDT_ONLY */
+}
+
+/**                                                                         **/
+/**                                                                         **/
+/********* Memory management routines end here                       *********/
+
+/********* Constructors begin here                                   *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  maketriangle()   Create a new triangle with orientation zero.            */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void maketriangle(struct mesh *m, struct behavior *b, struct otri *newotri)
+#else /* not ANSI_DECLARATORS */
+void maketriangle(m, b, newotri)
+struct mesh *m;
+struct behavior *b;
+struct otri *newotri;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  int i;
+
+  newotri->tri = (triangle *) poolalloc(&m->triangles);
+  /* Initialize the three adjoining triangles to be "outer space". */
+  newotri->tri[0] = (triangle) m->dummytri;
+  newotri->tri[1] = (triangle) m->dummytri;
+  newotri->tri[2] = (triangle) m->dummytri;
+  /* Three NULL vertices. */
+  newotri->tri[3] = (triangle) NULL;
+  newotri->tri[4] = (triangle) NULL;
+  newotri->tri[5] = (triangle) NULL;
+  if (b->usesegments) {
+    /* Initialize the three adjoining subsegments to be the omnipresent */
+    /*   subsegment.                                                    */
+    newotri->tri[6] = (triangle) m->dummysub;
+    newotri->tri[7] = (triangle) m->dummysub;
+    newotri->tri[8] = (triangle) m->dummysub;
+  }
+  for (i = 0; i < m->eextras; i++) {
+    setelemattribute(*newotri, i, 0.0);
+  }
+  if (b->vararea) {
+    setareabound(*newotri, -1.0);
+  }
+
+  newotri->orient = 0;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  makesubseg()   Create a new subsegment with orientation zero.            */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void makesubseg(struct mesh *m, struct osub *newsubseg)
+#else /* not ANSI_DECLARATORS */
+void makesubseg(m, newsubseg)
+struct mesh *m;
+struct osub *newsubseg;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  newsubseg->ss = (subseg *) poolalloc(&m->subsegs);
+  /* Initialize the two adjoining subsegments to be the omnipresent */
+  /*   subsegment.                                                  */
+  newsubseg->ss[0] = (subseg) m->dummysub;
+  newsubseg->ss[1] = (subseg) m->dummysub;
+  /* Two NULL vertices. */
+  newsubseg->ss[2] = (subseg) NULL;
+  newsubseg->ss[3] = (subseg) NULL;
+  /* Initialize the two adjoining triangles to be "outer space." */
+  newsubseg->ss[4] = (subseg) m->dummytri;
+  newsubseg->ss[5] = (subseg) m->dummytri;
+  /* Set the boundary marker to zero. */
+  setmark(*newsubseg, 0);
+
+  newsubseg->ssorient = 0;
+}
+
+/**                                                                         **/
+/**                                                                         **/
+/********* Constructors end here                                     *********/
+
+/********* Geometric primitives begin here                           *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/* The adaptive exact arithmetic geometric predicates implemented herein are */
+/*   described in detail in my paper, "Adaptive Precision Floating-Point     */
+/*   Arithmetic and Fast Robust Geometric Predicates."  See the header for a */
+/*   full citation.                                                          */
+
+/* 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 without       */
+/*   forcing the value to be stored to memory (rather than be kept in the    */
+/*   register to which the optimizer assigned it).                           */
+
+#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 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
+
+/* 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)
+
+/* Macro for multiplying a two-component expansion by a single component.    */
+
+#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)
+
+/*****************************************************************************/
+/*                                                                           */
+/*  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 half;
+  REAL check, lastcheck;
+  int every_other;
+#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 */
+
+  every_other = 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 these routines will work on such machines.)       */
+  do {
+    lastcheck = check;
+    epsilon *= half;
+    if (every_other) {
+      splitter *= 2.0;
+    }
+    every_other = !every_other;
+    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;
+  iccerrboundA = (10.0 + 96.0 * epsilon) * epsilon;
+  iccerrboundB = (4.0 + 48.0 * epsilon) * epsilon;
+  iccerrboundC = (44.0 + 576.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;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  fast_expansion_sum_zeroelim()   Sum two expansions, eliminating zero     */
+/*                                  components from the output expansion.    */
+/*                                                                           */
+/*  Sets h = e + f.  See my Robust Predicates 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.                                                              */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+int fast_expansion_sum_zeroelim(int elen, REAL *e, int flen, REAL *f, REAL *h)
+#else /* not ANSI_DECLARATORS */
+int fast_expansion_sum_zeroelim(elen, e, flen, f, h)  /* h cannot be e or f. */
+int elen;
+REAL *e;
+int flen;
+REAL *f;
+REAL *h;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  REAL Q;
+  INEXACT REAL Qnew;
+  INEXACT REAL hh;
+  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;
+    enow = e[++eindex];
+  } else {
+    Q = fnow;
+    fnow = f[++findex];
+  }
+  hindex = 0;
+  if ((eindex < elen) && (findex < flen)) {
+    if ((fnow > enow) == (fnow > -enow)) {
+      Fast_Two_Sum(enow, Q, Qnew, hh);
+      enow = e[++eindex];
+    } else {
+      Fast_Two_Sum(fnow, Q, Qnew, hh);
+      fnow = f[++findex];
+    }
+    Q = Qnew;
+    if (hh != 0.0) {
+      h[hindex++] = hh;
+    }
+    while ((eindex < elen) && (findex < flen)) {
+      if ((fnow > enow) == (fnow > -enow)) {
+        Two_Sum(Q, enow, Qnew, hh);
+        enow = e[++eindex];
+      } else {
+        Two_Sum(Q, fnow, Qnew, hh);
+        fnow = f[++findex];
+      }
+      Q = Qnew;
+      if (hh != 0.0) {
+        h[hindex++] = hh;
+      }
+    }
+  }
+  while (eindex < elen) {
+    Two_Sum(Q, enow, Qnew, hh);
+    enow = e[++eindex];
+    Q = Qnew;
+    if (hh != 0.0) {
+      h[hindex++] = hh;
+    }
+  }
+  while (findex < flen) {
+    Two_Sum(Q, fnow, Qnew, hh);
+    fnow = f[++findex];
+    Q = Qnew;
+    if (hh != 0.0) {
+      h[hindex++] = hh;
+    }
+  }
+  if ((Q != 0.0) || (hindex == 0)) {
+    h[hindex++] = Q;
+  }
+  return hindex;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  scale_expansion_zeroelim()   Multiply an expansion by a scalar,          */
+/*                               eliminating zero components from the        */
+/*                               output expansion.                           */
+/*                                                                           */
+/*  Sets h = be.  See my Robust Predicates 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.)                                                                 */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+int scale_expansion_zeroelim(int elen, REAL *e, REAL b, REAL *h)
+#else /* not ANSI_DECLARATORS */
+int scale_expansion_zeroelim(elen, e, b, h)   /* e and h cannot be the same. */
+int elen;
+REAL *e;
+REAL b;
+REAL *h;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  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 my Robust Predicates paper for details.                              */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+REAL estimate(int elen, REAL *e)
+#else /* not ANSI_DECLARATORS */
+REAL estimate(elen, e)
+int elen;
+REAL *e;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  REAL Q;
+  int eindex;
+
+  Q = e[0];
+  for (eindex = 1; eindex < elen; eindex++) {
+    Q += e[eindex];
+  }
+  return Q;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  counterclockwise()   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.               */
+/*                                                                           */
+/*  Uses exact arithmetic if necessary to ensure a correct answer.  The      */
+/*  result returned is the determinant of a matrix.  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, this function is usually quite fast, but will run  */
+/*  more slowly when the input points are collinear or nearly so.            */
+/*                                                                           */
+/*  See my Robust Predicates paper for details.                              */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+REAL counterclockwiseadapt(vertex pa, vertex pb, vertex pc, REAL detsum)
+#else /* not ANSI_DECLARATORS */
+REAL counterclockwiseadapt(pa, pb, pc, detsum)
+vertex pa;
+vertex pb;
+vertex pc;
+REAL detsum;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  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]);
+}
+
+#ifdef ANSI_DECLARATORS
+REAL counterclockwise(struct mesh *m, struct behavior *b,
+                      vertex pa, vertex pb, vertex pc)
+#else /* not ANSI_DECLARATORS */
+REAL counterclockwise(m, b, pa, pb, pc)
+struct mesh *m;
+struct behavior *b;
+vertex pa;
+vertex pb;
+vertex pc;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  REAL detleft, detright, det;
+  REAL detsum, errbound;
+
+  m->counterclockcount++;
+
+  detleft = (pa[0] - pc[0]) * (pb[1] - pc[1]);
+  detright = (pa[1] - pc[1]) * (pb[0] - pc[0]);
+  det = detleft - detright;
+
+  if (b->noexact) {
+    return det;
+  }
+
+  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 counterclockwiseadapt(pa, pb, pc, detsum);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  incircle()   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.          */
+/*                                                                           */
+/*  Uses exact arithmetic if necessary to ensure a correct answer.  The      */
+/*  result returned is the determinant of a matrix.  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, this function is usually quite fast, but will run  */
+/*  more slowly when the input points are cocircular or nearly so.           */
+/*                                                                           */
+/*  See my Robust Predicates paper for details.                              */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+REAL incircleadapt(vertex pa, vertex pb, vertex pc, vertex pd, REAL permanent)
+#else /* not ANSI_DECLARATORS */
+REAL incircleadapt(pa, pb, pc, pd, permanent)
+vertex pa;
+vertex pb;
+vertex pc;
+vertex pd;
+REAL permanent;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  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;
+
+  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];
+}
+
+#ifdef ANSI_DECLARATORS
+REAL incircle(struct mesh *m, struct behavior *b,
+              vertex pa, vertex pb, vertex pc, vertex pd)
+#else /* not ANSI_DECLARATORS */
+REAL incircle(m, b, pa, pb, pc, pd)
+struct mesh *m;
+struct behavior *b;
+vertex pa;
+vertex pb;
+vertex pc;
+vertex pd;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  REAL adx, bdx, cdx, ady, bdy, cdy;
+  REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady;
+  REAL alift, blift, clift;
+  REAL det;
+  REAL permanent, errbound;
+
+  m->incirclecount++;
+
+  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);
+
+  if (b->noexact) {
+    return det;
+  }
+
+  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);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  orient3d()   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.                                                */
+/*                                                                           */
+/*  Uses exact arithmetic if necessary to ensure a correct answer.  The      */
+/*  result returned is the determinant of a matrix.  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, this function is usually quite fast, but will run  */
+/*  more slowly when the input points are coplanar or nearly so.             */
+/*                                                                           */
+/*  See my Robust Predicates paper for details.                              */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+REAL orient3dadapt(vertex pa, vertex pb, vertex pc, vertex pd,
+                   REAL aheight, REAL bheight, REAL cheight, REAL dheight,
+                   REAL permanent)
+#else /* not ANSI_DECLARATORS */
+REAL orient3dadapt(pa, pb, pc, pd,
+                   aheight, bheight, cheight, dheight, permanent)
+vertex pa;
+vertex pb;
+vertex pc;
+vertex pd;
+REAL aheight;
+REAL bheight;
+REAL cheight;
+REAL dheight;
+REAL permanent;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  INEXACT REAL adx, bdx, cdx, ady, bdy, cdy, adheight, bdheight, cdheight;
+  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;
+
+  REAL adxtail, bdxtail, cdxtail;
+  REAL adytail, bdytail, cdytail;
+  REAL adheighttail, bdheighttail, cdheighttail;
+  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]);
+  adheight = (REAL) (aheight - dheight);
+  bdheight = (REAL) (bheight - dheight);
+  cdheight = (REAL) (cheight - dheight);
+
+  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, adheight, 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, bdheight, 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, cdheight, 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(aheight, dheight, adheight, adheighttail);
+  Two_Diff_Tail(bheight, dheight, bdheight, bdheighttail);
+  Two_Diff_Tail(cheight, dheight, cdheight, cdheighttail);
+
+  if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0) &&
+      (adytail == 0.0) && (bdytail == 0.0) && (cdytail == 0.0) &&
+      (adheighttail == 0.0) &&
+      (bdheighttail == 0.0) &&
+      (cdheighttail == 0.0)) {
+    return det;
+  }
+
+  errbound = o3derrboundC * permanent + resulterrbound * Absolute(det);
+  det += (adheight * ((bdx * cdytail + cdy * bdxtail) -
+                      (bdy * cdxtail + cdx * bdytail)) +
+          adheighttail * (bdx * cdy - bdy * cdx)) +
+         (bdheight * ((cdx * adytail + ady * cdxtail) -
+                      (cdy * adxtail + adx * cdytail)) +
+          bdheighttail * (cdx * ady - cdy * adx)) +
+         (cdheight * ((adx * bdytail + bdy * adxtail) -
+                      (ady * bdxtail + bdx * adytail)) +
+          cdheighttail * (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, adheight, 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, bdheight, 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, cdheight, w);
+  finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+                                          finother);
+  finswap = finnow; finnow = finother; finother = finswap;
+
+  if (adheighttail != 0.0) {
+    vlength = scale_expansion_zeroelim(4, bc, adheighttail, v);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v,
+                                            finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (bdheighttail != 0.0) {
+    vlength = scale_expansion_zeroelim(4, ca, bdheighttail, v);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v,
+                                            finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (cdheighttail != 0.0) {
+    vlength = scale_expansion_zeroelim(4, ab, cdheighttail, 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, cdheight, 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 (cdheighttail != 0.0) {
+        Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdheighttail,
+                        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, bdheight, 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 (bdheighttail != 0.0) {
+        Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdheighttail,
+                        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, adheight, 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 (adheighttail != 0.0) {
+        Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adheighttail,
+                        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, cdheight, 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 (cdheighttail != 0.0) {
+        Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdheighttail,
+                        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, bdheight, 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 (bdheighttail != 0.0) {
+        Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdheighttail,
+                        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, adheight, 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 (adheighttail != 0.0) {
+        Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adheighttail,
+                        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 (adheighttail != 0.0) {
+    wlength = scale_expansion_zeroelim(bctlen, bct, adheighttail, w);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+                                            finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (bdheighttail != 0.0) {
+    wlength = scale_expansion_zeroelim(catlen, cat, bdheighttail, w);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+                                            finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (cdheighttail != 0.0) {
+    wlength = scale_expansion_zeroelim(abtlen, abt, cdheighttail, w);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+                                            finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+
+  return finnow[finlength - 1];
+}
+
+#ifdef ANSI_DECLARATORS
+REAL orient3d(struct mesh *m, struct behavior *b,
+              vertex pa, vertex pb, vertex pc, vertex pd,
+              REAL aheight, REAL bheight, REAL cheight, REAL dheight)
+#else /* not ANSI_DECLARATORS */
+REAL orient3d(m, b, pa, pb, pc, pd, aheight, bheight, cheight, dheight)
+struct mesh *m;
+struct behavior *b;
+vertex pa;
+vertex pb;
+vertex pc;
+vertex pd;
+REAL aheight;
+REAL bheight;
+REAL cheight;
+REAL dheight;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  REAL adx, bdx, cdx, ady, bdy, cdy, adheight, bdheight, cdheight;
+  REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady;
+  REAL det;
+  REAL permanent, errbound;
+
+  m->orient3dcount++;
+
+  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];
+  adheight = aheight - dheight;
+  bdheight = bheight - dheight;
+  cdheight = cheight - dheight;
+
+  bdxcdy = bdx * cdy;
+  cdxbdy = cdx * bdy;
+
+  cdxady = cdx * ady;
+  adxcdy = adx * cdy;
+
+  adxbdy = adx * bdy;
+  bdxady = bdx * ady;
+
+  det = adheight * (bdxcdy - cdxbdy) 
+      + bdheight * (cdxady - adxcdy)
+      + cdheight * (adxbdy - bdxady);
+
+  if (b->noexact) {
+    return det;
+  }
+
+  permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * Absolute(adheight)
+            + (Absolute(cdxady) + Absolute(adxcdy)) * Absolute(bdheight)
+            + (Absolute(adxbdy) + Absolute(bdxady)) * Absolute(cdheight);
+  errbound = o3derrboundA * permanent;
+  if ((det > errbound) || (-det > errbound)) {
+    return det;
+  }
+
+  return orient3dadapt(pa, pb, pc, pd, aheight, bheight, cheight, dheight,
+                       permanent);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  nonregular()   Return a positive value if the point pd is incompatible   */
+/*                 with the circle or plane passing through pa, pb, and pc   */
+/*                 (meaning that pd is inside the circle or below the        */
+/*                 plane); a negative value if it is compatible; and zero if */
+/*                 the four points are cocircular/coplanar.  The points pa,  */
+/*                 pb, and pc must be in counterclockwise order, or the sign */
+/*                 of the result will be reversed.                           */
+/*                                                                           */
+/*  If the -w switch is used, the points are lifted onto the parabolic       */
+/*  lifting map, then they are dropped according to their weights, then the  */
+/*  3D orientation test is applied.  If the -W switch is used, the points'   */
+/*  heights are already provided, so the 3D orientation test is applied      */
+/*  directly.  If neither switch is used, the incircle test is applied.      */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+REAL nonregular(struct mesh *m, struct behavior *b,
+                vertex pa, vertex pb, vertex pc, vertex pd)
+#else /* not ANSI_DECLARATORS */
+REAL nonregular(m, b, pa, pb, pc, pd)
+struct mesh *m;
+struct behavior *b;
+vertex pa;
+vertex pb;
+vertex pc;
+vertex pd;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  if (b->weighted == 0) {
+    return incircle(m, b, pa, pb, pc, pd);
+  } else if (b->weighted == 1) {
+    return orient3d(m, b, pa, pb, pc, pd,
+                    pa[0] * pa[0] + pa[1] * pa[1] - pa[2],
+                    pb[0] * pb[0] + pb[1] * pb[1] - pb[2],
+                    pc[0] * pc[0] + pc[1] * pc[1] - pc[2],
+                    pd[0] * pd[0] + pd[1] * pd[1] - pd[2]);
+  } else {
+    return orient3d(m, b, pa, pb, pc, pd, pa[2], pb[2], pc[2], pd[2]);
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  findcircumcenter()   Find the circumcenter of a triangle.                */
+/*                                                                           */
+/*  The result is returned both in terms of x-y coordinates and xi-eta       */
+/*  (barycentric) coordinates.  The xi-eta coordinate system is defined in   */
+/*  terms of the triangle:  the origin of the triangle is the origin of the  */
+/*  coordinate system; the destination of the triangle is one unit along the */
+/*  xi axis; and the apex of the triangle is one unit along the eta axis.    */
+/*  This procedure also returns the square of the length of the triangle's   */
+/*  shortest edge.                                                           */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void findcircumcenter(struct mesh *m, struct behavior *b,
+                      vertex torg, vertex tdest, vertex tapex,
+                      vertex circumcenter, REAL *xi, REAL *eta, REAL *minedge,
+                      int offcenter)
+#else /* not ANSI_DECLARATORS */
+void findcircumcenter(m, b, torg, tdest, tapex, circumcenter, xi, eta, minedge,
+                      offcenter)
+struct mesh *m;
+struct behavior *b;
+vertex torg;
+vertex tdest;
+vertex tapex;
+vertex circumcenter;
+REAL *xi;
+REAL *eta;
+REAL *minedge;
+int offcenter;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  REAL xdo, ydo, xao, yao;
+  REAL dodist, aodist, dadist;
+  REAL denominator;
+  REAL dx, dy, dxoff, dyoff;
+
+  m->circumcentercount++;
+
+  /* Compute the circumcenter of the triangle. */
+  xdo = tdest[0] - torg[0];
+  ydo = tdest[1] - torg[1];
+  xao = tapex[0] - torg[0];
+  yao = tapex[1] - torg[1];
+  dodist = xdo * xdo + ydo * ydo;
+  aodist = xao * xao + yao * yao;
+  dadist = (tdest[0] - tapex[0]) * (tdest[0] - tapex[0]) +
+           (tdest[1] - tapex[1]) * (tdest[1] - tapex[1]);
+  if (b->noexact) {
+    denominator = 0.5 / (xdo * yao - xao * ydo);
+  } else {
+    /* Use the counterclockwise() routine to ensure a positive (and */
+    /*   reasonably accurate) result, avoiding any possibility of   */
+    /*   division by zero.                                          */
+    denominator = 0.5 / counterclockwise(m, b, tdest, tapex, torg);
+    /* Don't count the above as an orientation test. */
+    m->counterclockcount--;
+  }
+  dx = (yao * dodist - ydo * aodist) * denominator;
+  dy = (xdo * aodist - xao * dodist) * denominator;
+
+  /* Find the (squared) length of the triangle's shortest edge.  This   */
+  /*   serves as a conservative estimate of the insertion radius of the */
+  /*   circumcenter's parent.  The estimate is used to ensure that      */
+  /*   the algorithm terminates even if very small angles appear in     */
+  /*   the input PSLG.                                                  */
+  if ((dodist < aodist) && (dodist < dadist)) {
+    *minedge = dodist;
+    if (offcenter && (b->offconstant > 0.0)) {
+      /* Find the position of the off-center, as described by Alper Ungor. */
+      dxoff = 0.5 * xdo - b->offconstant * ydo;
+      dyoff = 0.5 * ydo + b->offconstant * xdo;
+      /* If the off-center is closer to the origin than the */
+      /*   circumcenter, use the off-center instead.        */
+      if (dxoff * dxoff + dyoff * dyoff < dx * dx + dy * dy) {
+        dx = dxoff;
+        dy = dyoff;
+      }
+    }
+  } else if (aodist < dadist) {
+    *minedge = aodist;
+    if (offcenter && (b->offconstant > 0.0)) {
+      dxoff = 0.5 * xao + b->offconstant * yao;
+      dyoff = 0.5 * yao - b->offconstant * xao;
+      /* If the off-center is closer to the origin than the */
+      /*   circumcenter, use the off-center instead.        */
+      if (dxoff * dxoff + dyoff * dyoff < dx * dx + dy * dy) {
+        dx = dxoff;
+        dy = dyoff;
+      }
+    }
+  } else {
+    *minedge = dadist;
+    if (offcenter && (b->offconstant > 0.0)) {
+      dxoff = 0.5 * (tapex[0] - tdest[0]) -
+              b->offconstant * (tapex[1] - tdest[1]);
+      dyoff = 0.5 * (tapex[1] - tdest[1]) +
+              b->offconstant * (tapex[0] - tdest[0]);
+      /* If the off-center is closer to the destination than the */
+      /*   circumcenter, use the off-center instead.             */
+      if (dxoff * dxoff + dyoff * dyoff <
+          (dx - xdo) * (dx - xdo) + (dy - ydo) * (dy - ydo)) {
+        dx = xdo + dxoff;
+        dy = ydo + dyoff;
+      }
+    }
+  }
+
+  circumcenter[0] = torg[0] + dx;
+  circumcenter[1] = torg[1] + dy;
+
+  /* To interpolate vertex attributes for the new vertex inserted at */
+  /*   the circumcenter, define a coordinate system with a xi-axis,  */
+  /*   directed from the triangle's origin to its destination, and   */
+  /*   an eta-axis, directed from its origin to its apex.            */
+  /*   Calculate the xi and eta coordinates of the circumcenter.     */
+  *xi = (yao * dx - xao * dy) * (2.0 * denominator);
+  *eta = (xdo * dy - ydo * dx) * (2.0 * denominator);
+}
+
+/**                                                                         **/
+/**                                                                         **/
+/********* Geometric primitives end here                             *********/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  triangleinit()   Initialize some variables.                              */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void triangleinit(struct mesh *m)
+#else /* not ANSI_DECLARATORS */
+void triangleinit(m)
+struct mesh *m;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  m->vertices.maxitems = m->triangles.maxitems = m->subsegs.maxitems =
+    m->viri.maxitems = m->badsubsegs.maxitems = m->badtriangles.maxitems =
+    m->flipstackers.maxitems = m->splaynodes.maxitems = 0l;
+  m->vertices.itembytes = m->triangles.itembytes = m->subsegs.itembytes =
+    m->viri.itembytes = m->badsubsegs.itembytes = m->badtriangles.itembytes =
+    m->flipstackers.itembytes = m->splaynodes.itembytes = 0;
+  m->recenttri.tri = (triangle *) NULL; /* No triangle has been visited yet. */
+  m->undeads = 0;                       /* No eliminated input vertices yet. */
+  m->samples = 1;         /* Point location should take at least one sample. */
+  m->checksegments = 0;   /* There are no segments in the triangulation yet. */
+  m->checkquality = 0;     /* The quality triangulation stage has not begun. */
+  m->incirclecount = m->counterclockcount = m->orient3dcount = 0;
+  m->hyperbolacount = m->circletopcount = m->circumcentercount = 0;
+  randomseed = 1;
+
+  exactinit();                     /* Initialize exact arithmetic constants. */
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  randomnation()   Generate a random number between 0 and `choices' - 1.   */
+/*                                                                           */
+/*  This is a simple linear congruential random number generator.  Hence, it */
+/*  is a bad random number generator, but good enough for most randomized    */
+/*  geometric algorithms.                                                    */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+unsigned long randomnation(unsigned int choices)
+#else /* not ANSI_DECLARATORS */
+unsigned long randomnation(choices)
+unsigned int choices;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  randomseed = (randomseed * 1366l + 150889l) % 714025l;
+  return randomseed / (714025l / choices + 1);
+}
+
+/********* Mesh quality testing routines begin here                  *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  checkmesh()   Test the mesh for topological consistency.                 */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+void checkmesh(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void checkmesh(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri triangleloop;
+  struct otri oppotri, oppooppotri;
+  vertex triorg, tridest, triapex;
+  vertex oppoorg, oppodest;
+  int horrors;
+  int saveexact;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+
+  /* Temporarily turn on exact arithmetic if it's off. */
+  saveexact = b->noexact;
+  b->noexact = 0;
+  if (!b->quiet) {
+    printf("  Checking consistency of mesh...\n");
+  }
+  horrors = 0;
+  /* Run through the list of triangles, checking each one. */
+  traversalinit(&m->triangles);
+  triangleloop.tri = triangletraverse(m);
+  while (triangleloop.tri != (triangle *) NULL) {
+    /* Check all three edges of the triangle. */
+    for (triangleloop.orient = 0; triangleloop.orient < 3;
+         triangleloop.orient++) {
+      org(triangleloop, triorg);
+      dest(triangleloop, tridest);
+      if (triangleloop.orient == 0) {       /* Only test for inversion once. */
+        /* Test if the triangle is flat or inverted. */
+        apex(triangleloop, triapex);
+        if (counterclockwise(m, b, triorg, tridest, triapex) <= 0.0) {
+          printf("  !! !! Inverted ");
+          printtriangle(m, b, &triangleloop);
+          horrors++;
+        }
+      }
+      /* Find the neighboring triangle on this edge. */
+      sym(triangleloop, oppotri);
+      if (oppotri.tri != m->dummytri) {
+        /* Check that the triangle's neighbor knows it's a neighbor. */
+        sym(oppotri, oppooppotri);
+        if ((triangleloop.tri != oppooppotri.tri)
+            || (triangleloop.orient != oppooppotri.orient)) {
+          printf("  !! !! Asymmetric triangle-triangle bond:\n");
+          if (triangleloop.tri == oppooppotri.tri) {
+            printf("   (Right triangle, wrong orientation)\n");
+          }
+          printf("    First ");
+          printtriangle(m, b, &triangleloop);
+          printf("    Second (nonreciprocating) ");
+          printtriangle(m, b, &oppotri);
+          horrors++;
+        }
+        /* Check that both triangles agree on the identities */
+        /*   of their shared vertices.                       */
+        org(oppotri, oppoorg);
+        dest(oppotri, oppodest);
+        if ((triorg != oppodest) || (tridest != oppoorg)) {
+          printf("  !! !! Mismatched edge coordinates between two triangles:\n"
+                 );
+          printf("    First mismatched ");
+          printtriangle(m, b, &triangleloop);
+          printf("    Second mismatched ");
+          printtriangle(m, b, &oppotri);
+          horrors++;
+        }
+      }
+    }
+    triangleloop.tri = triangletraverse(m);
+  }
+  if (horrors == 0) {
+    if (!b->quiet) {
+      printf("  In my studied opinion, the mesh appears to be consistent.\n");
+    }
+  } else if (horrors == 1) {
+    printf("  !! !! !! !! Precisely one festering wound discovered.\n");
+  } else {
+    printf("  !! !! !! !! %d abominations witnessed.\n", horrors);
+  }
+  /* Restore the status of exact arithmetic. */
+  b->noexact = saveexact;
+}
+
+#endif /* not REDUCED */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  checkdelaunay()   Ensure that the mesh is (constrained) Delaunay.        */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+void checkdelaunay(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void checkdelaunay(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri triangleloop;
+  struct otri oppotri;
+  struct osub opposubseg;
+  vertex triorg, tridest, triapex;
+  vertex oppoapex;
+  int shouldbedelaunay;
+  int horrors;
+  int saveexact;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  /* Temporarily turn on exact arithmetic if it's off. */
+  saveexact = b->noexact;
+  b->noexact = 0;
+  if (!b->quiet) {
+    printf("  Checking Delaunay property of mesh...\n");
+  }
+  horrors = 0;
+  /* Run through the list of triangles, checking each one. */
+  traversalinit(&m->triangles);
+  triangleloop.tri = triangletraverse(m);
+  while (triangleloop.tri != (triangle *) NULL) {
+    /* Check all three edges of the triangle. */
+    for (triangleloop.orient = 0; triangleloop.orient < 3;
+         triangleloop.orient++) {
+      org(triangleloop, triorg);
+      dest(triangleloop, tridest);
+      apex(triangleloop, triapex);
+      sym(triangleloop, oppotri);
+      apex(oppotri, oppoapex);
+      /* Only test that the edge is locally Delaunay if there is an   */
+      /*   adjoining triangle whose pointer is larger (to ensure that */
+      /*   each pair isn't tested twice).                             */
+      shouldbedelaunay = (oppotri.tri != m->dummytri) &&
+            !deadtri(oppotri.tri) && (triangleloop.tri < oppotri.tri) &&
+            (triorg != m->infvertex1) && (triorg != m->infvertex2) &&
+            (triorg != m->infvertex3) &&
+            (tridest != m->infvertex1) && (tridest != m->infvertex2) &&
+            (tridest != m->infvertex3) &&
+            (triapex != m->infvertex1) && (triapex != m->infvertex2) &&
+            (triapex != m->infvertex3) &&
+            (oppoapex != m->infvertex1) && (oppoapex != m->infvertex2) &&
+            (oppoapex != m->infvertex3);
+      if (m->checksegments && shouldbedelaunay) {
+        /* If a subsegment separates the triangles, then the edge is */
+        /*   constrained, so no local Delaunay test should be done.  */
+        tspivot(triangleloop, opposubseg);
+        if (opposubseg.ss != m->dummysub){
+          shouldbedelaunay = 0;
+        }
+      }
+      if (shouldbedelaunay) {
+        if (nonregular(m, b, triorg, tridest, triapex, oppoapex) > 0.0) {
+          if (!b->weighted) {
+            printf("  !! !! Non-Delaunay pair of triangles:\n");
+            printf("    First non-Delaunay ");
+            printtriangle(m, b, &triangleloop);
+            printf("    Second non-Delaunay ");
+          } else {
+            printf("  !! !! Non-regular pair of triangles:\n");
+            printf("    First non-regular ");
+            printtriangle(m, b, &triangleloop);
+            printf("    Second non-regular ");
+          }
+          printtriangle(m, b, &oppotri);
+          horrors++;
+        }
+      }
+    }
+    triangleloop.tri = triangletraverse(m);
+  }
+  if (horrors == 0) {
+    if (!b->quiet) {
+      printf(
+  "  By virtue of my perceptive intelligence, I declare the mesh Delaunay.\n");
+    }
+  } else if (horrors == 1) {
+    printf(
+         "  !! !! !! !! Precisely one terrifying transgression identified.\n");
+  } else {
+    printf("  !! !! !! !! %d obscenities viewed with horror.\n", horrors);
+  }
+  /* Restore the status of exact arithmetic. */
+  b->noexact = saveexact;
+}
+
+#endif /* not REDUCED */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  enqueuebadtriang()   Add a bad triangle data structure to the end of a   */
+/*                       queue.                                              */
+/*                                                                           */
+/*  The queue is actually a set of 64 queues.  I use multiple queues to give */
+/*  priority to smaller angles.  I originally implemented a heap, but the    */
+/*  queues are faster by a larger margin than I'd suspected.                 */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void enqueuebadtriang(struct mesh *m, struct behavior *b,
+                      struct badtriang *badtri)
+#else /* not ANSI_DECLARATORS */
+void enqueuebadtriang(m, b, badtri)
+struct mesh *m;
+struct behavior *b;
+struct badtriang *badtri;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  int queuenumber;
+  int i;
+
+  if (b->verbose > 2) {
+    printf("  Queueing bad triangle:\n");
+    printf("    (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n",
+           badtri->triangorg[0], badtri->triangorg[1],
+           badtri->triangdest[0], badtri->triangdest[1],
+           badtri->triangapex[0], badtri->triangapex[1]);
+  }
+  /* Determine the appropriate queue to put the bad triangle into. */
+  if (badtri->key > 0.6) {
+    queuenumber = (int) (160.0 * (badtri->key - 0.6));
+    if (queuenumber > 63) {
+      queuenumber = 63;
+    }
+  } else {
+    /* It's not a bad angle; put the triangle in the lowest-priority queue. */
+    queuenumber = 0;
+  }
+
+  /* Are we inserting into an empty queue? */
+  if (m->queuefront[queuenumber] == (struct badtriang *) NULL) {
+    /* Yes, we are inserting into an empty queue.     */
+    /*   Will this become the highest-priority queue? */
+    if (queuenumber > m->firstnonemptyq) {
+      /* Yes, this is the highest-priority queue. */
+      m->nextnonemptyq[queuenumber] = m->firstnonemptyq;
+      m->firstnonemptyq = queuenumber;
+    } else {
+      /* No, this is not the highest-priority queue. */
+      /*   Find the queue with next higher priority. */
+      i = queuenumber + 1;
+      while (m->queuefront[i] == (struct badtriang *) NULL) {
+        i++;
+      }
+      /* Mark the newly nonempty queue as following a higher-priority queue. */
+      m->nextnonemptyq[queuenumber] = m->nextnonemptyq[i];
+      m->nextnonemptyq[i] = queuenumber;
+    }
+    /* Put the bad triangle at the beginning of the (empty) queue. */
+    m->queuefront[queuenumber] = badtri;
+  } else {
+    /* Add the bad triangle to the end of an already nonempty queue. */
+    m->queuetail[queuenumber]->nexttriang = badtri;
+  }
+  /* Maintain a pointer to the last triangle of the queue. */
+  m->queuetail[queuenumber] = badtri;
+  /* Newly enqueued bad triangle has no successor in the queue. */
+  badtri->nexttriang = (struct badtriang *) NULL;
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  enqueuebadtri()   Add a bad triangle to the end of a queue.              */
+/*                                                                           */
+/*  Allocates a badtriang data structure for the triangle, then passes it to */
+/*  enqueuebadtriang().                                                      */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void enqueuebadtri(struct mesh *m, struct behavior *b, struct otri *enqtri,
+                   REAL angle, vertex enqapex, vertex enqorg, vertex enqdest)
+#else /* not ANSI_DECLARATORS */
+void enqueuebadtri(m, b, enqtri, angle, enqapex, enqorg, enqdest)
+struct mesh *m;
+struct behavior *b;
+struct otri *enqtri;
+REAL angle;
+vertex enqapex;
+vertex enqorg;
+vertex enqdest;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct badtriang *newbad;
+
+  /* Allocate space for the bad triangle. */
+  newbad = (struct badtriang *) poolalloc(&m->badtriangles);
+  newbad->poortri = encode(*enqtri);
+  newbad->key = angle;
+  newbad->triangapex = enqapex;
+  newbad->triangorg = enqorg;
+  newbad->triangdest = enqdest;
+  enqueuebadtriang(m, b, newbad);
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  dequeuebadtriang()   Remove a triangle from the front of the queue.      */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+struct badtriang *dequeuebadtriang(struct mesh *m)
+#else /* not ANSI_DECLARATORS */
+struct badtriang *dequeuebadtriang(m)
+struct mesh *m;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct badtriang *result;
+
+  /* If no queues are nonempty, return NULL. */
+  if (m->firstnonemptyq < 0) {
+    return (struct badtriang *) NULL;
+  }
+  /* Find the first triangle of the highest-priority queue. */
+  result = m->queuefront[m->firstnonemptyq];
+  /* Remove the triangle from the queue. */
+  m->queuefront[m->firstnonemptyq] = result->nexttriang;
+  /* If this queue is now empty, note the new highest-priority */
+  /*   nonempty queue.                                         */
+  if (result == m->queuetail[m->firstnonemptyq]) {
+    m->firstnonemptyq = m->nextnonemptyq[m->firstnonemptyq];
+  }
+  return result;
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  under60degrees()   Return 1 if the two incident input segments are       */
+/*                     separated by an angle less than 60 degrees;           */
+/*                     0 otherwise.                                          */
+/*                                                                           */
+/*  The two input segments MUST have the same origin.                        */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+int under60degrees(struct osub *sub1, struct osub *sub2) {
+  vertex segmentapex, v1, v2;
+  REAL dotprod;
+
+  sorg(*sub1, segmentapex);
+  sdest(*sub1, v1);
+  sdest(*sub2, v2);
+  dotprod = (v2[0] - segmentapex[0]) * (v1[0] - segmentapex[0]) +
+            (v2[1] - segmentapex[1]) * (v1[1] - segmentapex[1]);
+  return (dotprod > 0.0) &&
+         (4.0 * dotprod * dotprod >
+          ((v1[0] - segmentapex[0]) * (v1[0] - segmentapex[0]) +
+           (v1[1] - segmentapex[1]) * (v1[1] - segmentapex[1])) *
+          ((v2[0] - segmentapex[0]) * (v2[0] - segmentapex[0]) +
+           (v2[1] - segmentapex[1]) * (v2[1] - segmentapex[1])));
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  clockwiseseg()   Find the next segment clockwise from `thissub' having   */
+/*                   the same origin and return it as `nextsub' if the       */
+/*                   intervening region is inside the domain.                */
+/*                                                                           */
+/*  Returns 1 if the next segment is separated from `thissub' by less than   */
+/*  60 degrees, and the intervening region is inside the domain.             */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+int clockwiseseg(struct mesh *m, struct osub *thissub, struct osub *nextsub) {
+  struct otri neighbortri;
+  triangle ptr;           /* Temporary variable used by sym() and stpivot(). */
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  stpivot(*thissub, neighbortri);
+  if (neighbortri.tri == m->dummytri) {
+    return 0;
+  } else {
+    lnextself(neighbortri);
+    tspivot(neighbortri, *nextsub);
+    while (nextsub->ss == m->dummysub) {
+      symself(neighbortri);
+      lnextself(neighbortri);
+      tspivot(neighbortri, *nextsub);
+    }
+    ssymself(*nextsub);
+    return under60degrees(thissub, nextsub);
+  }
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  counterclockwiseseg()   Find the next segment counterclockwise from      */
+/*                          `thissub' having the same origin and return it   */
+/*                          as `nextsub' if the intervening region is inside */
+/*                          the domain.                                      */
+/*                                                                           */
+/*  Returns 1 if the next segment is separated from `thissub' by less than   */
+/*  60 degrees, and the intervening region is inside the domain.             */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+int counterclockwiseseg(struct mesh *m, struct osub *thissub,
+                        struct osub *nextsub) {
+  struct otri neighbortri;
+  struct osub subsym;
+  triangle ptr;           /* Temporary variable used by sym() and stpivot(). */
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  ssym(*thissub, subsym);
+  stpivot(subsym, neighbortri);
+  if (neighbortri.tri == m->dummytri) {
+    return 0;
+  } else {
+    lprevself(neighbortri);
+    tspivot(neighbortri, *nextsub);
+    while (nextsub->ss == m->dummysub) {
+      symself(neighbortri);
+      lprevself(neighbortri);
+      tspivot(neighbortri, *nextsub);
+    }
+    return under60degrees(thissub, nextsub);
+  }
+}
+
+#endif /* not CDT_ONLY */
+
+#ifndef CDT_ONLY
+
+/*****************************************************************************/
+/*                                                                           */
+/*  splitpermitted()   Return 1 if `testsubseg' is part of a subsegment      */
+/*                     cluster that is eligible for splitting.               */
+/*                                                                           */
+/*  The term "subsegment cluster" is formally defined in my paper "Mesh      */
+/*  Generation for Domains with Small Angles."  The algorithm that uses this */
+/*  procedure is also described there.                                       */
+/*                                                                           */
+/*  A subsegment cluster is eligible for splitting if (1) it includes a      */
+/*  subsegment whose length is not a power of two, (2) its subsegments are   */
+/*  not all the same length, or (3) no new edge that will be created by      */
+/*  splitting all the subsegments in the cluster has a length shorter than   */
+/*  the insertion radius of the encroaching vertex, whose square is given    */
+/*  as the parameter `iradius'.  Note that the shortest edges created by     */
+/*  splitting a cluster are those whose endpoints are both subsegment        */
+/*  midpoints introduced when the cluster is split.                          */
+/*                                                                           */
+/*  `testsubseg' is also eligible for splitting (and a 1 will be returned)   */
+/*  if it is part of two subsegment clusters; one at its origin and one at   */
+/*  its destination.                                                         */
+/*                                                                           */
+/*****************************************************************************/
+
+int splitpermitted(struct mesh *m, struct osub *testsubseg, REAL iradius) {
+  struct osub cwsubseg, ccwsubseg, cwsubseg2, ccwsubseg2;
+  struct osub testsym;
+  struct osub startsubseg, nowsubseg;
+  vertex suborg, dest1, dest2;
+  REAL nearestpoweroffour, seglength, prevseglength, edgelength;
+  int cwsmall, ccwsmall, cwsmall2, ccwsmall2;
+  int orgcluster, destcluster;
+  int toosmall;
+
+  /* Find the square of the subsegment's length, and the nearest power of */
+  /*   four (which is the square of the nearest power of two to the       */
+  /*   subsegment's length).                                              */
+  sorg(*testsubseg, suborg);
+  sdest(*testsubseg, dest1);
+  seglength = (dest1[0] - suborg[0]) * (dest1[0] - suborg[0]) +
+              (dest1[1] - suborg[1]) * (dest1[1] - suborg[1]);
+  nearestpoweroffour = 1.0;
+  while (seglength > 2.0 * nearestpoweroffour) {
+    nearestpoweroffour *= 4.0;
+  }
+  while (seglength < 0.5 * nearestpoweroffour) {
+    nearestpoweroffour *= 0.25;
+  }
+  /* If the segment's length is not a power of two, the segment */
+  /*   is eligible for splitting.                               */
+  if ((nearestpoweroffour > 1.001 * seglength) ||
+      (nearestpoweroffour < 0.999 * seglength)) {
+    return 1;
+  }
+
+  /* Is `testsubseg' part of a subsegment cluster at its origin? */
+  cwsmall = clockwiseseg(m, testsubseg, &cwsubseg);
+  ccwsmall = cwsmall ? 0 : counterclockwiseseg(m, testsubseg, &ccwsubseg);
+  orgcluster = cwsmall || ccwsmall;
+
+  /* Is `testsubseg' part of a subsegment cluster at its destination? */
+  ssym(*testsubseg, testsym);
+  cwsmall2 = clockwiseseg(m, &testsym, &cwsubseg2);
+  ccwsmall2 = cwsmall2 ? 0 : counterclockwiseseg(m, &testsym, &ccwsubseg2);
+  destcluster = cwsmall2 || ccwsmall2;
+
+  if (orgcluster == destcluster) {
+    /* `testsubseg' is part of two clusters or none, */
+    /*   and thus should be split.                   */
+    return 1;
+  } else if (orgcluster) {
+    /* `testsubseg' is part of a cluster at its origin. */
+    subsegcopy(*testsubseg, startsubseg);
+  } else {
+    /* `testsubseg' is part of a cluster at its destination; switch to */
+    /*   the symmetric case, so we can use the same code to handle it. */
+    subsegcopy(testsym, startsubseg);
+    subsegcopy(cwsubseg2, cwsubseg);
+    subsegcopy(ccwsubseg2, ccwsubseg);
+    cwsmall = cwsmall2;
+    ccwsmall = ccwsmall2;
+  }
+
+  toosmall = 0;
+  if (cwsmall) {
+    /* Check the subsegment(s) clockwise from `testsubseg'. */
+    subsegcopy(startsubseg, nowsubseg);
+    sorg(nowsubseg, suborg);
+    sdest(nowsubseg, dest1);
+    prevseglength = nearestpoweroffour;
+    do {
+      /* Is the next subsegment shorter than `startsubseg'? */
+      sdest(cwsubseg, dest2);
+      seglength = (dest2[0] - suborg[0]) * (dest2[0] - suborg[0]) +
+                  (dest2[1] - suborg[1]) * (dest2[1] - suborg[1]);
+      if (nearestpoweroffour > 1.001 * seglength) {
+        /* It's shorter; it's safe to split `startsubseg'. */
+        return 1;
+      }
+      /* If the current and previous subsegments are split to a length  */
+      /*   half that of `startsubseg' (which is a likely consequence if */
+      /*   `startsubseg' is split), what will be (the square of) the    */
+      /*   length of the free edge between the splitting vertices?      */
+      edgelength = 0.5 * nearestpoweroffour *
+                   (1 - (((dest1[0] - suborg[0]) * (dest2[0] - suborg[0]) +
+                          (dest1[1] - suborg[1]) * (dest2[1] - suborg[1])) /
+                         sqrt(prevseglength * seglength)));
+      if (edgelength < iradius) {
+        /* If this cluster is split, the new edge dest1-dest2 will be     */
+        /*   smaller than the insertion radius of the encroaching vertex. */
+        /*   Hence, we'd prefer to avoid splitting it if possible.        */
+        toosmall = 1;
+      }
+      if (cwsubseg.ss == startsubseg.ss) {
+        /* We've gone all the way around the vertex.  Split the cluster */
+        /*   if no edges will be too short.                             */
+        return !toosmall;
+      }
+
+      /* Find the next subsegment clockwise around the vertex. */
+      subsegcopy(cwsubseg, nowsubseg);
+      dest1 = dest2;
+      prevseglength = seglength;
+      cwsmall = clockwiseseg(m, &nowsubseg, &cwsubseg);
+    } while (cwsmall);
+
+    /* Prepare to start searching counterclockwise from */
+    /*   the starting subsegment.                       */
+    ccwsmall = counterclockwiseseg(m, &startsubseg, &ccwsubseg);
+  }
+
+  if (ccwsmall) {
+    /* Check the subsegment(s) counterclockwise from `testsubseg'. */
+    subsegcopy(startsubseg, nowsubseg);
+    sorg(nowsubseg, suborg);
+    sdest(nowsubseg, dest1);
+    prevseglength = nearestpoweroffour;
+    do {
+      /* Is the next subsegment shorter than `startsubseg'? */
+      sdest(ccwsubseg, dest2);
+      seglength = (dest2[0] - suborg[0]) * (dest2[0] - suborg[0]) +
+                  (dest2[1] - suborg[1]) * (dest2[1] - suborg[1]);
+      if (nearestpoweroffour > 1.001 * seglength) {
+        /* It's shorter; it's safe to split `startsubseg'. */
+        return 1;
+      }
+      /*   half that of `startsubseg' (which is a likely consequence if */
+      /*   `startsubseg' is split), what will be (the square of) the    */
+      /*   length of the free edge between the splitting vertices?      */
+      edgelength = 0.5 * nearestpoweroffour *
+                   (1 - (((dest1[0] - suborg[0]) * (dest2[0] - suborg[0]) +
+                          (dest1[1] - suborg[1]) * (dest2[1] - suborg[1])) /
+                         sqrt(prevseglength * seglength)));
+      if (edgelength < iradius) {
+        /* If this cluster is split, the new edge dest1-dest2 will be     */
+        /*   smaller than the insertion radius of the encroaching vertex. */
+        /*   Hence, we'd prefer to avoid splitting it if possible.        */
+        toosmall = 1;
+      }
+      if (ccwsubseg.ss == startsubseg.ss) {
+        /* We've gone all the way around the vertex.  Split the cluster */
+        /*   if no edges will be too short.                             */
+        return !toosmall;
+      }
+
+      /* Find the next subsegment counterclockwise around the vertex. */
+      subsegcopy(ccwsubseg, nowsubseg);
+      dest1 = dest2;
+      prevseglength = seglength;
+      ccwsmall = counterclockwiseseg(m, &nowsubseg, &ccwsubseg);
+    } while (ccwsmall);
+  }
+
+  /* We've found every subsegment in the cluster.  Split the cluster */
+  /*   if no edges will be too short.                                */
+  return !toosmall;
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  checkseg4encroach()   Check a subsegment to see if it is encroached; add */
+/*                        it to the list if it is.                           */
+/*                                                                           */
+/*  A subsegment is encroached if there is a vertex in its diametral circle  */
+/*  (that is, the subsegment faces an angle greater than 90 degrees).  This  */
+/*  definition is due to Ruppert.                                            */
+/*                                                                           */
+/*  Returns a nonzero value if the subsegment is encroached.                 */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+int checkseg4encroach(struct mesh *m, struct behavior *b,
+                      struct osub *testsubseg, REAL iradius)
+#else /* not ANSI_DECLARATORS */
+int checkseg4encroach(m, b, testsubseg, iradius)
+struct mesh *m;
+struct behavior *b;
+struct osub *testsubseg;
+REAL iradius;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri neighbortri;
+  struct osub testsym;
+  struct badsubseg *encroachedseg;
+  REAL dotproduct;
+  int encroached;
+  int sides;
+  int enq;
+  vertex eorg, edest, eapex;
+  triangle ptr;                     /* Temporary variable used by stpivot(). */
+
+  encroached = 0;
+  sides = 0;
+
+  sorg(*testsubseg, eorg);
+  sdest(*testsubseg, edest);
+  /* Check one neighbor of the subsegment. */
+  stpivot(*testsubseg, neighbortri);
+  /* Does the neighbor exist, or is this a boundary edge? */
+  if (neighbortri.tri != m->dummytri) {
+    sides++;
+    /* Find a vertex opposite this subsegment. */
+    apex(neighbortri, eapex);
+    /* Check whether the apex is in the diametral lens of the subsegment */
+    /*   (or the diametral circle, if `nolenses' is set).  A dot product */
+    /*   of two sides of the triangle is used to check whether the angle */
+    /*   at the apex is greater than 120 degrees (for lenses; 90 degrees */
+    /*   for diametral circles).                                         */
+    dotproduct = (eorg[0] - eapex[0]) * (edest[0] - eapex[0]) +
+                 (eorg[1] - eapex[1]) * (edest[1] - eapex[1]);
+    if (dotproduct < 0.0) {
+      if (b->nolenses ||
+          (dotproduct * dotproduct >=
+           0.25 * ((eorg[0] - eapex[0]) * (eorg[0] - eapex[0]) +
+                   (eorg[1] - eapex[1]) * (eorg[1] - eapex[1])) *
+                  ((edest[0] - eapex[0]) * (edest[0] - eapex[0]) +
+                   (edest[1] - eapex[1]) * (edest[1] - eapex[1])))) {
+        encroached = 1;
+      }
+    }
+  }
+  /* Check the other neighbor of the subsegment. */
+  ssym(*testsubseg, testsym);
+  stpivot(testsym, neighbortri);
+  /* Does the neighbor exist, or is this a boundary edge? */
+  if (neighbortri.tri != m->dummytri) {
+    sides++;
+    /* Find the other vertex opposite this subsegment. */
+    apex(neighbortri, eapex);
+    /* Check whether the apex is in the diametral lens of the subsegment */
+    /*   (or the diametral circle, if `nolenses' is set).                */
+    dotproduct = (eorg[0] - eapex[0]) * (edest[0] - eapex[0]) +
+                 (eorg[1] - eapex[1]) * (edest[1] - eapex[1]);
+    if (dotproduct < 0.0) {
+      if (b->nolenses ||
+          (dotproduct * dotproduct >=
+           0.25 * ((eorg[0] - eapex[0]) * (eorg[0] - eapex[0]) +
+                   (eorg[1] - eapex[1]) * (eorg[1] - eapex[1])) *
+                  ((edest[0] - eapex[0]) * (edest[0] - eapex[0]) +
+                   (edest[1] - eapex[1]) * (edest[1] - eapex[1])))) {
+        encroached += 2;
+      }
+    }
+  }
+
+  if (encroached && (!b->nobisect || ((b->nobisect == 1) && (sides == 2)))) {
+    /* Decide whether `testsubseg' should be split. */
+    if (iradius > 0.0) {
+      /* The encroaching vertex is a triangle circumcenter, which will be   */
+      /*   rejected.  Hence, `testsubseg' probably should be split, unless  */
+      /*   it is part of a subsegment cluster which, according to the rules */
+      /*   described in my paper "Mesh Generation for Domains with Small    */
+      /*   Angles," should not be split.                                    */
+      enq = splitpermitted(m, testsubseg, iradius);
+    } else {
+      /* The encroaching vertex is an input vertex or was inserted in a */
+      /*   subsegment, so the encroached subsegment must be split.      */
+      enq = 1;
+    }
+    if (enq) {
+      if (b->verbose > 2) {
+        printf(
+          "  Queueing encroached subsegment (%.12g, %.12g) (%.12g, %.12g).\n",
+          eorg[0], eorg[1], edest[0], edest[1]);
+      }
+      /* Add the subsegment to the list of encroached subsegments. */
+      /*   Be sure to get the orientation right.                   */
+      encroachedseg = (struct badsubseg *) poolalloc(&m->badsubsegs);
+      if (encroached == 1) {
+        encroachedseg->encsubseg = sencode(*testsubseg);
+        encroachedseg->subsegorg = eorg;
+        encroachedseg->subsegdest = edest;
+      } else {
+        encroachedseg->encsubseg = sencode(testsym);
+        encroachedseg->subsegorg = edest;
+        encroachedseg->subsegdest = eorg;
+      }
+    }
+  }
+
+  return encroached;
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  testtriangle()   Test a face for quality measures.                       */
+/*                                                                           */
+/*  Tests a triangle to see if it satisfies the minimum angle condition and  */
+/*  the maximum area condition.  Triangles that aren't up to spec are added  */
+/*  to the bad triangle queue.                                               */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void testtriangle(struct mesh *m, struct behavior *b, struct otri *testtri)
+#else /* not ANSI_DECLARATORS */
+void testtriangle(m, b, testtri)
+struct mesh *m;
+struct behavior *b;
+struct otri *testtri;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri sametesttri;
+  struct osub subseg1, subseg2;
+  vertex torg, tdest, tapex;
+  vertex anglevertex;
+  REAL dxod, dyod, dxda, dyda, dxao, dyao;
+  REAL dxod2, dyod2, dxda2, dyda2, dxao2, dyao2;
+  REAL apexlen, orglen, destlen;
+  REAL angle;
+  REAL area;
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  org(*testtri, torg);
+  dest(*testtri, tdest);
+  apex(*testtri, tapex);
+  dxod = torg[0] - tdest[0];
+  dyod = torg[1] - tdest[1];
+  dxda = tdest[0] - tapex[0];
+  dyda = tdest[1] - tapex[1];
+  dxao = tapex[0] - torg[0];
+  dyao = tapex[1] - torg[1];
+  dxod2 = dxod * dxod;
+  dyod2 = dyod * dyod;
+  dxda2 = dxda * dxda;
+  dyda2 = dyda * dyda;
+  dxao2 = dxao * dxao;
+  dyao2 = dyao * dyao;
+  /* Find the lengths of the triangle's three edges. */
+  apexlen = dxod2 + dyod2;
+  orglen = dxda2 + dyda2;
+  destlen = dxao2 + dyao2;
+  if ((apexlen < orglen) && (apexlen < destlen)) {
+    /* The edge opposite the apex is shortest. */
+    /* Find the square of the cosine of the angle at the apex. */
+    angle = dxda * dxao + dyda * dyao;
+    angle = angle * angle / (orglen * destlen);
+    anglevertex = tapex;
+    lnext(*testtri, sametesttri);
+    tspivot(sametesttri, subseg1);
+    lnextself(sametesttri);
+    tspivot(sametesttri, subseg2);
+  } else if (orglen < destlen) {
+    /* The edge opposite the origin is shortest. */
+    /* Find the square of the cosine of the angle at the origin. */
+    angle = dxod * dxao + dyod * dyao;
+    angle = angle * angle / (apexlen * destlen);
+    anglevertex = torg;
+    tspivot(*testtri, subseg1);
+    lprev(*testtri, sametesttri);
+    tspivot(sametesttri, subseg2);
+  } else {
+    /* The edge opposite the destination is shortest. */
+    /* Find the square of the cosine of the angle at the destination. */
+    angle = dxod * dxda + dyod * dyda;
+    angle = angle * angle / (apexlen * orglen);
+    anglevertex = tdest;
+    tspivot(*testtri, subseg1);
+    lnext(*testtri, sametesttri);
+    tspivot(sametesttri, subseg2);
+  }
+
+  /* Check if both edges that form the angle are segments. */
+  if ((subseg1.ss != m->dummysub) && (subseg2.ss != m->dummysub)) {
+    /* The angle is a segment intersection.  Don't add this bad triangle to */
+    /*   the list; there's nothing that can be done about a small angle     */
+    /*   between two segments.                                              */
+    angle = 0.0;
+  }
+
+  /* Check whether the angle is smaller than permitted. */
+  if (angle > b->goodangle) {
+    /* Add this triangle to the list of bad triangles. */
+    enqueuebadtri(m, b, testtri, angle, tapex, torg, tdest);
+    return;
+  }
+
+  if (b->vararea || b->fixedarea || b->usertest) {
+    /* Check whether the area is larger than permitted. */
+    area = 0.5 * (dxod * dyda - dyod * dxda);
+    if (b->fixedarea && (area > b->maxarea)) {
+      /* Add this triangle to the list of bad triangles. */
+      enqueuebadtri(m, b, testtri, angle, tapex, torg, tdest);
+      return;
+    }
+
+    /* Nonpositive area constraints are treated as unconstrained. */
+    if ((b->vararea) && (area > areabound(*testtri)) &&
+        (areabound(*testtri) > 0.0)) {
+      /* Add this triangle to the list of bad triangles. */
+      enqueuebadtri(m, b, testtri, angle, tapex, torg, tdest);
+      return;
+    }
+
+    if (b->usertest) {
+      /* Check whether the user thinks this triangle is too large. */
+      if (triunsuitable(torg, tdest, tapex, area)) {
+        enqueuebadtri(m, b, testtri, angle, tapex, torg, tdest);
+        return;
+      }
+    }
+  }
+}
+
+#endif /* not CDT_ONLY */
+
+/**                                                                         **/
+/**                                                                         **/
+/********* Mesh quality testing routines end here                    *********/
+
+/********* Point location routines begin here                        *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  makevertexmap()   Construct a mapping from vertices to triangles to      */
+/*                    improve the speed of point location for segment        */
+/*                    insertion.                                             */
+/*                                                                           */
+/*  Traverses all the triangles, and provides each corner of each triangle   */
+/*  with a pointer to that triangle.  Of course, pointers will be            */
+/*  overwritten by other pointers because (almost) each vertex is a corner   */
+/*  of several triangles, but in the end every vertex will point to some     */
+/*  triangle that contains it.                                               */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void makevertexmap(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void makevertexmap(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri triangleloop;
+  vertex triorg;
+
+  if (b->verbose) {
+    printf("    Constructing mapping from vertices to triangles.\n");
+  }
+  traversalinit(&m->triangles);
+  triangleloop.tri = triangletraverse(m);
+  while (triangleloop.tri != (triangle *) NULL) {
+    /* Check all three vertices of the triangle. */
+    for (triangleloop.orient = 0; triangleloop.orient < 3;
+         triangleloop.orient++) {
+      org(triangleloop, triorg);
+      setvertex2tri(triorg, encode(triangleloop));
+    }
+    triangleloop.tri = triangletraverse(m);
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  preciselocate()   Find a triangle or edge containing a given point.      */
+/*                                                                           */
+/*  Begins its search from `searchtri'.  It is important that `searchtri'    */
+/*  be a handle with the property that `searchpoint' is strictly to the left */
+/*  of the edge denoted by `searchtri', or is collinear with that edge and   */
+/*  does not intersect that edge.  (In particular, `searchpoint' should not  */
+/*  be the origin or destination of that edge.)                              */
+/*                                                                           */
+/*  These conditions are imposed because preciselocate() is normally used in */
+/*  one of two situations:                                                   */
+/*                                                                           */
+/*  (1)  To try to find the location to insert a new point.  Normally, we    */
+/*       know an edge that the point is strictly to the left of.  In the     */
+/*       incremental Delaunay algorithm, that edge is a bounding box edge.   */
+/*       In Ruppert's Delaunay refinement algorithm for quality meshing,     */
+/*       that edge is the shortest edge of the triangle whose circumcenter   */
+/*       is being inserted.                                                  */
+/*                                                                           */
+/*  (2)  To try to find an existing point.  In this case, any edge on the    */
+/*       convex hull is a good starting edge.  You must screen out the       */
+/*       possibility that the vertex sought is an endpoint of the starting   */
+/*       edge before you call preciselocate().                               */
+/*                                                                           */
+/*  On completion, `searchtri' is a triangle that contains `searchpoint'.    */
+/*                                                                           */
+/*  This implementation differs from that given by Guibas and Stolfi.  It    */
+/*  walks from triangle to triangle, crossing an edge only if `searchpoint'  */
+/*  is on the other side of the line containing that edge.  After entering   */
+/*  a triangle, there are two edges by which one can leave that triangle.    */
+/*  If both edges are valid (`searchpoint' is on the other side of both      */
+/*  edges), one of the two is chosen by drawing a line perpendicular to      */
+/*  the entry edge (whose endpoints are `forg' and `fdest') passing through  */
+/*  `fapex'.  Depending on which side of this perpendicular `searchpoint'    */
+/*  falls on, an exit edge is chosen.                                        */
+/*                                                                           */
+/*  This implementation is empirically faster than the Guibas and Stolfi     */
+/*  point location routine (which I originally used), which tends to spiral  */
+/*  in toward its target.                                                    */
+/*                                                                           */
+/*  Returns ONVERTEX if the point lies on an existing vertex.  `searchtri'   */
+/*  is a handle whose origin is the existing vertex.                         */
+/*                                                                           */
+/*  Returns ONEDGE if the point lies on a mesh edge.  `searchtri' is a       */
+/*  handle whose primary edge is the edge on which the point lies.           */
+/*                                                                           */
+/*  Returns INTRIANGLE if the point lies strictly within a triangle.         */
+/*  `searchtri' is a handle on the triangle that contains the point.         */
+/*                                                                           */
+/*  Returns OUTSIDE if the point lies outside the mesh.  `searchtri' is a    */
+/*  handle whose primary edge the point is to the right of.  This might      */
+/*  occur when the circumcenter of a triangle falls just slightly outside    */
+/*  the mesh due to floating-point roundoff error.  It also occurs when      */
+/*  seeking a hole or region point that a foolish user has placed outside    */
+/*  the mesh.                                                                */
+/*                                                                           */
+/*  If `stopatsubsegment' is nonzero, the search will stop if it tries to    */
+/*  walk through a subsegment, and will return OUTSIDE.                      */
+/*                                                                           */
+/*  WARNING:  This routine is designed for convex triangulations, and will   */
+/*  not generally work after the holes and concavities have been carved.     */
+/*  However, it can still be used to find the circumcenter of a triangle, as */
+/*  long as the search is begun from the triangle in question.               */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+enum locateresult preciselocate(struct mesh *m, struct behavior *b,
+                                vertex searchpoint, struct otri *searchtri,
+                                int stopatsubsegment)
+#else /* not ANSI_DECLARATORS */
+enum locateresult preciselocate(m, b, searchpoint, searchtri, stopatsubsegment)
+struct mesh *m;
+struct behavior *b;
+vertex searchpoint;
+struct otri *searchtri;
+int stopatsubsegment;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri backtracktri;
+  struct osub checkedge;
+  vertex forg, fdest, fapex;
+  REAL orgorient, destorient;
+  int moveleft;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  if (b->verbose > 2) {
+    printf("  Searching for point (%.12g, %.12g).\n",
+           searchpoint[0], searchpoint[1]);
+  }
+  /* Where are we? */
+  org(*searchtri, forg);
+  dest(*searchtri, fdest);
+  apex(*searchtri, fapex);
+  while (1) {
+    if (b->verbose > 2) {
+      printf("    At (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n",
+             forg[0], forg[1], fdest[0], fdest[1], fapex[0], fapex[1]);
+    }
+    /* Check whether the apex is the point we seek. */
+    if ((fapex[0] == searchpoint[0]) && (fapex[1] == searchpoint[1])) {
+      lprevself(*searchtri);
+      return ONVERTEX;
+    }
+    /* Does the point lie on the other side of the line defined by the */
+    /*   triangle edge opposite the triangle's destination?            */
+    destorient = counterclockwise(m, b, forg, fapex, searchpoint);
+    /* Does the point lie on the other side of the line defined by the */
+    /*   triangle edge opposite the triangle's origin?                 */
+    orgorient = counterclockwise(m, b, fapex, fdest, searchpoint);
+    if (destorient > 0.0) {
+      if (orgorient > 0.0) {
+        /* Move left if the inner product of (fapex - searchpoint) and  */
+        /*   (fdest - forg) is positive.  This is equivalent to drawing */
+        /*   a line perpendicular to the line (forg, fdest) and passing */
+        /*   through `fapex', and determining which side of this line   */
+        /*   `searchpoint' falls on.                                    */
+        moveleft = (fapex[0] - searchpoint[0]) * (fdest[0] - forg[0]) +
+                   (fapex[1] - searchpoint[1]) * (fdest[1] - forg[1]) > 0.0;
+      } else {
+        moveleft = 1;
+      }
+    } else {
+      if (orgorient > 0.0) {
+        moveleft = 0;
+      } else {
+        /* The point we seek must be on the boundary of or inside this */
+        /*   triangle.                                                 */
+        if (destorient == 0.0) {
+          lprevself(*searchtri);
+          return ONEDGE;
+        }
+        if (orgorient == 0.0) {
+          lnextself(*searchtri);
+          return ONEDGE;
+        }
+        return INTRIANGLE;
+      }
+    }
+
+    /* Move to another triangle.  Leave a trace `backtracktri' in case */
+    /*   floating-point roundoff or some such bogey causes us to walk  */
+    /*   off a boundary of the triangulation.                          */
+    if (moveleft) {
+      lprev(*searchtri, backtracktri);
+      fdest = fapex;
+    } else {
+      lnext(*searchtri, backtracktri);
+      forg = fapex;
+    }
+    sym(backtracktri, *searchtri);
+
+    if (m->checksegments && stopatsubsegment) {
+      /* Check for walking through a subsegment. */
+      tspivot(backtracktri, checkedge);
+      if (checkedge.ss != m->dummysub) {
+        /* Go back to the last triangle. */
+        otricopy(backtracktri, *searchtri);
+        return OUTSIDE;
+      }
+    }
+    /* Check for walking right out of the triangulation. */
+    if (searchtri->tri == m->dummytri) {
+      /* Go back to the last triangle. */
+      otricopy(backtracktri, *searchtri);
+      return OUTSIDE;
+    }
+
+    apex(*searchtri, fapex);
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  locate()   Find a triangle or edge containing a given point.             */
+/*                                                                           */
+/*  Searching begins from one of:  the input `searchtri', a recently         */
+/*  encountered triangle `recenttri', or from a triangle chosen from a       */
+/*  random sample.  The choice is made by determining which triangle's       */
+/*  origin is closest to the point we are searching for.  Normally,          */
+/*  `searchtri' should be a handle on the convex hull of the triangulation.  */
+/*                                                                           */
+/*  Details on the random sampling method can be found in the Mucke, Saias,  */
+/*  and Zhu paper cited in the header of this code.                          */
+/*                                                                           */
+/*  On completion, `searchtri' is a triangle that contains `searchpoint'.    */
+/*                                                                           */
+/*  Returns ONVERTEX if the point lies on an existing vertex.  `searchtri'   */
+/*  is a handle whose origin is the existing vertex.                         */
+/*                                                                           */
+/*  Returns ONEDGE if the point lies on a mesh edge.  `searchtri' is a       */
+/*  handle whose primary edge is the edge on which the point lies.           */
+/*                                                                           */
+/*  Returns INTRIANGLE if the point lies strictly within a triangle.         */
+/*  `searchtri' is a handle on the triangle that contains the point.         */
+/*                                                                           */
+/*  Returns OUTSIDE if the point lies outside the mesh.  `searchtri' is a    */
+/*  handle whose primary edge the point is to the right of.  This might      */
+/*  occur when the circumcenter of a triangle falls just slightly outside    */
+/*  the mesh due to floating-point roundoff error.  It also occurs when      */
+/*  seeking a hole or region point that a foolish user has placed outside    */
+/*  the mesh.                                                                */
+/*                                                                           */
+/*  WARNING:  This routine is designed for convex triangulations, and will   */
+/*  not generally work after the holes and concavities have been carved.     */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+enum locateresult locate(struct mesh *m, struct behavior *b,
+                         vertex searchpoint, struct otri *searchtri)
+#else /* not ANSI_DECLARATORS */
+enum locateresult locate(m, b, searchpoint, searchtri)
+struct mesh *m;
+struct behavior *b;
+vertex searchpoint;
+struct otri *searchtri;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  VOID **sampleblock;
+  triangle *firsttri;
+  struct otri sampletri;
+  vertex torg, tdest;
+  unsigned long alignptr;
+  REAL searchdist, dist;
+  REAL ahead;
+  long sampleblocks, samplesperblock, samplenum;
+  long triblocks;
+  long i, j;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+
+  if (b->verbose > 2) {
+    printf("  Randomly sampling for a triangle near point (%.12g, %.12g).\n",
+           searchpoint[0], searchpoint[1]);
+  }
+  /* Record the distance from the suggested starting triangle to the */
+  /*   point we seek.                                                */
+  org(*searchtri, torg);
+  searchdist = (searchpoint[0] - torg[0]) * (searchpoint[0] - torg[0]) +
+               (searchpoint[1] - torg[1]) * (searchpoint[1] - torg[1]);
+  if (b->verbose > 2) {
+    printf("    Boundary triangle has origin (%.12g, %.12g).\n",
+           torg[0], torg[1]);
+  }
+
+  /* If a recently encountered triangle has been recorded and has not been */
+  /*   deallocated, test it as a good starting point.                      */
+  if (m->recenttri.tri != (triangle *) NULL) {
+    if (!deadtri(m->recenttri.tri)) {
+      org(m->recenttri, torg);
+      if ((torg[0] == searchpoint[0]) && (torg[1] == searchpoint[1])) {
+        otricopy(m->recenttri, *searchtri);
+        return ONVERTEX;
+      }
+      dist = (searchpoint[0] - torg[0]) * (searchpoint[0] - torg[0]) +
+             (searchpoint[1] - torg[1]) * (searchpoint[1] - torg[1]);
+      if (dist < searchdist) {
+        otricopy(m->recenttri, *searchtri);
+        searchdist = dist;
+        if (b->verbose > 2) {
+          printf("    Choosing recent triangle with origin (%.12g, %.12g).\n",
+                 torg[0], torg[1]);
+        }
+      }
+    }
+  }
+
+  /* The number of random samples taken is proportional to the cube root of */
+  /*   the number of triangles in the mesh.  The next bit of code assumes   */
+  /*   that the number of triangles increases monotonically.                */
+  while (SAMPLEFACTOR * m->samples * m->samples * m->samples <
+         m->triangles.items) {
+    m->samples++;
+  }
+  triblocks = (m->triangles.maxitems + TRIPERBLOCK - 1) / TRIPERBLOCK;
+  samplesperblock = (m->samples + triblocks - 1) / triblocks;
+  sampleblocks = m->samples / samplesperblock;
+  sampleblock = m->triangles.firstblock;
+  sampletri.orient = 0;
+  for (i = 0; i < sampleblocks; i++) {
+    alignptr = (unsigned long) (sampleblock + 1);
+    firsttri = (triangle *) (alignptr + (unsigned long) m->triangles.alignbytes
+                      - (alignptr % (unsigned long) m->triangles.alignbytes));
+    for (j = 0; j < samplesperblock; j++) {
+      if (i == triblocks - 1) {
+        samplenum = randomnation((unsigned int)
+                                 (m->triangles.maxitems - (i * TRIPERBLOCK)));
+      } else {
+        samplenum = randomnation(TRIPERBLOCK);
+      }
+      sampletri.tri = (triangle *)
+                      (firsttri + (samplenum * m->triangles.itemwords));
+      if (!deadtri(sampletri.tri)) {
+        org(sampletri, torg);
+        dist = (searchpoint[0] - torg[0]) * (searchpoint[0] - torg[0]) +
+               (searchpoint[1] - torg[1]) * (searchpoint[1] - torg[1]);
+        if (dist < searchdist) {
+          otricopy(sampletri, *searchtri);
+          searchdist = dist;
+          if (b->verbose > 2) {
+            printf("    Choosing triangle with origin (%.12g, %.12g).\n",
+                   torg[0], torg[1]);
+          }
+        }
+      }
+    }
+    sampleblock = (VOID **) *sampleblock;
+  }
+
+  /* Where are we? */
+  org(*searchtri, torg);
+  dest(*searchtri, tdest);
+  /* Check the starting triangle's vertices. */
+  if ((torg[0] == searchpoint[0]) && (torg[1] == searchpoint[1])) {
+    return ONVERTEX;
+  }
+  if ((tdest[0] == searchpoint[0]) && (tdest[1] == searchpoint[1])) {
+    lnextself(*searchtri);
+    return ONVERTEX;
+  }
+  /* Orient `searchtri' to fit the preconditions of calling preciselocate(). */
+  ahead = counterclockwise(m, b, torg, tdest, searchpoint);
+  if (ahead < 0.0) {
+    /* Turn around so that `searchpoint' is to the left of the */
+    /*   edge specified by `searchtri'.                        */
+    symself(*searchtri);
+  } else if (ahead == 0.0) {
+    /* Check if `searchpoint' is between `torg' and `tdest'. */
+    if (((torg[0] < searchpoint[0]) == (searchpoint[0] < tdest[0])) &&
+        ((torg[1] < searchpoint[1]) == (searchpoint[1] < tdest[1]))) {
+      return ONEDGE;
+    }
+  }
+  return preciselocate(m, b, searchpoint, searchtri, 0);
+}
+
+/**                                                                         **/
+/**                                                                         **/
+/********* Point location routines end here                          *********/
+
+/********* Mesh transformation routines begin here                   *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  insertsubseg()   Create a new subsegment and insert it between two       */
+/*                   triangles.                                              */
+/*                                                                           */
+/*  The new subsegment is inserted at the edge described by the handle       */
+/*  `tri'.  Its vertices are properly initialized.  The marker `subsegmark'  */
+/*  is applied to the subsegment and, if appropriate, its vertices.          */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void insertsubseg(struct mesh *m, struct behavior *b, struct otri *tri,
+                  int subsegmark)
+#else /* not ANSI_DECLARATORS */
+void insertsubseg(m, b, tri, subsegmark)
+struct mesh *m;
+struct behavior *b;
+struct otri *tri;             /* Edge at which to insert the new subsegment. */
+int subsegmark;                            /* Marker for the new subsegment. */
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri oppotri;
+  struct osub newsubseg;
+  vertex triorg, tridest;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  org(*tri, triorg);
+  dest(*tri, tridest);
+  /* Mark vertices if possible. */
+  if (vertexmark(triorg) == 0) {
+    setvertexmark(triorg, subsegmark);
+  }
+  if (vertexmark(tridest) == 0) {
+    setvertexmark(tridest, subsegmark);
+  }
+  /* Check if there's already a subsegment here. */
+  tspivot(*tri, newsubseg);
+  if (newsubseg.ss == m->dummysub) {
+    /* Make new subsegment and initialize its vertices. */
+    makesubseg(m, &newsubseg);
+    setsorg(newsubseg, tridest);
+    setsdest(newsubseg, triorg);
+    /* Bond new subsegment to the two triangles it is sandwiched between. */
+    /*   Note that the facing triangle `oppotri' might be equal to        */
+    /*   `dummytri' (outer space), but the new subsegment is bonded to it */
+    /*   all the same.                                                    */
+    tsbond(*tri, newsubseg);
+    sym(*tri, oppotri);
+    ssymself(newsubseg);
+    tsbond(oppotri, newsubseg);
+    setmark(newsubseg, subsegmark);
+    if (b->verbose > 2) {
+      printf("  Inserting new ");
+      printsubseg(m, b, &newsubseg);
+    }
+  } else {
+    if (mark(newsubseg) == 0) {
+      setmark(newsubseg, subsegmark);
+    }
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  Terminology                                                              */
+/*                                                                           */
+/*  A "local transformation" replaces a small set of triangles with another  */
+/*  set of triangles.  This may or may not involve inserting or deleting a   */
+/*  vertex.                                                                  */
+/*                                                                           */
+/*  The term "casing" is used to describe the set of triangles that are      */
+/*  attached to the triangles being transformed, but are not transformed     */
+/*  themselves.  Think of the casing as a fixed hollow structure inside      */
+/*  which all the action happens.  A "casing" is only defined relative to    */
+/*  a single transformation; each occurrence of a transformation will        */
+/*  involve a different casing.                                              */
+/*                                                                           */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  flip()   Transform two triangles to two different triangles by flipping  */
+/*           an edge counterclockwise within a quadrilateral.                */
+/*                                                                           */
+/*  Imagine the original triangles, abc and bad, oriented so that the        */
+/*  shared edge ab lies in a horizontal plane, with the vertex b on the left */
+/*  and the vertex a on the right.  The vertex c lies below the edge, and    */
+/*  the vertex d lies above the edge.  The `flipedge' handle holds the edge  */
+/*  ab of triangle abc, and is directed left, from vertex a to vertex b.     */
+/*                                                                           */
+/*  The triangles abc and bad are deleted and replaced by the triangles cdb  */
+/*  and dca.  The triangles that represent abc and bad are NOT deallocated;  */
+/*  they are reused for dca and cdb, respectively.  Hence, any handles that  */
+/*  may have held the original triangles are still valid, although not       */
+/*  directed as they were before.                                            */
+/*                                                                           */
+/*  Upon completion of this routine, the `flipedge' handle holds the edge    */
+/*  dc of triangle dca, and is directed down, from vertex d to vertex c.     */
+/*  (Hence, the two triangles have rotated counterclockwise.)                */
+/*                                                                           */
+/*  WARNING:  This transformation is geometrically valid only if the         */
+/*  quadrilateral adbc is convex.  Furthermore, this transformation is       */
+/*  valid only if there is not a subsegment between the triangles abc and    */
+/*  bad.  This routine does not check either of these preconditions, and     */
+/*  it is the responsibility of the calling routine to ensure that they are  */
+/*  met.  If they are not, the streets shall be filled with wailing and      */
+/*  gnashing of teeth.                                                       */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void flip(struct mesh *m, struct behavior *b, struct otri *flipedge)
+#else /* not ANSI_DECLARATORS */
+void flip(m, b, flipedge)
+struct mesh *m;
+struct behavior *b;
+struct otri *flipedge;                    /* Handle for the triangle abc. */
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri botleft, botright;
+  struct otri topleft, topright;
+  struct otri top;
+  struct otri botlcasing, botrcasing;
+  struct otri toplcasing, toprcasing;
+  struct osub botlsubseg, botrsubseg;
+  struct osub toplsubseg, toprsubseg;
+  vertex leftvertex, rightvertex, botvertex;
+  vertex farvertex;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  /* Identify the vertices of the quadrilateral. */
+  org(*flipedge, rightvertex);
+  dest(*flipedge, leftvertex);
+  apex(*flipedge, botvertex);
+  sym(*flipedge, top);
+#ifdef SELF_CHECK
+  if (top.tri == m->dummytri) {
+    printf("Internal error in flip():  Attempt to flip on boundary.\n");
+    lnextself(*flipedge);
+    return;
+  }
+  if (m->checksegments) {
+    tspivot(*flipedge, toplsubseg);
+    if (toplsubseg.ss != m->dummysub) {
+      printf("Internal error in flip():  Attempt to flip a segment.\n");
+      lnextself(*flipedge);
+      return;
+    }
+  }
+#endif /* SELF_CHECK */
+  apex(top, farvertex);
+
+  /* Identify the casing of the quadrilateral. */
+  lprev(top, topleft);
+  sym(topleft, toplcasing);
+  lnext(top, topright);
+  sym(topright, toprcasing);
+  lnext(*flipedge, botleft);
+  sym(botleft, botlcasing);
+  lprev(*flipedge, botright);
+  sym(botright, botrcasing);
+  /* Rotate the quadrilateral one-quarter turn counterclockwise. */
+  bond(topleft, botlcasing);
+  bond(botleft, botrcasing);
+  bond(botright, toprcasing);
+  bond(topright, toplcasing);
+
+  if (m->checksegments) {
+    /* Check for subsegments and rebond them to the quadrilateral. */
+    tspivot(topleft, toplsubseg);
+    tspivot(botleft, botlsubseg);
+    tspivot(botright, botrsubseg);
+    tspivot(topright, toprsubseg);
+    if (toplsubseg.ss == m->dummysub) {
+      tsdissolve(topright);
+    } else {
+      tsbond(topright, toplsubseg);
+    }
+    if (botlsubseg.ss == m->dummysub) {
+      tsdissolve(topleft);
+    } else {
+      tsbond(topleft, botlsubseg);
+    }
+    if (botrsubseg.ss == m->dummysub) {
+      tsdissolve(botleft);
+    } else {
+      tsbond(botleft, botrsubseg);
+    }
+    if (toprsubseg.ss == m->dummysub) {
+      tsdissolve(botright);
+    } else {
+      tsbond(botright, toprsubseg);
+    }
+  }
+
+  /* New vertex assignments for the rotated quadrilateral. */
+  setorg(*flipedge, farvertex);
+  setdest(*flipedge, botvertex);
+  setapex(*flipedge, rightvertex);
+  setorg(top, botvertex);
+  setdest(top, farvertex);
+  setapex(top, leftvertex);
+  if (b->verbose > 2) {
+    printf("  Edge flip results in left ");
+    printtriangle(m, b, &top);
+    printf("  and right ");
+    printtriangle(m, b, flipedge);
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  unflip()   Transform two triangles to two different triangles by         */
+/*             flipping an edge clockwise within a quadrilateral.  Reverses  */
+/*             the flip() operation so that the data structures representing */
+/*             the triangles are back where they were before the flip().     */
+/*                                                                           */
+/*  Imagine the original triangles, abc and bad, oriented so that the        */
+/*  shared edge ab lies in a horizontal plane, with the vertex b on the left */
+/*  and the vertex a on the right.  The vertex c lies below the edge, and    */
+/*  the vertex d lies above the edge.  The `flipedge' handle holds the edge  */
+/*  ab of triangle abc, and is directed left, from vertex a to vertex b.     */
+/*                                                                           */
+/*  The triangles abc and bad are deleted and replaced by the triangles cdb  */
+/*  and dca.  The triangles that represent abc and bad are NOT deallocated;  */
+/*  they are reused for cdb and dca, respectively.  Hence, any handles that  */
+/*  may have held the original triangles are still valid, although not       */
+/*  directed as they were before.                                            */
+/*                                                                           */
+/*  Upon completion of this routine, the `flipedge' handle holds the edge    */
+/*  cd of triangle cdb, and is directed up, from vertex c to vertex d.       */
+/*  (Hence, the two triangles have rotated clockwise.)                       */
+/*                                                                           */
+/*  WARNING:  This transformation is geometrically valid only if the         */
+/*  quadrilateral adbc is convex.  Furthermore, this transformation is       */
+/*  valid only if there is not a subsegment between the triangles abc and    */
+/*  bad.  This routine does not check either of these preconditions, and     */
+/*  it is the responsibility of the calling routine to ensure that they are  */
+/*  met.  If they are not, the streets shall be filled with wailing and      */
+/*  gnashing of teeth.                                                       */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void unflip(struct mesh *m, struct behavior *b, struct otri *flipedge)
+#else /* not ANSI_DECLARATORS */
+void unflip(m, b, flipedge)
+struct mesh *m;
+struct behavior *b;
+struct otri *flipedge;                    /* Handle for the triangle abc. */
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri botleft, botright;
+  struct otri topleft, topright;
+  struct otri top;
+  struct otri botlcasing, botrcasing;
+  struct otri toplcasing, toprcasing;
+  struct osub botlsubseg, botrsubseg;
+  struct osub toplsubseg, toprsubseg;
+  vertex leftvertex, rightvertex, botvertex;
+  vertex farvertex;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  /* Identify the vertices of the quadrilateral. */
+  org(*flipedge, rightvertex);
+  dest(*flipedge, leftvertex);
+  apex(*flipedge, botvertex);
+  sym(*flipedge, top);
+#ifdef SELF_CHECK
+  if (top.tri == m->dummytri) {
+    printf("Internal error in unflip():  Attempt to flip on boundary.\n");
+    lnextself(*flipedge);
+    return;
+  }
+  if (m->checksegments) {
+    tspivot(*flipedge, toplsubseg);
+    if (toplsubseg.ss != m->dummysub) {
+      printf("Internal error in unflip():  Attempt to flip a subsegment.\n");
+      lnextself(*flipedge);
+      return;
+    }
+  }
+#endif /* SELF_CHECK */
+  apex(top, farvertex);
+
+  /* Identify the casing of the quadrilateral. */
+  lprev(top, topleft);
+  sym(topleft, toplcasing);
+  lnext(top, topright);
+  sym(topright, toprcasing);
+  lnext(*flipedge, botleft);
+  sym(botleft, botlcasing);
+  lprev(*flipedge, botright);
+  sym(botright, botrcasing);
+  /* Rotate the quadrilateral one-quarter turn clockwise. */
+  bond(topleft, toprcasing);
+  bond(botleft, toplcasing);
+  bond(botright, botlcasing);
+  bond(topright, botrcasing);
+
+  if (m->checksegments) {
+    /* Check for subsegments and rebond them to the quadrilateral. */
+    tspivot(topleft, toplsubseg);
+    tspivot(botleft, botlsubseg);
+    tspivot(botright, botrsubseg);
+    tspivot(topright, toprsubseg);
+    if (toplsubseg.ss == m->dummysub) {
+      tsdissolve(botleft);
+    } else {
+      tsbond(botleft, toplsubseg);
+    }
+    if (botlsubseg.ss == m->dummysub) {
+      tsdissolve(botright);
+    } else {
+      tsbond(botright, botlsubseg);
+    }
+    if (botrsubseg.ss == m->dummysub) {
+      tsdissolve(topright);
+    } else {
+      tsbond(topright, botrsubseg);
+    }
+    if (toprsubseg.ss == m->dummysub) {
+      tsdissolve(topleft);
+    } else {
+      tsbond(topleft, toprsubseg);
+    }
+  }
+
+  /* New vertex assignments for the rotated quadrilateral. */
+  setorg(*flipedge, botvertex);
+  setdest(*flipedge, farvertex);
+  setapex(*flipedge, leftvertex);
+  setorg(top, farvertex);
+  setdest(top, botvertex);
+  setapex(top, rightvertex);
+  if (b->verbose > 2) {
+    printf("  Edge unflip results in left ");
+    printtriangle(m, b, flipedge);
+    printf("  and right ");
+    printtriangle(m, b, &top);
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  insertvertex()   Insert a vertex into a Delaunay triangulation,          */
+/*                   performing flips as necessary to maintain the Delaunay  */
+/*                   property.                                               */
+/*                                                                           */
+/*  The point `insertvertex' is located.  If `searchtri.tri' is not NULL,    */
+/*  the search for the containing triangle begins from `searchtri'.  If      */
+/*  `searchtri.tri' is NULL, a full point location procedure is called.      */
+/*  If `insertvertex' is found inside a triangle, the triangle is split into */
+/*  three; if `insertvertex' lies on an edge, the edge is split in two,      */
+/*  thereby splitting the two adjacent triangles into four.  Edge flips are  */
+/*  used to restore the Delaunay property.  If `insertvertex' lies on an     */
+/*  existing vertex, no action is taken, and the value DUPLICATEVERTEX is    */
+/*  returned.  On return, `searchtri' is set to a handle whose origin is the */
+/*  existing vertex.                                                         */
+/*                                                                           */
+/*  Normally, the parameter `splitseg' is set to NULL, implying that no      */
+/*  subsegment should be split.  In this case, if `insertvertex' is found to */
+/*  lie on a segment, no action is taken, and the value VIOLATINGVERTEX is   */
+/*  returned.  On return, `searchtri' is set to a handle whose primary edge  */
+/*  is the violated subsegment.                                              */
+/*                                                                           */
+/*  If the calling routine wishes to split a subsegment by inserting a       */
+/*  vertex in it, the parameter `splitseg' should be that subsegment.  In    */
+/*  this case, `searchtri' MUST be the triangle handle reached by pivoting   */
+/*  from that subsegment; no point location is done.                         */
+/*                                                                           */
+/*  `segmentflaws' and `triflaws' are flags that indicate whether or not     */
+/*  there should be checks for the creation of encroached subsegments or bad */
+/*  quality triangles.  If a newly inserted vertex encroaches upon           */
+/*  subsegments, these subsegments are added to the list of subsegments to   */
+/*  be split if `segmentflaws' is set.  If bad triangles are created, these  */
+/*  are added to the queue if `triflaws' is set.                             */
+/*                                                                           */
+/*  If a duplicate vertex or violated segment does not prevent the vertex    */
+/*  from being inserted, the return value will be ENCROACHINGVERTEX if the   */
+/*  vertex encroaches upon a subsegment (and checking is enabled), or        */
+/*  SUCCESSFULVERTEX otherwise.  In either case, `searchtri' is set to a     */
+/*  handle whose origin is the newly inserted vertex.                        */
+/*                                                                           */
+/*  insertvertex() does not use flip() for reasons of speed; some            */
+/*  information can be reused from edge flip to edge flip, like the          */
+/*  locations of subsegments.                                                */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+enum insertvertexresult insertvertex(struct mesh *m, struct behavior *b,
+                                     vertex newvertex, struct otri *searchtri,
+                                     struct osub *splitseg,
+                                     int segmentflaws, int triflaws,
+                                     REAL iradius)
+#else /* not ANSI_DECLARATORS */
+enum insertvertexresult insertvertex(m, b, newvertex, searchtri, splitseg,
+                                     segmentflaws, triflaws, iradius)
+struct mesh *m;
+struct behavior *b;
+vertex newvertex;
+struct otri *searchtri;
+struct osub *splitseg;
+int segmentflaws;
+int triflaws;
+REAL iradius;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri horiz;
+  struct otri top;
+  struct otri botleft, botright;
+  struct otri topleft, topright;
+  struct otri newbotleft, newbotright;
+  struct otri newtopright;
+  struct otri botlcasing, botrcasing;
+  struct otri toplcasing, toprcasing;
+  struct otri testtri;
+  struct osub botlsubseg, botrsubseg;
+  struct osub toplsubseg, toprsubseg;
+  struct osub brokensubseg;
+  struct osub checksubseg;
+  struct osub rightsubseg;
+  struct osub newsubseg;
+  struct badsubseg *encroached;
+  struct flipstacker *newflip;
+  vertex first;
+  vertex leftvertex, rightvertex, botvertex, topvertex, farvertex;
+  REAL attrib;
+  REAL area;
+  enum insertvertexresult success;
+  enum locateresult intersect;
+  int doflip;
+  int mirrorflag;
+  int enq;
+  int i;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+  subseg sptr;         /* Temporary variable used by spivot() and tspivot(). */
+
+  if (b->verbose > 1) {
+    printf("  Inserting (%.12g, %.12g).\n", newvertex[0], newvertex[1]);
+  }
+
+  if (splitseg == (struct osub *) NULL) {
+    /* Find the location of the vertex to be inserted.  Check if a good */
+    /*   starting triangle has already been provided by the caller.     */
+    if (searchtri->tri == m->dummytri) {
+      /* Find a boundary triangle. */
+      horiz.tri = m->dummytri;
+      horiz.orient = 0;
+      symself(horiz);
+      /* Search for a triangle containing `newvertex'. */
+      intersect = locate(m, b, newvertex, &horiz);
+    } else {
+      /* Start searching from the triangle provided by the caller. */
+      otricopy(*searchtri, horiz);
+      intersect = preciselocate(m, b, newvertex, &horiz, 1);
+    }
+  } else {
+    /* The calling routine provides the subsegment in which */
+    /*   the vertex is inserted.                             */
+    otricopy(*searchtri, horiz);
+    intersect = ONEDGE;
+  }
+  if (intersect == ONVERTEX) {
+    /* There's already a vertex there.  Return in `searchtri' a triangle */
+    /*   whose origin is the existing vertex.                            */
+    otricopy(horiz, *searchtri);
+    otricopy(horiz, m->recenttri);
+    return DUPLICATEVERTEX;
+  }
+  if ((intersect == ONEDGE) || (intersect == OUTSIDE)) {
+    /* The vertex falls on an edge or boundary. */
+    if (m->checksegments && (splitseg == (struct osub *) NULL)) {
+      /* Check whether the vertex falls on a subsegment. */
+      tspivot(horiz, brokensubseg);
+      if (brokensubseg.ss != m->dummysub) {
+        /* The vertex falls on a subsegment, and hence will not be inserted. */
+        if (segmentflaws) {
+          if (b->nobisect == 2) {
+            enq = 0;
+#ifndef CDT_ONLY
+          } else if (iradius > 0.0) {
+            enq = splitpermitted(m, &brokensubseg, iradius);
+#endif /* not CDT_ONLY */
+          } else {
+            enq = 1;
+          }
+          if (enq && (b->nobisect == 1)) {
+            /* This subsegment may be split only if it is an */
+            /*   internal boundary.                          */
+            sym(horiz, testtri);
+            enq = testtri.tri != m->dummytri;
+          }
+          if (enq) {
+            /* Add the subsegment to the list of encroached subsegments. */
+            encroached = (struct badsubseg *) poolalloc(&m->badsubsegs);
+            encroached->encsubseg = sencode(brokensubseg);
+            sorg(brokensubseg, encroached->subsegorg);
+            sdest(brokensubseg, encroached->subsegdest);
+            if (b->verbose > 2) {
+              printf(
+          "  Queueing encroached subsegment (%.12g, %.12g) (%.12g, %.12g).\n",
+                     encroached->subsegorg[0], encroached->subsegorg[1],
+                     encroached->subsegdest[0], encroached->subsegdest[1]);
+            }
+          }
+        }
+        /* Return a handle whose primary edge contains the vertex, */
+        /*   which has not been inserted.                          */
+        otricopy(horiz, *searchtri);
+        otricopy(horiz, m->recenttri);
+        return VIOLATINGVERTEX;
+      }
+    }
+
+    /* Insert the vertex on an edge, dividing one triangle into two (if */
+    /*   the edge lies on a boundary) or two triangles into four.       */
+    lprev(horiz, botright);
+    sym(botright, botrcasing);
+    sym(horiz, topright);
+    /* Is there a second triangle?  (Or does this edge lie on a boundary?) */
+    mirrorflag = topright.tri != m->dummytri;
+    if (mirrorflag) {
+      lnextself(topright);
+      sym(topright, toprcasing);
+      maketriangle(m, b, &newtopright);
+    } else {
+      /* Splitting a boundary edge increases the number of boundary edges. */
+      m->hullsize++;
+    }
+    maketriangle(m, b, &newbotright);
+
+    /* Set the vertices of changed and new triangles. */
+    org(horiz, rightvertex);
+    dest(horiz, leftvertex);
+    apex(horiz, botvertex);
+    setorg(newbotright, botvertex);
+    setdest(newbotright, rightvertex);
+    setapex(newbotright, newvertex);
+    setorg(horiz, newvertex);
+    for (i = 0; i < m->eextras; i++) {
+      /* Set the element attributes of a new triangle. */
+      setelemattribute(newbotright, i, elemattribute(botright, i));
+    }
+    if (b->vararea) {
+      /* Set the area constraint of a new triangle. */
+      setareabound(newbotright, areabound(botright));
+    }
+    if (mirrorflag) {
+      dest(topright, topvertex);
+      setorg(newtopright, rightvertex);
+      setdest(newtopright, topvertex);
+      setapex(newtopright, newvertex);
+      setorg(topright, newvertex);
+      for (i = 0; i < m->eextras; i++) {
+        /* Set the element attributes of another new triangle. */
+        setelemattribute(newtopright, i, elemattribute(topright, i));
+      }
+      if (b->vararea) {
+        /* Set the area constraint of another new triangle. */
+        setareabound(newtopright, areabound(topright));
+      }
+    }
+
+    /* There may be subsegments that need to be bonded */
+    /*   to the new triangle(s).                       */
+    if (m->checksegments) {
+      tspivot(botright, botrsubseg);
+      if (botrsubseg.ss != m->dummysub) {
+        tsdissolve(botright);
+        tsbond(newbotright, botrsubseg);
+      }
+      if (mirrorflag) {
+        tspivot(topright, toprsubseg);
+        if (toprsubseg.ss != m->dummysub) {
+          tsdissolve(topright);
+          tsbond(newtopright, toprsubseg);
+        }
+      }
+    }
+
+    /* Bond the new triangle(s) to the surrounding triangles. */
+    bond(newbotright, botrcasing);
+    lprevself(newbotright);
+    bond(newbotright, botright);
+    lprevself(newbotright);
+    if (mirrorflag) {
+      bond(newtopright, toprcasing);
+      lnextself(newtopright);
+      bond(newtopright, topright);
+      lnextself(newtopright);
+      bond(newtopright, newbotright);
+    }
+
+    if (splitseg != (struct osub *) NULL) {
+      /* Split the subsegment into two. */
+      setsdest(*splitseg, newvertex);
+      ssymself(*splitseg);
+      spivot(*splitseg, rightsubseg);
+      insertsubseg(m, b, &newbotright, mark(*splitseg));
+      tspivot(newbotright, newsubseg);
+      sbond(*splitseg, newsubseg);
+      ssymself(newsubseg);
+      sbond(newsubseg, rightsubseg);
+      ssymself(*splitseg);
+      /* Transfer the subsegment's boundary marker to the vertex */
+      /*   if required.                                          */
+      if (vertexmark(newvertex) == 0) {
+        setvertexmark(newvertex, mark(*splitseg));
+      }
+    }
+
+    if (m->checkquality) {
+      poolrestart(&m->flipstackers);
+      m->lastflip = (struct flipstacker *) poolalloc(&m->flipstackers);
+      m->lastflip->flippedtri = encode(horiz);
+      m->lastflip->prevflip = (struct flipstacker *) &insertvertex;
+    }
+
+#ifdef SELF_CHECK
+    if (counterclockwise(m, b, rightvertex, leftvertex, botvertex) < 0.0) {
+      printf("Internal error in insertvertex():\n");
+      printf(
+            "  Clockwise triangle prior to edge vertex insertion (bottom).\n");
+    }
+    if (mirrorflag) {
+      if (counterclockwise(m, b, leftvertex, rightvertex, topvertex) < 0.0) {
+        printf("Internal error in insertvertex():\n");
+        printf("  Clockwise triangle prior to edge vertex insertion (top).\n");
+      }
+      if (counterclockwise(m, b, rightvertex, topvertex, newvertex) < 0.0) {
+        printf("Internal error in insertvertex():\n");
+        printf(
+            "  Clockwise triangle after edge vertex insertion (top right).\n");
+      }
+      if (counterclockwise(m, b, topvertex, leftvertex, newvertex) < 0.0) {
+        printf("Internal error in insertvertex():\n");
+        printf(
+            "  Clockwise triangle after edge vertex insertion (top left).\n");
+      }
+    }
+    if (counterclockwise(m, b, leftvertex, botvertex, newvertex) < 0.0) {
+      printf("Internal error in insertvertex():\n");
+      printf(
+          "  Clockwise triangle after edge vertex insertion (bottom left).\n");
+    }
+    if (counterclockwise(m, b, botvertex, rightvertex, newvertex) < 0.0) {
+      printf("Internal error in insertvertex():\n");
+      printf(
+        "  Clockwise triangle after edge vertex insertion (bottom right).\n");
+    }
+#endif /* SELF_CHECK */
+    if (b->verbose > 2) {
+      printf("  Updating bottom left ");
+      printtriangle(m, b, &botright);
+      if (mirrorflag) {
+        printf("  Updating top left ");
+        printtriangle(m, b, &topright);
+        printf("  Creating top right ");
+        printtriangle(m, b, &newtopright);
+      }
+      printf("  Creating bottom right ");
+      printtriangle(m, b, &newbotright);
+    }
+
+    /* Position `horiz' on the first edge to check for */
+    /*   the Delaunay property.                        */
+    lnextself(horiz);
+  } else {
+    /* Insert the vertex in a triangle, splitting it into three. */
+    lnext(horiz, botleft);
+    lprev(horiz, botright);
+    sym(botleft, botlcasing);
+    sym(botright, botrcasing);
+    maketriangle(m, b, &newbotleft);
+    maketriangle(m, b, &newbotright);
+
+    /* Set the vertices of changed and new triangles. */
+    org(horiz, rightvertex);
+    dest(horiz, leftvertex);
+    apex(horiz, botvertex);
+    setorg(newbotleft, leftvertex);
+    setdest(newbotleft, botvertex);
+    setapex(newbotleft, newvertex);
+    setorg(newbotright, botvertex);
+    setdest(newbotright, rightvertex);
+    setapex(newbotright, newvertex);
+    setapex(horiz, newvertex);
+    for (i = 0; i < m->eextras; i++) {
+      /* Set the element attributes of the new triangles. */
+      attrib = elemattribute(horiz, i);
+      setelemattribute(newbotleft, i, attrib);
+      setelemattribute(newbotright, i, attrib);
+    }
+    if (b->vararea) {
+      /* Set the area constraint of the new triangles. */
+      area = areabound(horiz);
+      setareabound(newbotleft, area);
+      setareabound(newbotright, area);
+    }
+
+    /* There may be subsegments that need to be bonded */
+    /*   to the new triangles.                         */
+    if (m->checksegments) {
+      tspivot(botleft, botlsubseg);
+      if (botlsubseg.ss != m->dummysub) {
+        tsdissolve(botleft);
+        tsbond(newbotleft, botlsubseg);
+      }
+      tspivot(botright, botrsubseg);
+      if (botrsubseg.ss != m->dummysub) {
+        tsdissolve(botright);
+        tsbond(newbotright, botrsubseg);
+      }
+    }
+
+    /* Bond the new triangles to the surrounding triangles. */
+    bond(newbotleft, botlcasing);
+    bond(newbotright, botrcasing);
+    lnextself(newbotleft);
+    lprevself(newbotright);
+    bond(newbotleft, newbotright);
+    lnextself(newbotleft);
+    bond(botleft, newbotleft);
+    lprevself(newbotright);
+    bond(botright, newbotright);
+
+    if (m->checkquality) {
+      poolrestart(&m->flipstackers);
+      m->lastflip = (struct flipstacker *) poolalloc(&m->flipstackers);
+      m->lastflip->flippedtri = encode(horiz);
+      m->lastflip->prevflip = (struct flipstacker *) NULL;
+    }
+
+#ifdef SELF_CHECK
+    if (counterclockwise(m, b, rightvertex, leftvertex, botvertex) < 0.0) {
+      printf("Internal error in insertvertex():\n");
+      printf("  Clockwise triangle prior to vertex insertion.\n");
+    }
+    if (counterclockwise(m, b, rightvertex, leftvertex, newvertex) < 0.0) {
+      printf("Internal error in insertvertex():\n");
+      printf("  Clockwise triangle after vertex insertion (top).\n");
+    }
+    if (counterclockwise(m, b, leftvertex, botvertex, newvertex) < 0.0) {
+      printf("Internal error in insertvertex():\n");
+      printf("  Clockwise triangle after vertex insertion (left).\n");
+    }
+    if (counterclockwise(m, b, botvertex, rightvertex, newvertex) < 0.0) {
+      printf("Internal error in insertvertex():\n");
+      printf("  Clockwise triangle after vertex insertion (right).\n");
+    }
+#endif /* SELF_CHECK */
+    if (b->verbose > 2) {
+      printf("  Updating top ");
+      printtriangle(m, b, &horiz);
+      printf("  Creating left ");
+      printtriangle(m, b, &newbotleft);
+      printf("  Creating right ");
+      printtriangle(m, b, &newbotright);
+    }
+  }
+
+  /* The insertion is successful by default, unless an encroached */
+  /*   subsegment is found.                                       */
+  success = SUCCESSFULVERTEX;
+  /* Circle around the newly inserted vertex, checking each edge opposite */
+  /*   it for the Delaunay property.  Non-Delaunay edges are flipped.     */
+  /*   `horiz' is always the edge being checked.  `first' marks where to  */
+  /*   stop circling.                                                     */
+  org(horiz, first);
+  rightvertex = first;
+  dest(horiz, leftvertex);
+  /* Circle until finished. */
+  while (1) {
+    /* By default, the edge will be flipped. */
+    doflip = 1;
+
+    if (m->checksegments) {
+      /* Check for a subsegment, which cannot be flipped. */
+      tspivot(horiz, checksubseg);
+      if (checksubseg.ss != m->dummysub) {
+        /* The edge is a subsegment and cannot be flipped. */
+        doflip = 0;
+#ifndef CDT_ONLY
+        if (segmentflaws) {
+          /* Does the new vertex encroach upon this subsegment? */
+          if (checkseg4encroach(m, b, &checksubseg, iradius)) {
+            success = ENCROACHINGVERTEX;
+          }
+        }
+#endif /* not CDT_ONLY */
+      }
+    }
+
+    if (doflip) {
+      /* Check if the edge is a boundary edge. */
+      sym(horiz, top);
+      if (top.tri == m->dummytri) {
+        /* The edge is a boundary edge and cannot be flipped. */
+        doflip = 0;
+      } else {
+        /* Find the vertex on the other side of the edge. */
+        apex(top, farvertex);
+        /* In the incremental Delaunay triangulation algorithm, any of      */
+        /*   `leftvertex', `rightvertex', and `farvertex' could be vertices */
+        /*   of the triangular bounding box.  These vertices must be        */
+        /*   treated as if they are infinitely distant, even though their   */
+        /*   "coordinates" are not.                                         */
+        if ((leftvertex == m->infvertex1) || (leftvertex == m->infvertex2) ||
+            (leftvertex == m->infvertex3)) {
+          /* `leftvertex' is infinitely distant.  Check the convexity of  */
+          /*   the boundary of the triangulation.  'farvertex' might be   */
+          /*   infinite as well, but trust me, this same condition should */
+          /*   be applied.                                                */
+          doflip = counterclockwise(m, b, newvertex, rightvertex, farvertex)
+                   > 0.0;
+        } else if ((rightvertex == m->infvertex1) ||
+                   (rightvertex == m->infvertex2) ||
+                   (rightvertex == m->infvertex3)) {
+          /* `rightvertex' is infinitely distant.  Check the convexity of */
+          /*   the boundary of the triangulation.  'farvertex' might be   */
+          /*   infinite as well, but trust me, this same condition should */
+          /*   be applied.                                                */
+          doflip = counterclockwise(m, b, farvertex, leftvertex, newvertex)
+                   > 0.0;
+        } else if ((farvertex == m->infvertex1) ||
+                   (farvertex == m->infvertex2) ||
+                   (farvertex == m->infvertex3)) {
+          /* `farvertex' is infinitely distant and cannot be inside */
+          /*   the circumcircle of the triangle `horiz'.            */
+          doflip = 0;
+        } else {
+          /* Test whether the edge is locally Delaunay. */
+          doflip = incircle(m, b, leftvertex, newvertex, rightvertex,
+                            farvertex) > 0.0;
+        }
+        if (doflip) {
+          /* We made it!  Flip the edge `horiz' by rotating its containing */
+          /*   quadrilateral (the two triangles adjacent to `horiz').      */
+          /* Identify the casing of the quadrilateral. */
+          lprev(top, topleft);
+          sym(topleft, toplcasing);
+          lnext(top, topright);
+          sym(topright, toprcasing);
+          lnext(horiz, botleft);
+          sym(botleft, botlcasing);
+          lprev(horiz, botright);
+          sym(botright, botrcasing);
+          /* Rotate the quadrilateral one-quarter turn counterclockwise. */
+          bond(topleft, botlcasing);
+          bond(botleft, botrcasing);
+          bond(botright, toprcasing);
+          bond(topright, toplcasing);
+          if (m->checksegments) {
+            /* Check for subsegments and rebond them to the quadrilateral. */
+            tspivot(topleft, toplsubseg);
+            tspivot(botleft, botlsubseg);
+            tspivot(botright, botrsubseg);
+            tspivot(topright, toprsubseg);
+            if (toplsubseg.ss == m->dummysub) {
+              tsdissolve(topright);
+            } else {
+              tsbond(topright, toplsubseg);
+            }
+            if (botlsubseg.ss == m->dummysub) {
+              tsdissolve(topleft);
+            } else {
+              tsbond(topleft, botlsubseg);
+            }
+            if (botrsubseg.ss == m->dummysub) {
+              tsdissolve(botleft);
+            } else {
+              tsbond(botleft, botrsubseg);
+            }
+            if (toprsubseg.ss == m->dummysub) {
+              tsdissolve(botright);
+            } else {
+              tsbond(botright, toprsubseg);
+            }
+          }
+          /* New vertex assignments for the rotated quadrilateral. */
+          setorg(horiz, farvertex);
+          setdest(horiz, newvertex);
+          setapex(horiz, rightvertex);
+          setorg(top, newvertex);
+          setdest(top, farvertex);
+          setapex(top, leftvertex);
+          for (i = 0; i < m->eextras; i++) {
+            /* Take the average of the two triangles' attributes. */
+            attrib = 0.5 * (elemattribute(top, i) + elemattribute(horiz, i));
+            setelemattribute(top, i, attrib);
+            setelemattribute(horiz, i, attrib);
+          }
+          if (b->vararea) {
+            if ((areabound(top) <= 0.0) || (areabound(horiz) <= 0.0)) {
+              area = -1.0;
+            } else {
+              /* Take the average of the two triangles' area constraints.    */
+              /*   This prevents small area constraints from migrating a     */
+              /*   long, long way from their original location due to flips. */
+              area = 0.5 * (areabound(top) + areabound(horiz));
+            }
+            setareabound(top, area);
+            setareabound(horiz, area);
+          }
+
+          if (m->checkquality) {
+            newflip = (struct flipstacker *) poolalloc(&m->flipstackers);
+            newflip->flippedtri = encode(horiz);
+            newflip->prevflip = m->lastflip;
+            m->lastflip = newflip;
+          }
+
+#ifdef SELF_CHECK
+          if (newvertex != (vertex) NULL) {
+            if (counterclockwise(m, b, leftvertex, newvertex, rightvertex) <
+                0.0) {
+              printf("Internal error in insertvertex():\n");
+              printf("  Clockwise triangle prior to edge flip (bottom).\n");
+            }
+            /* The following test has been removed because constrainededge() */
+            /*   sometimes generates inverted triangles that insertvertex()  */
+            /*   removes.                                                    */
+/*
+            if (counterclockwise(m, b, rightvertex, farvertex, leftvertex) <
+                0.0) {
+              printf("Internal error in insertvertex():\n");
+              printf("  Clockwise triangle prior to edge flip (top).\n");
+            }
+*/
+            if (counterclockwise(m, b, farvertex, leftvertex, newvertex) <
+                0.0) {
+              printf("Internal error in insertvertex():\n");
+              printf("  Clockwise triangle after edge flip (left).\n");
+            }
+            if (counterclockwise(m, b, newvertex, rightvertex, farvertex) <
+                0.0) {
+              printf("Internal error in insertvertex():\n");
+              printf("  Clockwise triangle after edge flip (right).\n");
+            }
+          }
+#endif /* SELF_CHECK */
+          if (b->verbose > 2) {
+            printf("  Edge flip results in left ");
+            lnextself(topleft);
+            printtriangle(m, b, &topleft);
+            printf("  and right ");
+            printtriangle(m, b, &horiz);
+          }
+          /* On the next iterations, consider the two edges that were  */
+          /*   exposed (this is, are now visible to the newly inserted */
+          /*   vertex) by the edge flip.                               */
+          lprevself(horiz);
+          leftvertex = farvertex;
+        }
+      }
+    }
+    if (!doflip) {
+      /* The handle `horiz' is accepted as locally Delaunay. */
+#ifndef CDT_ONLY
+      if (triflaws) {
+        /* Check the triangle `horiz' for quality. */
+        testtriangle(m, b, &horiz);
+      }
+#endif /* not CDT_ONLY */
+      /* Look for the next edge around the newly inserted vertex. */
+      lnextself(horiz);
+      sym(horiz, testtri);
+      /* Check for finishing a complete revolution about the new vertex, or */
+      /*   falling outside  of the triangulation.  The latter will happen   */
+      /*   when a vertex is inserted at a boundary.                         */
+      if ((leftvertex == first) || (testtri.tri == m->dummytri)) {
+        /* We're done.  Return a triangle whose origin is the new vertex. */
+        lnext(horiz, *searchtri);
+        lnext(horiz, m->recenttri);
+        return success;
+      }
+      /* Finish finding the next edge around the newly inserted vertex. */
+      lnext(testtri, horiz);
+      rightvertex = leftvertex;
+      dest(horiz, leftvertex);
+    }
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  triangulatepolygon()   Find the Delaunay triangulation of a polygon that */
+/*                         has a certain "nice" shape.  This includes the    */
+/*                         polygons that result from deletion of a vertex or */
+/*                         insertion of a segment.                           */
+/*                                                                           */
+/*  This is a conceptually difficult routine.  The starting assumption is    */
+/*  that we have a polygon with n sides.  n - 1 of these sides are currently */
+/*  represented as edges in the mesh.  One side, called the "base", need not */
+/*  be.                                                                      */
+/*                                                                           */
+/*  Inside the polygon is a structure I call a "fan", consisting of n - 1    */
+/*  triangles that share a common origin.  For each of these triangles, the  */
+/*  edge opposite the origin is one of the sides of the polygon.  The        */
+/*  primary edge of each triangle is the edge directed from the origin to    */
+/*  the destination; note that this is not the same edge that is a side of   */
+/*  the polygon.  `firstedge' is the primary edge of the first triangle.     */
+/*  From there, the triangles follow in counterclockwise order about the     */
+/*  polygon, until `lastedge', the primary edge of the last triangle.        */
+/*  `firstedge' and `lastedge' are probably connected to other triangles     */
+/*  beyond the extremes of the fan, but their identity is not important, as  */
+/*  long as the fan remains connected to them.                               */
+/*                                                                           */
+/*  Imagine the polygon oriented so that its base is at the bottom.  This    */
+/*  puts `firstedge' on the far right, and `lastedge' on the far left.       */
+/*  The right vertex of the base is the destination of `firstedge', and the  */
+/*  left vertex of the base is the apex of `lastedge'.                       */
+/*                                                                           */
+/*  The challenge now is to find the right sequence of edge flips to         */
+/*  transform the fan into a Delaunay triangulation of the polygon.  Each    */
+/*  edge flip effectively removes one triangle from the fan, committing it   */
+/*  to the polygon.  The resulting polygon has one fewer edge.  If `doflip'  */
+/*  is set, the final flip will be performed, resulting in a fan of one      */
+/*  (useless?) triangle.  If `doflip' is not set, the final flip is not      */
+/*  performed, resulting in a fan of two triangles, and an unfinished        */
+/*  triangular polygon that is not yet filled out with a single triangle.    */
+/*  On completion of the routine, `lastedge' is the last remaining triangle, */
+/*  or the leftmost of the last two.                                         */
+/*                                                                           */
+/*  Although the flips are performed in the order described above, the       */
+/*  decisions about what flips to perform are made in precisely the reverse  */
+/*  order.  The recursive triangulatepolygon() procedure makes a decision,   */
+/*  uses up to two recursive calls to triangulate the "subproblems"          */
+/*  (polygons with fewer edges), and then performs an edge flip.             */
+/*                                                                           */
+/*  The "decision" it makes is which vertex of the polygon should be         */
+/*  connected to the base.  This decision is made by testing every possible  */
+/*  vertex.  Once the best vertex is found, the two edges that connect this  */
+/*  vertex to the base become the bases for two smaller polygons.  These     */
+/*  are triangulated recursively.  Unfortunately, this approach can take     */
+/*  O(n^2) time not only in the worst case, but in many common cases.  It's  */
+/*  rarely a big deal for vertex deletion, where n is rarely larger than     */
+/*  ten, but it could be a big deal for segment insertion, especially if     */
+/*  there's a lot of long segments that each cut many triangles.  I ought to */
+/*  code a faster algorithm some day.                                        */
+/*                                                                           */
+/*  The `edgecount' parameter is the number of sides of the polygon,         */
+/*  including its base.  `triflaws' is a flag that determines whether the    */
+/*  new triangles should be tested for quality, and enqueued if they are     */
+/*  bad.                                                                     */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void triangulatepolygon(struct mesh *m, struct behavior *b,
+                        struct otri *firstedge, struct otri *lastedge,
+                        int edgecount, int doflip, int triflaws)
+#else /* not ANSI_DECLARATORS */
+void triangulatepolygon(m, b, firstedge, lastedge, edgecount, doflip, triflaws)
+struct mesh *m;
+struct behavior *b;
+struct otri *firstedge;
+struct otri *lastedge;
+int edgecount;
+int doflip;
+int triflaws;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri testtri;
+  struct otri besttri;
+  struct otri tempedge;
+  vertex leftbasevertex, rightbasevertex;
+  vertex testvertex;
+  vertex bestvertex;
+  int bestnumber;
+  int i;
+  triangle ptr;   /* Temporary variable used by sym(), onext(), and oprev(). */
+
+  /* Identify the base vertices. */
+  apex(*lastedge, leftbasevertex);
+  dest(*firstedge, rightbasevertex);
+  if (b->verbose > 2) {
+    printf("  Triangulating interior polygon at edge\n");
+    printf("    (%.12g, %.12g) (%.12g, %.12g)\n", leftbasevertex[0],
+           leftbasevertex[1], rightbasevertex[0], rightbasevertex[1]);
+  }
+  /* Find the best vertex to connect the base to. */
+  onext(*firstedge, besttri);
+  dest(besttri, bestvertex);
+  otricopy(besttri, testtri);
+  bestnumber = 1;
+  for (i = 2; i <= edgecount - 2; i++) {
+    onextself(testtri);
+    dest(testtri, testvertex);
+    /* Is this a better vertex? */
+    if (incircle(m, b, leftbasevertex, rightbasevertex, bestvertex,
+                 testvertex) > 0.0) {
+      otricopy(testtri, besttri);
+      bestvertex = testvertex;
+      bestnumber = i;
+    }
+  }
+  if (b->verbose > 2) {
+    printf("    Connecting edge to (%.12g, %.12g)\n", bestvertex[0],
+           bestvertex[1]);
+  }
+  if (bestnumber > 1) {
+    /* Recursively triangulate the smaller polygon on the right. */
+    oprev(besttri, tempedge);
+    triangulatepolygon(m, b, firstedge, &tempedge, bestnumber + 1, 1,
+                       triflaws);
+  }
+  if (bestnumber < edgecount - 2) {
+    /* Recursively triangulate the smaller polygon on the left. */
+    sym(besttri, tempedge);
+    triangulatepolygon(m, b, &besttri, lastedge, edgecount - bestnumber, 1,
+                       triflaws);
+    /* Find `besttri' again; it may have been lost to edge flips. */
+    sym(tempedge, besttri);
+  }
+  if (doflip) {
+    /* Do one final edge flip. */
+    flip(m, b, &besttri);
+#ifndef CDT_ONLY
+    if (triflaws) {
+      /* Check the quality of the newly committed triangle. */
+      sym(besttri, testtri);
+      testtriangle(m, b, &testtri);
+    }
+#endif /* not CDT_ONLY */
+  }
+  /* Return the base triangle. */
+  otricopy(besttri, *lastedge);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  deletevertex()   Delete a vertex from a Delaunay triangulation, ensuring */
+/*                   that the triangulation remains Delaunay.                */
+/*                                                                           */
+/*  The origin of `deltri' is deleted.  The union of the triangles adjacent  */
+/*  to this vertex is a polygon, for which the Delaunay triangulation is     */
+/*  found.  Two triangles are removed from the mesh.                         */
+/*                                                                           */
+/*  Only interior vertices that do not lie on segments or boundaries may be  */
+/*  deleted.                                                                 */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void deletevertex(struct mesh *m, struct behavior *b, struct otri *deltri)
+#else /* not ANSI_DECLARATORS */
+void deletevertex(m, b, deltri)
+struct mesh *m;
+struct behavior *b;
+struct otri *deltri;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri countingtri;
+  struct otri firstedge, lastedge;
+  struct otri deltriright;
+  struct otri lefttri, righttri;
+  struct otri leftcasing, rightcasing;
+  struct osub leftsubseg, rightsubseg;
+  vertex delvertex;
+  vertex neworg;
+  int edgecount;
+  triangle ptr;   /* Temporary variable used by sym(), onext(), and oprev(). */
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  org(*deltri, delvertex);
+  if (b->verbose > 1) {
+    printf("  Deleting (%.12g, %.12g).\n", delvertex[0], delvertex[1]);
+  }
+  vertexdealloc(m, delvertex);
+
+  /* Count the degree of the vertex being deleted. */
+  onext(*deltri, countingtri);
+  edgecount = 1;
+  while (!otriequal(*deltri, countingtri)) {
+#ifdef SELF_CHECK
+    if (countingtri.tri == m->dummytri) {
+      printf("Internal error in deletevertex():\n");
+      printf("  Attempt to delete boundary vertex.\n");
+      internalerror();
+    }
+#endif /* SELF_CHECK */
+    edgecount++;
+    onextself(countingtri);
+  }
+
+#ifdef SELF_CHECK
+  if (edgecount < 3) {
+    printf("Internal error in deletevertex():\n  Vertex has degree %d.\n",
+           edgecount);
+    internalerror();
+  }
+#endif /* SELF_CHECK */
+  if (edgecount > 3) {
+    /* Triangulate the polygon defined by the union of all triangles */
+    /*   adjacent to the vertex being deleted.  Check the quality of */
+    /*   the resulting triangles.                                    */
+    onext(*deltri, firstedge);
+    oprev(*deltri, lastedge);
+    triangulatepolygon(m, b, &firstedge, &lastedge, edgecount, 0,
+                       !b->nobisect);
+  }
+  /* Splice out two triangles. */
+  lprev(*deltri, deltriright);
+  dnext(*deltri, lefttri);
+  sym(lefttri, leftcasing);
+  oprev(deltriright, righttri);
+  sym(righttri, rightcasing);
+  bond(*deltri, leftcasing);
+  bond(deltriright, rightcasing);
+  tspivot(lefttri, leftsubseg);
+  if (leftsubseg.ss != m->dummysub) {
+    tsbond(*deltri, leftsubseg);
+  }
+  tspivot(righttri, rightsubseg);
+  if (rightsubseg.ss != m->dummysub) {
+    tsbond(deltriright, rightsubseg);
+  }
+
+  /* Set the new origin of `deltri' and check its quality. */
+  org(lefttri, neworg);
+  setorg(*deltri, neworg);
+  if (!b->nobisect) {
+    testtriangle(m, b, deltri);
+  }
+
+  /* Delete the two spliced-out triangles. */
+  triangledealloc(m, lefttri.tri);
+  triangledealloc(m, righttri.tri);
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  undovertex()   Undo the most recent vertex insertion.                    */
+/*                                                                           */
+/*  Walks through the list of transformations (flips and a vertex insertion) */
+/*  in the reverse of the order in which they were done, and undoes them.    */
+/*  The inserted vertex is removed from the triangulation and deallocated.   */
+/*  Two triangles (possibly just one) are also deallocated.                  */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void undovertex(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void undovertex(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri fliptri;
+  struct otri botleft, botright, topright;
+  struct otri botlcasing, botrcasing, toprcasing;
+  struct otri gluetri;
+  struct osub botlsubseg, botrsubseg, toprsubseg;
+  vertex botvertex, rightvertex;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  /* Walk through the list of transformations (flips and a vertex insertion) */
+  /*   in the reverse of the order in which they were done, and undo them.   */
+  while (m->lastflip != (struct flipstacker *) NULL) {
+    /* Find a triangle involved in the last unreversed transformation. */
+    decode(m->lastflip->flippedtri, fliptri);
+
+    /* We are reversing one of three transformations:  a trisection of one */
+    /*   triangle into three (by inserting a vertex in the triangle), a    */
+    /*   bisection of two triangles into four (by inserting a vertex in an */
+    /*   edge), or an edge flip.                                           */
+    if (m->lastflip->prevflip == (struct flipstacker *) NULL) {
+      /* Restore a triangle that was split into three triangles, */
+      /*   so it is again one triangle.                          */
+      dprev(fliptri, botleft);
+      lnextself(botleft);
+      onext(fliptri, botright);
+      lprevself(botright);
+      sym(botleft, botlcasing);
+      sym(botright, botrcasing);
+      dest(botleft, botvertex);
+
+      setapex(fliptri, botvertex);
+      lnextself(fliptri);
+      bond(fliptri, botlcasing);
+      tspivot(botleft, botlsubseg);
+      tsbond(fliptri, botlsubseg);
+      lnextself(fliptri);
+      bond(fliptri, botrcasing);
+      tspivot(botright, botrsubseg);
+      tsbond(fliptri, botrsubseg);
+
+      /* Delete the two spliced-out triangles. */
+      triangledealloc(m, botleft.tri);
+      triangledealloc(m, botright.tri);
+    } else if (m->lastflip->prevflip == (struct flipstacker *) &insertvertex) {
+      /* Restore two triangles that were split into four triangles, */
+      /*   so they are again two triangles.                         */
+      lprev(fliptri, gluetri);
+      sym(gluetri, botright);
+      lnextself(botright);
+      sym(botright, botrcasing);
+      dest(botright, rightvertex);
+
+      setorg(fliptri, rightvertex);
+      bond(gluetri, botrcasing);
+      tspivot(botright, botrsubseg);
+      tsbond(gluetri, botrsubseg);
+
+      /* Delete the spliced-out triangle. */
+      triangledealloc(m, botright.tri);
+
+      sym(fliptri, gluetri);
+      if (gluetri.tri != m->dummytri) {
+        lnextself(gluetri);
+        dnext(gluetri, topright);
+        sym(topright, toprcasing);
+
+        setorg(gluetri, rightvertex);
+        bond(gluetri, toprcasing);
+        tspivot(topright, toprsubseg);
+        tsbond(gluetri, toprsubseg);
+
+        /* Delete the spliced-out triangle. */
+        triangledealloc(m, topright.tri);
+      }
+
+      /* This is the end of the list, sneakily encoded. */
+      m->lastflip->prevflip = (struct flipstacker *) NULL;
+    } else {
+      /* Undo an edge flip. */
+      unflip(m, b, &fliptri);
+    }
+
+    /* Go on and process the next transformation. */
+    m->lastflip = m->lastflip->prevflip;
+  }
+}
+
+#endif /* not CDT_ONLY */
+
+/**                                                                         **/
+/**                                                                         **/
+/********* Mesh transformation routines end here                     *********/
+
+/********* Divide-and-conquer Delaunay triangulation begins here     *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  The divide-and-conquer bounding box                                      */
+/*                                                                           */
+/*  I originally implemented the divide-and-conquer and incremental Delaunay */
+/*  triangulations using the edge-based data structure presented by Guibas   */
+/*  and Stolfi.  Switching to a triangle-based data structure doubled the    */
+/*  speed.  However, I had to think of a few extra tricks to maintain the    */
+/*  elegance of the original algorithms.                                     */
+/*                                                                           */
+/*  The "bounding box" used by my variant of the divide-and-conquer          */
+/*  algorithm uses one triangle for each edge of the convex hull of the      */
+/*  triangulation.  These bounding triangles all share a common apical       */
+/*  vertex, which is represented by NULL and which represents nothing.       */
+/*  The bounding triangles are linked in a circular fan about this NULL      */
+/*  vertex, and the edges on the convex hull of the triangulation appear     */
+/*  opposite the NULL vertex.  You might find it easiest to imagine that     */
+/*  the NULL vertex is a point in 3D space behind the center of the          */
+/*  triangulation, and that the bounding triangles form a sort of cone.      */
+/*                                                                           */
+/*  This bounding box makes it easy to represent degenerate cases.  For      */
+/*  instance, the triangulation of two vertices is a single edge.  This edge */
+/*  is represented by two bounding box triangles, one on each "side" of the  */
+/*  edge.  These triangles are also linked together in a fan about the NULL  */
+/*  vertex.                                                                  */
+/*                                                                           */
+/*  The bounding box also makes it easy to traverse the convex hull, as the  */
+/*  divide-and-conquer algorithm needs to do.                                */
+/*                                                                           */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  vertexsort()   Sort an array of vertices by x-coordinate, using the      */
+/*                 y-coordinate as a secondary key.                          */
+/*                                                                           */
+/*  Uses quicksort.  Randomized O(n log n) time.  No, I did not make any of  */
+/*  the usual quicksort mistakes.                                            */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void vertexsort(vertex *sortarray, int arraysize)
+#else /* not ANSI_DECLARATORS */
+void vertexsort(sortarray, arraysize)
+vertex *sortarray;
+int arraysize;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  int left, right;
+  int pivot;
+  REAL pivotx, pivoty;
+  vertex temp;
+
+  if (arraysize == 2) {
+    /* Recursive base case. */
+    if ((sortarray[0][0] > sortarray[1][0]) ||
+        ((sortarray[0][0] == sortarray[1][0]) &&
+         (sortarray[0][1] > sortarray[1][1]))) {
+      temp = sortarray[1];
+      sortarray[1] = sortarray[0];
+      sortarray[0] = temp;
+    }
+    return;
+  }
+  /* Choose a random pivot to split the array. */
+  pivot = (int) randomnation((unsigned int) arraysize);
+  pivotx = sortarray[pivot][0];
+  pivoty = sortarray[pivot][1];
+  /* Split the array. */
+  left = -1;
+  right = arraysize;
+  while (left < right) {
+    /* Search for a vertex whose x-coordinate is too large for the left. */
+    do {
+      left++;
+    } while ((left <= right) && ((sortarray[left][0] < pivotx) ||
+                                 ((sortarray[left][0] == pivotx) &&
+                                  (sortarray[left][1] < pivoty))));
+    /* Search for a vertex whose x-coordinate is too small for the right. */
+    do {
+      right--;
+    } while ((left <= right) && ((sortarray[right][0] > pivotx) ||
+                                 ((sortarray[right][0] == pivotx) &&
+                                  (sortarray[right][1] > pivoty))));
+    if (left < right) {
+      /* Swap the left and right vertices. */
+      temp = sortarray[left];
+      sortarray[left] = sortarray[right];
+      sortarray[right] = temp;
+    }
+  }
+  if (left > 1) {
+    /* Recursively sort the left subset. */
+    vertexsort(sortarray, left);
+  }
+  if (right < arraysize - 2) {
+    /* Recursively sort the right subset. */
+    vertexsort(&sortarray[right + 1], arraysize - right - 1);
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  vertexmedian()   An order statistic algorithm, almost.  Shuffles an      */
+/*                   array of vertices so that the first `median' vertices   */
+/*                   occur lexicographically before the remaining vertices.  */
+/*                                                                           */
+/*  Uses the x-coordinate as the primary key if axis == 0; the y-coordinate  */
+/*  if axis == 1.  Very similar to the vertexsort() procedure, but runs in   */
+/*  randomized linear time.                                                  */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void vertexmedian(vertex *sortarray, int arraysize, int median, int axis)
+#else /* not ANSI_DECLARATORS */
+void vertexmedian(sortarray, arraysize, median, axis)
+vertex *sortarray;
+int arraysize;
+int median;
+int axis;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  int left, right;
+  int pivot;
+  REAL pivot1, pivot2;
+  vertex temp;
+
+  if (arraysize == 2) {
+    /* Recursive base case. */
+    if ((sortarray[0][axis] > sortarray[1][axis]) ||
+        ((sortarray[0][axis] == sortarray[1][axis]) &&
+         (sortarray[0][1 - axis] > sortarray[1][1 - axis]))) {
+      temp = sortarray[1];
+      sortarray[1] = sortarray[0];
+      sortarray[0] = temp;
+    }
+    return;
+  }
+  /* Choose a random pivot to split the array. */
+  pivot = (int) randomnation((unsigned int) arraysize);
+  pivot1 = sortarray[pivot][axis];
+  pivot2 = sortarray[pivot][1 - axis];
+  /* Split the array. */
+  left = -1;
+  right = arraysize;
+  while (left < right) {
+    /* Search for a vertex whose x-coordinate is too large for the left. */
+    do {
+      left++;
+    } while ((left <= right) && ((sortarray[left][axis] < pivot1) ||
+                                 ((sortarray[left][axis] == pivot1) &&
+                                  (sortarray[left][1 - axis] < pivot2))));
+    /* Search for a vertex whose x-coordinate is too small for the right. */
+    do {
+      right--;
+    } while ((left <= right) && ((sortarray[right][axis] > pivot1) ||
+                                 ((sortarray[right][axis] == pivot1) &&
+                                  (sortarray[right][1 - axis] > pivot2))));
+    if (left < right) {
+      /* Swap the left and right vertices. */
+      temp = sortarray[left];
+      sortarray[left] = sortarray[right];
+      sortarray[right] = temp;
+    }
+  }
+  /* Unlike in vertexsort(), at most one of the following */
+  /*   conditionals is true.                             */
+  if (left > median) {
+    /* Recursively shuffle the left subset. */
+    vertexmedian(sortarray, left, median, axis);
+  }
+  if (right < median - 1) {
+    /* Recursively shuffle the right subset. */
+    vertexmedian(&sortarray[right + 1], arraysize - right - 1,
+                 median - right - 1, axis);
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  alternateaxes()   Sorts the vertices as appropriate for the divide-and-  */
+/*                    conquer algorithm with alternating cuts.               */
+/*                                                                           */
+/*  Partitions by x-coordinate if axis == 0; by y-coordinate if axis == 1.   */
+/*  For the base case, subsets containing only two or three vertices are     */
+/*  always sorted by x-coordinate.                                           */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void alternateaxes(vertex *sortarray, int arraysize, int axis)
+#else /* not ANSI_DECLARATORS */
+void alternateaxes(sortarray, arraysize, axis)
+vertex *sortarray;
+int arraysize;
+int axis;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  int divider;
+
+  divider = arraysize >> 1;
+  if (arraysize <= 3) {
+    /* Recursive base case:  subsets of two or three vertices will be    */
+    /*   handled specially, and should always be sorted by x-coordinate. */
+    axis = 0;
+  }
+  /* Partition with a horizontal or vertical cut. */
+  vertexmedian(sortarray, arraysize, divider, axis);
+  /* Recursively partition the subsets with a cross cut. */
+  if (arraysize - divider >= 2) {
+    if (divider >= 2) {
+      alternateaxes(sortarray, divider, 1 - axis);
+    }
+    alternateaxes(&sortarray[divider], arraysize - divider, 1 - axis);
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  mergehulls()   Merge two adjacent Delaunay triangulations into a         */
+/*                 single Delaunay triangulation.                            */
+/*                                                                           */
+/*  This is similar to the algorithm given by Guibas and Stolfi, but uses    */
+/*  a triangle-based, rather than edge-based, data structure.                */
+/*                                                                           */
+/*  The algorithm walks up the gap between the two triangulations, knitting  */
+/*  them together.  As they are merged, some of their bounding triangles     */
+/*  are converted into real triangles of the triangulation.  The procedure   */
+/*  pulls each hull's bounding triangles apart, then knits them together     */
+/*  like the teeth of two gears.  The Delaunay property determines, at each  */
+/*  step, whether the next "tooth" is a bounding triangle of the left hull   */
+/*  or the right.  When a bounding triangle becomes real, its apex is        */
+/*  changed from NULL to a real vertex.                                      */
+/*                                                                           */
+/*  Only two new triangles need to be allocated.  These become new bounding  */
+/*  triangles at the top and bottom of the seam.  They are used to connect   */
+/*  the remaining bounding triangles (those that have not been converted     */
+/*  into real triangles) into a single fan.                                  */
+/*                                                                           */
+/*  On entry, `farleft' and `innerleft' are bounding triangles of the left   */
+/*  triangulation.  The origin of `farleft' is the leftmost vertex, and      */
+/*  the destination of `innerleft' is the rightmost vertex of the            */
+/*  triangulation.  Similarly, `innerright' and `farright' are bounding      */
+/*  triangles of the right triangulation.  The origin of `innerright' and    */
+/*  destination of `farright' are the leftmost and rightmost vertices.       */
+/*                                                                           */
+/*  On completion, the origin of `farleft' is the leftmost vertex of the     */
+/*  merged triangulation, and the destination of `farright' is the rightmost */
+/*  vertex.                                                                  */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void mergehulls(struct mesh *m, struct behavior *b, struct otri *farleft,
+                struct otri *innerleft, struct otri *innerright,
+                struct otri *farright, int axis)
+#else /* not ANSI_DECLARATORS */
+void mergehulls(m, b, farleft, innerleft, innerright, farright, axis)
+struct mesh *m;
+struct behavior *b;
+struct otri *farleft;
+struct otri *innerleft;
+struct otri *innerright;
+struct otri *farright;
+int axis;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri leftcand, rightcand;
+  struct otri baseedge;
+  struct otri nextedge;
+  struct otri sidecasing, topcasing, outercasing;
+  struct otri checkedge;
+  vertex innerleftdest;
+  vertex innerrightorg;
+  vertex innerleftapex, innerrightapex;
+  vertex farleftpt, farrightpt;
+  vertex farleftapex, farrightapex;
+  vertex lowerleft, lowerright;
+  vertex upperleft, upperright;
+  vertex nextapex;
+  vertex checkvertex;
+  int changemade;
+  int badedge;
+  int leftfinished, rightfinished;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+
+  dest(*innerleft, innerleftdest);
+  apex(*innerleft, innerleftapex);
+  org(*innerright, innerrightorg);
+  apex(*innerright, innerrightapex);
+  /* Special treatment for horizontal cuts. */
+  if (b->dwyer && (axis == 1)) {
+    org(*farleft, farleftpt);
+    apex(*farleft, farleftapex);
+    dest(*farright, farrightpt);
+    apex(*farright, farrightapex);
+    /* The pointers to the extremal vertices are shifted to point to the */
+    /*   topmost and bottommost vertex of each hull, rather than the     */
+    /*   leftmost and rightmost vertices.                                */
+    while (farleftapex[1] < farleftpt[1]) {
+      lnextself(*farleft);
+      symself(*farleft);
+      farleftpt = farleftapex;
+      apex(*farleft, farleftapex);
+    }
+    sym(*innerleft, checkedge);
+    apex(checkedge, checkvertex);
+    while (checkvertex[1] > innerleftdest[1]) {
+      lnext(checkedge, *innerleft);
+      innerleftapex = innerleftdest;
+      innerleftdest = checkvertex;
+      sym(*innerleft, checkedge);
+      apex(checkedge, checkvertex);
+    }
+    while (innerrightapex[1] < innerrightorg[1]) {
+      lnextself(*innerright);
+      symself(*innerright);
+      innerrightorg = innerrightapex;
+      apex(*innerright, innerrightapex);
+    }
+    sym(*farright, checkedge);
+    apex(checkedge, checkvertex);
+    while (checkvertex[1] > farrightpt[1]) {
+      lnext(checkedge, *farright);
+      farrightapex = farrightpt;
+      farrightpt = checkvertex;
+      sym(*farright, checkedge);
+      apex(checkedge, checkvertex);
+    }
+  }
+  /* Find a line tangent to and below both hulls. */
+  do {
+    changemade = 0;
+    /* Make innerleftdest the "bottommost" vertex of the left hull. */
+    if (counterclockwise(m, b, innerleftdest, innerleftapex, innerrightorg) >
+        0.0) {
+      lprevself(*innerleft);
+      symself(*innerleft);
+      innerleftdest = innerleftapex;
+      apex(*innerleft, innerleftapex);
+      changemade = 1;
+    }
+    /* Make innerrightorg the "bottommost" vertex of the right hull. */
+    if (counterclockwise(m, b, innerrightapex, innerrightorg, innerleftdest) >
+        0.0) {
+      lnextself(*innerright);
+      symself(*innerright);
+      innerrightorg = innerrightapex;
+      apex(*innerright, innerrightapex);
+      changemade = 1;
+    }
+  } while (changemade);
+  /* Find the two candidates to be the next "gear tooth." */
+  sym(*innerleft, leftcand);
+  sym(*innerright, rightcand);
+  /* Create the bottom new bounding triangle. */
+  maketriangle(m, b, &baseedge);
+  /* Connect it to the bounding boxes of the left and right triangulations. */
+  bond(baseedge, *innerleft);
+  lnextself(baseedge);
+  bond(baseedge, *innerright);
+  lnextself(baseedge);
+  setorg(baseedge, innerrightorg);
+  setdest(baseedge, innerleftdest);
+  /* Apex is intentionally left NULL. */
+  if (b->verbose > 2) {
+    printf("  Creating base bounding ");
+    printtriangle(m, b, &baseedge);
+  }
+  /* Fix the extreme triangles if necessary. */
+  org(*farleft, farleftpt);
+  if (innerleftdest == farleftpt) {
+    lnext(baseedge, *farleft);
+  }
+  dest(*farright, farrightpt);
+  if (innerrightorg == farrightpt) {
+    lprev(baseedge, *farright);
+  }
+  /* The vertices of the current knitting edge. */
+  lowerleft = innerleftdest;
+  lowerright = innerrightorg;
+  /* The candidate vertices for knitting. */
+  apex(leftcand, upperleft);
+  apex(rightcand, upperright);
+  /* Walk up the gap between the two triangulations, knitting them together. */
+  while (1) {
+    /* Have we reached the top?  (This isn't quite the right question,       */
+    /*   because even though the left triangulation might seem finished now, */
+    /*   moving up on the right triangulation might reveal a new vertex of   */
+    /*   the left triangulation.  And vice-versa.)                           */
+    leftfinished = counterclockwise(m, b, upperleft, lowerleft, lowerright) <=
+                   0.0;
+    rightfinished = counterclockwise(m, b, upperright, lowerleft, lowerright)
+                 <= 0.0;
+    if (leftfinished && rightfinished) {
+      /* Create the top new bounding triangle. */
+      maketriangle(m, b, &nextedge);
+      setorg(nextedge, lowerleft);
+      setdest(nextedge, lowerright);
+      /* Apex is intentionally left NULL. */
+      /* Connect it to the bounding boxes of the two triangulations. */
+      bond(nextedge, baseedge);
+      lnextself(nextedge);
+      bond(nextedge, rightcand);
+      lnextself(nextedge);
+      bond(nextedge, leftcand);
+      if (b->verbose > 2) {
+        printf("  Creating top bounding ");
+        printtriangle(m, b, &nextedge);
+      }
+      /* Special treatment for horizontal cuts. */
+      if (b->dwyer && (axis == 1)) {
+        org(*farleft, farleftpt);
+        apex(*farleft, farleftapex);
+        dest(*farright, farrightpt);
+        apex(*farright, farrightapex);
+        sym(*farleft, checkedge);
+        apex(checkedge, checkvertex);
+        /* The pointers to the extremal vertices are restored to the  */
+        /*   leftmost and rightmost vertices (rather than topmost and */
+        /*   bottommost).                                             */
+        while (checkvertex[0] < farleftpt[0]) {
+          lprev(checkedge, *farleft);
+          farleftapex = farleftpt;
+          farleftpt = checkvertex;
+          sym(*farleft, checkedge);
+          apex(checkedge, checkvertex);
+        }
+        while (farrightapex[0] > farrightpt[0]) {
+          lprevself(*farright);
+          symself(*farright);
+          farrightpt = farrightapex;
+          apex(*farright, farrightapex);
+        }
+      }
+      return;
+    }
+    /* Consider eliminating edges from the left triangulation. */
+    if (!leftfinished) {
+      /* What vertex would be exposed if an edge were deleted? */
+      lprev(leftcand, nextedge);
+      symself(nextedge);
+      apex(nextedge, nextapex);
+      /* If nextapex is NULL, then no vertex would be exposed; the */
+      /*   triangulation would have been eaten right through.      */
+      if (nextapex != (vertex) NULL) {
+        /* Check whether the edge is Delaunay. */
+        badedge = incircle(m, b, lowerleft, lowerright, upperleft, nextapex) >
+                  0.0;
+        while (badedge) {
+          /* Eliminate the edge with an edge flip.  As a result, the    */
+          /*   left triangulation will have one more boundary triangle. */
+          lnextself(nextedge);
+          sym(nextedge, topcasing);
+          lnextself(nextedge);
+          sym(nextedge, sidecasing);
+          bond(nextedge, topcasing);
+          bond(leftcand, sidecasing);
+          lnextself(leftcand);
+          sym(leftcand, outercasing);
+          lprevself(nextedge);
+          bond(nextedge, outercasing);
+          /* Correct the vertices to reflect the edge flip. */
+          setorg(leftcand, lowerleft);
+          setdest(leftcand, NULL);
+          setapex(leftcand, nextapex);
+          setorg(nextedge, NULL);
+          setdest(nextedge, upperleft);
+          setapex(nextedge, nextapex);
+          /* Consider the newly exposed vertex. */
+          upperleft = nextapex;
+          /* What vertex would be exposed if another edge were deleted? */
+          otricopy(sidecasing, nextedge);
+          apex(nextedge, nextapex);
+          if (nextapex != (vertex) NULL) {
+            /* Check whether the edge is Delaunay. */
+            badedge = incircle(m, b, lowerleft, lowerright, upperleft,
+                               nextapex) > 0.0;
+          } else {
+            /* Avoid eating right through the triangulation. */
+            badedge = 0;
+          }
+        }
+      }
+    }
+    /* Consider eliminating edges from the right triangulation. */
+    if (!rightfinished) {
+      /* What vertex would be exposed if an edge were deleted? */
+      lnext(rightcand, nextedge);
+      symself(nextedge);
+      apex(nextedge, nextapex);
+      /* If nextapex is NULL, then no vertex would be exposed; the */
+      /*   triangulation would have been eaten right through.      */
+      if (nextapex != (vertex) NULL) {
+        /* Check whether the edge is Delaunay. */
+        badedge = incircle(m, b, lowerleft, lowerright, upperright, nextapex) >
+                  0.0;
+        while (badedge) {
+          /* Eliminate the edge with an edge flip.  As a result, the     */
+          /*   right triangulation will have one more boundary triangle. */
+          lprevself(nextedge);
+          sym(nextedge, topcasing);
+          lprevself(nextedge);
+          sym(nextedge, sidecasing);
+          bond(nextedge, topcasing);
+          bond(rightcand, sidecasing);
+          lprevself(rightcand);
+          sym(rightcand, outercasing);
+          lnextself(nextedge);
+          bond(nextedge, outercasing);
+          /* Correct the vertices to reflect the edge flip. */
+          setorg(rightcand, NULL);
+          setdest(rightcand, lowerright);
+          setapex(rightcand, nextapex);
+          setorg(nextedge, upperright);
+          setdest(nextedge, NULL);
+          setapex(nextedge, nextapex);
+          /* Consider the newly exposed vertex. */
+          upperright = nextapex;
+          /* What vertex would be exposed if another edge were deleted? */
+          otricopy(sidecasing, nextedge);
+          apex(nextedge, nextapex);
+          if (nextapex != (vertex) NULL) {
+            /* Check whether the edge is Delaunay. */
+            badedge = incircle(m, b, lowerleft, lowerright, upperright,
+                               nextapex) > 0.0;
+          } else {
+            /* Avoid eating right through the triangulation. */
+            badedge = 0;
+          }
+        }
+      }
+    }
+    if (leftfinished || (!rightfinished &&
+           (incircle(m, b, upperleft, lowerleft, lowerright, upperright) >
+            0.0))) {
+      /* Knit the triangulations, adding an edge from `lowerleft' */
+      /*   to `upperright'.                                       */
+      bond(baseedge, rightcand);
+      lprev(rightcand, baseedge);
+      setdest(baseedge, lowerleft);
+      lowerright = upperright;
+      sym(baseedge, rightcand);
+      apex(rightcand, upperright);
+    } else {
+      /* Knit the triangulations, adding an edge from `upperleft' */
+      /*   to `lowerright'.                                       */
+      bond(baseedge, leftcand);
+      lnext(leftcand, baseedge);
+      setorg(baseedge, lowerright);
+      lowerleft = upperleft;
+      sym(baseedge, leftcand);
+      apex(leftcand, upperleft);
+    }
+    if (b->verbose > 2) {
+      printf("  Connecting ");
+      printtriangle(m, b, &baseedge);
+    }
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  divconqrecurse()   Recursively form a Delaunay triangulation by the      */
+/*                     divide-and-conquer method.                            */
+/*                                                                           */
+/*  Recursively breaks down the problem into smaller pieces, which are       */
+/*  knitted together by mergehulls().  The base cases (problems of two or    */
+/*  three vertices) are handled specially here.                              */
+/*                                                                           */
+/*  On completion, `farleft' and `farright' are bounding triangles such that */
+/*  the origin of `farleft' is the leftmost vertex (breaking ties by         */
+/*  choosing the highest leftmost vertex), and the destination of            */
+/*  `farright' is the rightmost vertex (breaking ties by choosing the        */
+/*  lowest rightmost vertex).                                                */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void divconqrecurse(struct mesh *m, struct behavior *b, vertex *sortarray,
+                    int vertices, int axis,
+                    struct otri *farleft, struct otri *farright)
+#else /* not ANSI_DECLARATORS */
+void divconqrecurse(m, b, sortarray, vertices, axis, farleft, farright)
+struct mesh *m;
+struct behavior *b;
+vertex *sortarray;
+int vertices;
+int axis;
+struct otri *farleft;
+struct otri *farright;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri midtri, tri1, tri2, tri3;
+  struct otri innerleft, innerright;
+  REAL area;
+  int divider;
+
+  if (b->verbose > 2) {
+    printf("  Triangulating %d vertices.\n", vertices);
+  }
+  if (vertices == 2) {
+    /* The triangulation of two vertices is an edge.  An edge is */
+    /*   represented by two bounding triangles.                  */
+    maketriangle(m, b, farleft);
+    setorg(*farleft, sortarray[0]);
+    setdest(*farleft, sortarray[1]);
+    /* The apex is intentionally left NULL. */
+    maketriangle(m, b, farright);
+    setorg(*farright, sortarray[1]);
+    setdest(*farright, sortarray[0]);
+    /* The apex is intentionally left NULL. */
+    bond(*farleft, *farright);
+    lprevself(*farleft);
+    lnextself(*farright);
+    bond(*farleft, *farright);
+    lprevself(*farleft);
+    lnextself(*farright);
+    bond(*farleft, *farright);
+    if (b->verbose > 2) {
+      printf("  Creating ");
+      printtriangle(m, b, farleft);
+      printf("  Creating ");
+      printtriangle(m, b, farright);
+    }
+    /* Ensure that the origin of `farleft' is sortarray[0]. */
+    lprev(*farright, *farleft);
+    return;
+  } else if (vertices == 3) {
+    /* The triangulation of three vertices is either a triangle (with */
+    /*   three bounding triangles) or two edges (with four bounding   */
+    /*   triangles).  In either case, four triangles are created.     */
+    maketriangle(m, b, &midtri);
+    maketriangle(m, b, &tri1);
+    maketriangle(m, b, &tri2);
+    maketriangle(m, b, &tri3);
+    area = counterclockwise(m, b, sortarray[0], sortarray[1], sortarray[2]);
+    if (area == 0.0) {
+      /* Three collinear vertices; the triangulation is two edges. */
+      setorg(midtri, sortarray[0]);
+      setdest(midtri, sortarray[1]);
+      setorg(tri1, sortarray[1]);
+      setdest(tri1, sortarray[0]);
+      setorg(tri2, sortarray[2]);
+      setdest(tri2, sortarray[1]);
+      setorg(tri3, sortarray[1]);
+      setdest(tri3, sortarray[2]);
+      /* All apices are intentionally left NULL. */
+      bond(midtri, tri1);
+      bond(tri2, tri3);
+      lnextself(midtri);
+      lprevself(tri1);
+      lnextself(tri2);
+      lprevself(tri3);
+      bond(midtri, tri3);
+      bond(tri1, tri2);
+      lnextself(midtri);
+      lprevself(tri1);
+      lnextself(tri2);
+      lprevself(tri3);
+      bond(midtri, tri1);
+      bond(tri2, tri3);
+      /* Ensure that the origin of `farleft' is sortarray[0]. */
+      otricopy(tri1, *farleft);
+      /* Ensure that the destination of `farright' is sortarray[2]. */
+      otricopy(tri2, *farright);
+    } else {
+      /* The three vertices are not collinear; the triangulation is one */
+      /*   triangle, namely `midtri'.                                   */
+      setorg(midtri, sortarray[0]);
+      setdest(tri1, sortarray[0]);
+      setorg(tri3, sortarray[0]);
+      /* Apices of tri1, tri2, and tri3 are left NULL. */
+      if (area > 0.0) {
+        /* The vertices are in counterclockwise order. */
+        setdest(midtri, sortarray[1]);
+        setorg(tri1, sortarray[1]);
+        setdest(tri2, sortarray[1]);
+        setapex(midtri, sortarray[2]);
+        setorg(tri2, sortarray[2]);
+        setdest(tri3, sortarray[2]);
+      } else {
+        /* The vertices are in clockwise order. */
+        setdest(midtri, sortarray[2]);
+        setorg(tri1, sortarray[2]);
+        setdest(tri2, sortarray[2]);
+        setapex(midtri, sortarray[1]);
+        setorg(tri2, sortarray[1]);
+        setdest(tri3, sortarray[1]);
+      }
+      /* The topology does not depend on how the vertices are ordered. */
+      bond(midtri, tri1);
+      lnextself(midtri);
+      bond(midtri, tri2);
+      lnextself(midtri);
+      bond(midtri, tri3);
+      lprevself(tri1);
+      lnextself(tri2);
+      bond(tri1, tri2);
+      lprevself(tri1);
+      lprevself(tri3);
+      bond(tri1, tri3);
+      lnextself(tri2);
+      lprevself(tri3);
+      bond(tri2, tri3);
+      /* Ensure that the origin of `farleft' is sortarray[0]. */
+      otricopy(tri1, *farleft);
+      /* Ensure that the destination of `farright' is sortarray[2]. */
+      if (area > 0.0) {
+        otricopy(tri2, *farright);
+      } else {
+        lnext(*farleft, *farright);
+      }
+    }
+    if (b->verbose > 2) {
+      printf("  Creating ");
+      printtriangle(m, b, &midtri);
+      printf("  Creating ");
+      printtriangle(m, b, &tri1);
+      printf("  Creating ");
+      printtriangle(m, b, &tri2);
+      printf("  Creating ");
+      printtriangle(m, b, &tri3);
+    }
+    return;
+  } else {
+    /* Split the vertices in half. */
+    divider = vertices >> 1;
+    /* Recursively triangulate each half. */
+    divconqrecurse(m, b, sortarray, divider, 1 - axis, farleft, &innerleft);
+    divconqrecurse(m, b, &sortarray[divider], vertices - divider, 1 - axis,
+                   &innerright, farright);
+    if (b->verbose > 1) {
+      printf("  Joining triangulations with %d and %d vertices.\n", divider,
+             vertices - divider);
+    }
+    /* Merge the two triangulations into one. */
+    mergehulls(m, b, farleft, &innerleft, &innerright, farright, axis);
+  }
+}
+
+#ifdef ANSI_DECLARATORS
+long removeghosts(struct mesh *m, struct behavior *b, struct otri *startghost)
+#else /* not ANSI_DECLARATORS */
+long removeghosts(m, b, startghost)
+struct mesh *m;
+struct behavior *b;
+struct otri *startghost;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri searchedge;
+  struct otri dissolveedge;
+  struct otri deadtriangle;
+  vertex markorg;
+  long hullsize;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+
+  if (b->verbose) {
+    printf("  Removing ghost triangles.\n");
+  }
+  /* Find an edge on the convex hull to start point location from. */
+  lprev(*startghost, searchedge);
+  symself(searchedge);
+  m->dummytri[0] = encode(searchedge);
+  /* Remove the bounding box and count the convex hull edges. */
+  otricopy(*startghost, dissolveedge);
+  hullsize = 0;
+  do {
+    hullsize++;
+    lnext(dissolveedge, deadtriangle);
+    lprevself(dissolveedge);
+    symself(dissolveedge);
+    /* If no PSLG is involved, set the boundary markers of all the vertices */
+    /*   on the convex hull.  If a PSLG is used, this step is done later.   */
+    if (!b->poly) {
+      /* Watch out for the case where all the input vertices are collinear. */
+      if (dissolveedge.tri != m->dummytri) {
+        org(dissolveedge, markorg);
+        if (vertexmark(markorg) == 0) {
+          setvertexmark(markorg, 1);
+        }
+      }
+    }
+    /* Remove a bounding triangle from a convex hull triangle. */
+    dissolve(dissolveedge);
+    /* Find the next bounding triangle. */
+    sym(deadtriangle, dissolveedge);
+    /* Delete the bounding triangle. */
+    triangledealloc(m, deadtriangle.tri);
+  } while (!otriequal(dissolveedge, *startghost));
+  return hullsize;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  divconqdelaunay()   Form a Delaunay triangulation by the divide-and-     */
+/*                      conquer method.                                      */
+/*                                                                           */
+/*  Sorts the vertices, calls a recursive procedure to triangulate them, and */
+/*  removes the bounding box, setting boundary markers as appropriate.       */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+long divconqdelaunay(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+long divconqdelaunay(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  vertex *sortarray;
+  struct otri hullleft, hullright;
+  int divider;
+  int i, j;
+
+  if (b->verbose) {
+    printf("  Sorting vertices.\n");
+  }
+
+  /* Allocate an array of pointers to vertices for sorting. */
+  sortarray = (vertex *) trimalloc(m->invertices * (int) sizeof(vertex));
+  traversalinit(&m->vertices);
+  for (i = 0; i < m->invertices; i++) {
+    sortarray[i] = vertextraverse(m);
+  }
+  /* Sort the vertices. */
+  vertexsort(sortarray, m->invertices);
+  /* Discard duplicate vertices, which can really mess up the algorithm. */
+  i = 0;
+  for (j = 1; j < m->invertices; j++) {
+    if ((sortarray[i][0] == sortarray[j][0])
+        && (sortarray[i][1] == sortarray[j][1])) {
+      if (!b->quiet) {
+        printf(
+"Warning:  A duplicate vertex at (%.12g, %.12g) appeared and was ignored.\n",
+               sortarray[j][0], sortarray[j][1]);
+      }
+      setvertextype(sortarray[j], UNDEADVERTEX);
+      m->undeads++;
+    } else {
+      i++;
+      sortarray[i] = sortarray[j];
+    }
+  }
+  i++;
+  if (b->dwyer) {
+    /* Re-sort the array of vertices to accommodate alternating cuts. */
+    divider = i >> 1;
+    if (i - divider >= 2) {
+      if (divider >= 2) {
+        alternateaxes(sortarray, divider, 1);
+      }
+      alternateaxes(&sortarray[divider], i - divider, 1);
+    }
+  }
+
+  if (b->verbose) {
+    printf("  Forming triangulation.\n");
+  }
+
+  /* Form the Delaunay triangulation. */
+  divconqrecurse(m, b, sortarray, i, 0, &hullleft, &hullright);
+  trifree((VOID *) sortarray);
+
+  return removeghosts(m, b, &hullleft);
+}
+
+/**                                                                         **/
+/**                                                                         **/
+/********* Divide-and-conquer Delaunay triangulation ends here       *********/
+
+/********* Incremental Delaunay triangulation begins here            *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  boundingbox()   Form an "infinite" bounding triangle to insert vertices  */
+/*                  into.                                                    */
+/*                                                                           */
+/*  The vertices at "infinity" are assigned finite coordinates, which are    */
+/*  used by the point location routines, but (mostly) ignored by the         */
+/*  Delaunay edge flip routines.                                             */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+void boundingbox(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void boundingbox(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri inftri;          /* Handle for the triangular bounding box. */
+  REAL width;
+
+  if (b->verbose) {
+    printf("  Creating triangular bounding box.\n");
+  }
+  /* Find the width (or height, whichever is larger) of the triangulation. */
+  width = m->xmax - m->xmin;
+  if (m->ymax - m->ymin > width) {
+    width = m->ymax - m->ymin;
+  }
+  if (width == 0.0) {
+    width = 1.0;
+  }
+  /* Create the vertices of the bounding box. */
+  m->infvertex1 = (vertex) trimalloc(m->vertices.itembytes);
+  m->infvertex2 = (vertex) trimalloc(m->vertices.itembytes);
+  m->infvertex3 = (vertex) trimalloc(m->vertices.itembytes);
+  m->infvertex1[0] = m->xmin - 50.0 * width;
+  m->infvertex1[1] = m->ymin - 40.0 * width;
+  m->infvertex2[0] = m->xmax + 50.0 * width;
+  m->infvertex2[1] = m->ymin - 40.0 * width;
+  m->infvertex3[0] = 0.5 * (m->xmin + m->xmax);
+  m->infvertex3[1] = m->ymax + 60.0 * width;
+
+  /* Create the bounding box. */
+  maketriangle(m, b, &inftri);
+  setorg(inftri, m->infvertex1);
+  setdest(inftri, m->infvertex2);
+  setapex(inftri, m->infvertex3);
+  /* Link dummytri to the bounding box so we can always find an */
+  /*   edge to begin searching (point location) from.           */
+  m->dummytri[0] = (triangle) inftri.tri;
+  if (b->verbose > 2) {
+    printf("  Creating ");
+    printtriangle(m, b, &inftri);
+  }
+}
+
+#endif /* not REDUCED */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  removebox()   Remove the "infinite" bounding triangle, setting boundary  */
+/*                markers as appropriate.                                    */
+/*                                                                           */
+/*  The triangular bounding box has three boundary triangles (one for each   */
+/*  side of the bounding box), and a bunch of triangles fanning out from     */
+/*  the three bounding box vertices (one triangle for each edge of the       */
+/*  convex hull of the inner mesh).  This routine removes these triangles.   */
+/*                                                                           */
+/*  Returns the number of edges on the convex hull of the triangulation.     */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+long removebox(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+long removebox(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri deadtriangle;
+  struct otri searchedge;
+  struct otri checkedge;
+  struct otri nextedge, finaledge, dissolveedge;
+  vertex markorg;
+  long hullsize;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+
+  if (b->verbose) {
+    printf("  Removing triangular bounding box.\n");
+  }
+  /* Find a boundary triangle. */
+  nextedge.tri = m->dummytri;
+  nextedge.orient = 0;
+  symself(nextedge);
+  /* Mark a place to stop. */
+  lprev(nextedge, finaledge);
+  lnextself(nextedge);
+  symself(nextedge);
+  /* Find a triangle (on the boundary of the vertex set) that isn't */
+  /*   a bounding box triangle.                                     */
+  lprev(nextedge, searchedge);
+  symself(searchedge);
+  /* Check whether nextedge is another boundary triangle */
+  /*   adjacent to the first one.                        */
+  lnext(nextedge, checkedge);
+  symself(checkedge);
+  if (checkedge.tri == m->dummytri) {
+    /* Go on to the next triangle.  There are only three boundary   */
+    /*   triangles, and this next triangle cannot be the third one, */
+    /*   so it's safe to stop here.                                 */
+    lprevself(searchedge);
+    symself(searchedge);
+  }
+  /* Find a new boundary edge to search from, as the current search */
+  /*   edge lies on a bounding box triangle and will be deleted.    */
+  m->dummytri[0] = encode(searchedge);
+  hullsize = -2l;
+  while (!otriequal(nextedge, finaledge)) {
+    hullsize++;
+    lprev(nextedge, dissolveedge);
+    symself(dissolveedge);
+    /* If not using a PSLG, the vertices should be marked now. */
+    /*   (If using a PSLG, markhull() will do the job.)        */
+    if (!b->poly) {
+      /* Be careful!  One must check for the case where all the input     */
+      /*   vertices are collinear, and thus all the triangles are part of */
+      /*   the bounding box.  Otherwise, the setvertexmark() call below   */
+      /*   will cause a bad pointer reference.                            */
+      if (dissolveedge.tri != m->dummytri) {
+        org(dissolveedge, markorg);
+        if (vertexmark(markorg) == 0) {
+          setvertexmark(markorg, 1);
+        }
+      }
+    }
+    /* Disconnect the bounding box triangle from the mesh triangle. */
+    dissolve(dissolveedge);
+    lnext(nextedge, deadtriangle);
+    sym(deadtriangle, nextedge);
+    /* Get rid of the bounding box triangle. */
+    triangledealloc(m, deadtriangle.tri);
+    /* Do we need to turn the corner? */
+    if (nextedge.tri == m->dummytri) {
+      /* Turn the corner. */
+      otricopy(dissolveedge, nextedge);
+    }
+  }
+  triangledealloc(m, finaledge.tri);
+
+  trifree((VOID *) m->infvertex1);  /* Deallocate the bounding box vertices. */
+  trifree((VOID *) m->infvertex2);
+  trifree((VOID *) m->infvertex3);
+
+  return hullsize;
+}
+
+#endif /* not REDUCED */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  incrementaldelaunay()   Form a Delaunay triangulation by incrementally   */
+/*                          inserting vertices.                              */
+/*                                                                           */
+/*  Returns the number of edges on the convex hull of the triangulation.     */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+long incrementaldelaunay(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+long incrementaldelaunay(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri starttri;
+  vertex vertexloop;
+
+  /* Create a triangular bounding box. */
+  boundingbox(m, b);
+  if (b->verbose) {
+    printf("  Incrementally inserting vertices.\n");
+  }
+  traversalinit(&m->vertices);
+  vertexloop = vertextraverse(m);
+  while (vertexloop != (vertex) NULL) {
+    starttri.tri = m->dummytri;
+    if (insertvertex(m, b, vertexloop, &starttri, (struct osub *) NULL, 0, 0,
+                     0.0) == DUPLICATEVERTEX) {
+      if (!b->quiet) {
+        printf(
+"Warning:  A duplicate vertex at (%.12g, %.12g) appeared and was ignored.\n",
+               vertexloop[0], vertexloop[1]);
+      }
+      setvertextype(vertexloop, UNDEADVERTEX);
+      m->undeads++;
+    }
+    vertexloop = vertextraverse(m);
+  }
+  /* Remove the bounding box. */
+  return removebox(m, b);
+}
+
+#endif /* not REDUCED */
+
+/**                                                                         **/
+/**                                                                         **/
+/********* Incremental Delaunay triangulation ends here              *********/
+
+/********* Sweepline Delaunay triangulation begins here              *********/
+/**                                                                         **/
+/**                                                                         **/
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+void eventheapinsert(struct event **heap, int heapsize, struct event *newevent)
+#else /* not ANSI_DECLARATORS */
+void eventheapinsert(heap, heapsize, newevent)
+struct event **heap;
+int heapsize;
+struct event *newevent;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  REAL eventx, eventy;
+  int eventnum;
+  int parent;
+  int notdone;
+
+  eventx = newevent->xkey;
+  eventy = newevent->ykey;
+  eventnum = heapsize;
+  notdone = eventnum > 0;
+  while (notdone) {
+    parent = (eventnum - 1) >> 1;
+    if ((heap[parent]->ykey < eventy) ||
+        ((heap[parent]->ykey == eventy)
+         && (heap[parent]->xkey <= eventx))) {
+      notdone = 0;
+    } else {
+      heap[eventnum] = heap[parent];
+      heap[eventnum]->heapposition = eventnum;
+
+      eventnum = parent;
+      notdone = eventnum > 0;
+    }
+  }
+  heap[eventnum] = newevent;
+  newevent->heapposition = eventnum;
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+void eventheapify(struct event **heap, int heapsize, int eventnum)
+#else /* not ANSI_DECLARATORS */
+void eventheapify(heap, heapsize, eventnum)
+struct event **heap;
+int heapsize;
+int eventnum;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct event *thisevent;
+  REAL eventx, eventy;
+  int leftchild, rightchild;
+  int smallest;
+  int notdone;
+
+  thisevent = heap[eventnum];
+  eventx = thisevent->xkey;
+  eventy = thisevent->ykey;
+  leftchild = 2 * eventnum + 1;
+  notdone = leftchild < heapsize;
+  while (notdone) {
+    if ((heap[leftchild]->ykey < eventy) ||
+        ((heap[leftchild]->ykey == eventy)
+         && (heap[leftchild]->xkey < eventx))) {
+      smallest = leftchild;
+    } else {
+      smallest = eventnum;
+    }
+    rightchild = leftchild + 1;
+    if (rightchild < heapsize) {
+      if ((heap[rightchild]->ykey < heap[smallest]->ykey) ||
+          ((heap[rightchild]->ykey == heap[smallest]->ykey)
+           && (heap[rightchild]->xkey < heap[smallest]->xkey))) {
+        smallest = rightchild;
+      }
+    }
+    if (smallest == eventnum) {
+      notdone = 0;
+    } else {
+      heap[eventnum] = heap[smallest];
+      heap[eventnum]->heapposition = eventnum;
+      heap[smallest] = thisevent;
+      thisevent->heapposition = smallest;
+
+      eventnum = smallest;
+      leftchild = 2 * eventnum + 1;
+      notdone = leftchild < heapsize;
+    }
+  }
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+void eventheapdelete(struct event **heap, int heapsize, int eventnum)
+#else /* not ANSI_DECLARATORS */
+void eventheapdelete(heap, heapsize, eventnum)
+struct event **heap;
+int heapsize;
+int eventnum;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct event *moveevent;
+  REAL eventx, eventy;
+  int parent;
+  int notdone;
+
+  moveevent = heap[heapsize - 1];
+  if (eventnum > 0) {
+    eventx = moveevent->xkey;
+    eventy = moveevent->ykey;
+    do {
+      parent = (eventnum - 1) >> 1;
+      if ((heap[parent]->ykey < eventy) ||
+          ((heap[parent]->ykey == eventy)
+           && (heap[parent]->xkey <= eventx))) {
+        notdone = 0;
+      } else {
+        heap[eventnum] = heap[parent];
+        heap[eventnum]->heapposition = eventnum;
+
+        eventnum = parent;
+        notdone = eventnum > 0;
+      }
+    } while (notdone);
+  }
+  heap[eventnum] = moveevent;
+  moveevent->heapposition = eventnum;
+  eventheapify(heap, heapsize - 1, eventnum);
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+void createeventheap(struct mesh *m, struct event ***eventheap,
+                     struct event **events, struct event **freeevents)
+#else /* not ANSI_DECLARATORS */
+void createeventheap(m, eventheap, events, freeevents)
+struct mesh *m;
+struct event ***eventheap;
+struct event **events;
+struct event **freeevents;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  vertex thisvertex;
+  int maxevents;
+  int i;
+
+  maxevents = (3 * m->invertices) / 2;
+  *eventheap = (struct event **)
+               trimalloc(maxevents * (int) sizeof(struct event *));
+  *events = (struct event *) trimalloc(maxevents * (int) sizeof(struct event));
+  traversalinit(&m->vertices);
+  for (i = 0; i < m->invertices; i++) {
+    thisvertex = vertextraverse(m);
+    (*events)[i].eventptr = (VOID *) thisvertex;
+    (*events)[i].xkey = thisvertex[0];
+    (*events)[i].ykey = thisvertex[1];
+    eventheapinsert(*eventheap, i, *events + i);
+  }
+  *freeevents = (struct event *) NULL;
+  for (i = maxevents - 1; i >= m->invertices; i--) {
+    (*events)[i].eventptr = (VOID *) *freeevents;
+    *freeevents = *events + i;
+  }
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+int rightofhyperbola(struct mesh *m, struct otri *fronttri, vertex newsite)
+#else /* not ANSI_DECLARATORS */
+int rightofhyperbola(m, fronttri, newsite)
+struct mesh *m;
+struct otri *fronttri;
+vertex newsite;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  vertex leftvertex, rightvertex;
+  REAL dxa, dya, dxb, dyb;
+
+  m->hyperbolacount++;
+
+  dest(*fronttri, leftvertex);
+  apex(*fronttri, rightvertex);
+  if ((leftvertex[1] < rightvertex[1]) ||
+      ((leftvertex[1] == rightvertex[1]) &&
+       (leftvertex[0] < rightvertex[0]))) {
+    if (newsite[0] >= rightvertex[0]) {
+      return 1;
+    }
+  } else {
+    if (newsite[0] <= leftvertex[0]) {
+      return 0;
+    }
+  }
+  dxa = leftvertex[0] - newsite[0];
+  dya = leftvertex[1] - newsite[1];
+  dxb = rightvertex[0] - newsite[0];
+  dyb = rightvertex[1] - newsite[1];
+  return dya * (dxb * dxb + dyb * dyb) > dyb * (dxa * dxa + dya * dya);
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+REAL circletop(struct mesh *m, vertex pa, vertex pb, vertex pc, REAL ccwabc)
+#else /* not ANSI_DECLARATORS */
+REAL circletop(m, pa, pb, pc, ccwabc)
+struct mesh *m;
+vertex pa;
+vertex pb;
+vertex pc;
+REAL ccwabc;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  REAL xac, yac, xbc, ybc, xab, yab;
+  REAL aclen2, bclen2, ablen2;
+
+  m->circletopcount++;
+
+  xac = pa[0] - pc[0];
+  yac = pa[1] - pc[1];
+  xbc = pb[0] - pc[0];
+  ybc = pb[1] - pc[1];
+  xab = pa[0] - pb[0];
+  yab = pa[1] - pb[1];
+  aclen2 = xac * xac + yac * yac;
+  bclen2 = xbc * xbc + ybc * ybc;
+  ablen2 = xab * xab + yab * yab;
+  return pc[1] + (xac * bclen2 - xbc * aclen2 + sqrt(aclen2 * bclen2 * ablen2))
+               / (2.0 * ccwabc);
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+void check4deadevent(struct otri *checktri, struct event **freeevents,
+                     struct event **eventheap, int *heapsize)
+#else /* not ANSI_DECLARATORS */
+void check4deadevent(checktri, freeevents, eventheap, heapsize)
+struct otri *checktri;
+struct event **freeevents;
+struct event **eventheap;
+int *heapsize;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct event *deadevent;
+  vertex eventvertex;
+  int eventnum;
+
+  org(*checktri, eventvertex);
+  if (eventvertex != (vertex) NULL) {
+    deadevent = (struct event *) eventvertex;
+    eventnum = deadevent->heapposition;
+    deadevent->eventptr = (VOID *) *freeevents;
+    *freeevents = deadevent;
+    eventheapdelete(eventheap, *heapsize, eventnum);
+    (*heapsize)--;
+    setorg(*checktri, NULL);
+  }
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+struct splaynode *splay(struct mesh *m, struct splaynode *splaytree,
+                        vertex searchpoint, struct otri *searchtri)
+#else /* not ANSI_DECLARATORS */
+struct splaynode *splay(m, splaytree, searchpoint, searchtri)
+struct mesh *m;
+struct splaynode *splaytree;
+vertex searchpoint;
+struct otri *searchtri;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct splaynode *child, *grandchild;
+  struct splaynode *lefttree, *righttree;
+  struct splaynode *leftright;
+  vertex checkvertex;
+  int rightofroot, rightofchild;
+
+  if (splaytree == (struct splaynode *) NULL) {
+    return (struct splaynode *) NULL;
+  }
+  dest(splaytree->keyedge, checkvertex);
+  if (checkvertex == splaytree->keydest) {
+    rightofroot = rightofhyperbola(m, &splaytree->keyedge, searchpoint);
+    if (rightofroot) {
+      otricopy(splaytree->keyedge, *searchtri);
+      child = splaytree->rchild;
+    } else {
+      child = splaytree->lchild;
+    }
+    if (child == (struct splaynode *) NULL) {
+      return splaytree;
+    }
+    dest(child->keyedge, checkvertex);
+    if (checkvertex != child->keydest) {
+      child = splay(m, child, searchpoint, searchtri);
+      if (child == (struct splaynode *) NULL) {
+        if (rightofroot) {
+          splaytree->rchild = (struct splaynode *) NULL;
+        } else {
+          splaytree->lchild = (struct splaynode *) NULL;
+        }
+        return splaytree;
+      }
+    }
+    rightofchild = rightofhyperbola(m, &child->keyedge, searchpoint);
+    if (rightofchild) {
+      otricopy(child->keyedge, *searchtri);
+      grandchild = splay(m, child->rchild, searchpoint, searchtri);
+      child->rchild = grandchild;
+    } else {
+      grandchild = splay(m, child->lchild, searchpoint, searchtri);
+      child->lchild = grandchild;
+    }
+    if (grandchild == (struct splaynode *) NULL) {
+      if (rightofroot) {
+        splaytree->rchild = child->lchild;
+        child->lchild = splaytree;
+      } else {
+        splaytree->lchild = child->rchild;
+        child->rchild = splaytree;
+      }
+      return child;
+    }
+    if (rightofchild) {
+      if (rightofroot) {
+        splaytree->rchild = child->lchild;
+        child->lchild = splaytree;
+      } else {
+        splaytree->lchild = grandchild->rchild;
+        grandchild->rchild = splaytree;
+      }
+      child->rchild = grandchild->lchild;
+      grandchild->lchild = child;
+    } else {
+      if (rightofroot) {
+        splaytree->rchild = grandchild->lchild;
+        grandchild->lchild = splaytree;
+      } else {
+        splaytree->lchild = child->rchild;
+        child->rchild = splaytree;
+      }
+      child->lchild = grandchild->rchild;
+      grandchild->rchild = child;
+    }
+    return grandchild;
+  } else {
+    lefttree = splay(m, splaytree->lchild, searchpoint, searchtri);
+    righttree = splay(m, splaytree->rchild, searchpoint, searchtri);
+
+    pooldealloc(&m->splaynodes, (VOID *) splaytree);
+    if (lefttree == (struct splaynode *) NULL) {
+      return righttree;
+    } else if (righttree == (struct splaynode *) NULL) {
+      return lefttree;
+    } else if (lefttree->rchild == (struct splaynode *) NULL) {
+      lefttree->rchild = righttree->lchild;
+      righttree->lchild = lefttree;
+      return righttree;
+    } else if (righttree->lchild == (struct splaynode *) NULL) {
+      righttree->lchild = lefttree->rchild;
+      lefttree->rchild = righttree;
+      return lefttree;
+    } else {
+/*      printf("Holy Toledo!!!\n"); */
+      leftright = lefttree->rchild;
+      while (leftright->rchild != (struct splaynode *) NULL) {
+        leftright = leftright->rchild;
+      }
+      leftright->rchild = righttree;
+      return lefttree;
+    }
+  }
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+struct splaynode *splayinsert(struct mesh *m, struct splaynode *splayroot,
+                              struct otri *newkey, vertex searchpoint)
+#else /* not ANSI_DECLARATORS */
+struct splaynode *splayinsert(m, splayroot, newkey, searchpoint)
+struct mesh *m;
+struct splaynode *splayroot;
+struct otri *newkey;
+vertex searchpoint;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct splaynode *newsplaynode;
+
+  newsplaynode = (struct splaynode *) poolalloc(&m->splaynodes);
+  otricopy(*newkey, newsplaynode->keyedge);
+  dest(*newkey, newsplaynode->keydest);
+  if (splayroot == (struct splaynode *) NULL) {
+    newsplaynode->lchild = (struct splaynode *) NULL;
+    newsplaynode->rchild = (struct splaynode *) NULL;
+  } else if (rightofhyperbola(m, &splayroot->keyedge, searchpoint)) {
+    newsplaynode->lchild = splayroot;
+    newsplaynode->rchild = splayroot->rchild;
+    splayroot->rchild = (struct splaynode *) NULL;
+  } else {
+    newsplaynode->lchild = splayroot->lchild;
+    newsplaynode->rchild = splayroot;
+    splayroot->lchild = (struct splaynode *) NULL;
+  }
+  return newsplaynode;
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+struct splaynode *circletopinsert(struct mesh *m, struct behavior *b,
+                                  struct splaynode *splayroot,
+                                  struct otri *newkey,
+                                  vertex pa, vertex pb, vertex pc, REAL topy)
+#else /* not ANSI_DECLARATORS */
+struct splaynode *circletopinsert(m, b, splayroot, newkey, pa, pb, pc, topy)
+struct mesh *m;
+struct behavior *b;
+struct splaynode *splayroot;
+struct otri *newkey;
+vertex pa;
+vertex pb;
+vertex pc;
+REAL topy;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  REAL ccwabc;
+  REAL xac, yac, xbc, ybc;
+  REAL aclen2, bclen2;
+  REAL searchpoint[2];
+  struct otri dummytri;
+
+  ccwabc = counterclockwise(m, b, pa, pb, pc);
+  xac = pa[0] - pc[0];
+  yac = pa[1] - pc[1];
+  xbc = pb[0] - pc[0];
+  ybc = pb[1] - pc[1];
+  aclen2 = xac * xac + yac * yac;
+  bclen2 = xbc * xbc + ybc * ybc;
+  searchpoint[0] = pc[0] - (yac * bclen2 - ybc * aclen2) / (2.0 * ccwabc);
+  searchpoint[1] = topy;
+  return splayinsert(m, splay(m, splayroot, (vertex) searchpoint, &dummytri),
+                     newkey, (vertex) searchpoint);
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+struct splaynode *frontlocate(struct mesh *m, struct splaynode *splayroot,
+                              struct otri *bottommost, vertex searchvertex,
+                              struct otri *searchtri, int *farright)
+#else /* not ANSI_DECLARATORS */
+struct splaynode *frontlocate(m, splayroot, bottommost, searchvertex,
+                              searchtri, farright)
+struct mesh *m;
+struct splaynode *splayroot;
+struct otri *bottommost;
+vertex searchvertex;
+struct otri *searchtri;
+int *farright;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  int farrightflag;
+  triangle ptr;                       /* Temporary variable used by onext(). */
+
+  otricopy(*bottommost, *searchtri);
+  splayroot = splay(m, splayroot, searchvertex, searchtri);
+
+  farrightflag = 0;
+  while (!farrightflag && rightofhyperbola(m, searchtri, searchvertex)) {
+    onextself(*searchtri);
+    farrightflag = otriequal(*searchtri, *bottommost);
+  }
+  *farright = farrightflag;
+  return splayroot;
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+long sweeplinedelaunay(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+long sweeplinedelaunay(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct event **eventheap;
+  struct event *events;
+  struct event *freeevents;
+  struct event *nextevent;
+  struct event *newevent;
+  struct splaynode *splayroot;
+  struct otri bottommost;
+  struct otri searchtri;
+  struct otri fliptri;
+  struct otri lefttri, righttri, farlefttri, farrighttri;
+  struct otri inserttri;
+  vertex firstvertex, secondvertex;
+  vertex nextvertex, lastvertex;
+  vertex connectvertex;
+  vertex leftvertex, midvertex, rightvertex;
+  REAL lefttest, righttest;
+  int heapsize;
+  int check4events, farrightflag;
+  triangle ptr;   /* Temporary variable used by sym(), onext(), and oprev(). */
+
+  poolinit(&m->splaynodes, sizeof(struct splaynode), SPLAYNODEPERBLOCK,
+           SPLAYNODEPERBLOCK, POINTER, 0);
+  splayroot = (struct splaynode *) NULL;
+
+  if (b->verbose) {
+    printf("  Placing vertices in event heap.\n");
+  }
+  createeventheap(m, &eventheap, &events, &freeevents);
+  heapsize = m->invertices;
+
+  if (b->verbose) {
+    printf("  Forming triangulation.\n");
+  }
+  maketriangle(m, b, &lefttri);
+  maketriangle(m, b, &righttri);
+  bond(lefttri, righttri);
+  lnextself(lefttri);
+  lprevself(righttri);
+  bond(lefttri, righttri);
+  lnextself(lefttri);
+  lprevself(righttri);
+  bond(lefttri, righttri);
+  firstvertex = (vertex) eventheap[0]->eventptr;
+  eventheap[0]->eventptr = (VOID *) freeevents;
+  freeevents = eventheap[0];
+  eventheapdelete(eventheap, heapsize, 0);
+  heapsize--;
+  do {
+    if (heapsize == 0) {
+      printf("Error:  Input vertices are all identical.\n");
+      exit(1);
+    }
+    secondvertex = (vertex) eventheap[0]->eventptr;
+    eventheap[0]->eventptr = (VOID *) freeevents;
+    freeevents = eventheap[0];
+    eventheapdelete(eventheap, heapsize, 0);
+    heapsize--;
+    if ((firstvertex[0] == secondvertex[0]) &&
+        (firstvertex[1] == secondvertex[1])) {
+      if (!b->quiet) {
+        printf(
+"Warning:  A duplicate vertex at (%.12g, %.12g) appeared and was ignored.\n",
+               secondvertex[0], secondvertex[1]);
+      }
+      setvertextype(secondvertex, UNDEADVERTEX);
+      m->undeads++;
+    }
+  } while ((firstvertex[0] == secondvertex[0]) &&
+           (firstvertex[1] == secondvertex[1]));
+  setorg(lefttri, firstvertex);
+  setdest(lefttri, secondvertex);
+  setorg(righttri, secondvertex);
+  setdest(righttri, firstvertex);
+  lprev(lefttri, bottommost);
+  lastvertex = secondvertex;
+  while (heapsize > 0) {
+    nextevent = eventheap[0];
+    eventheapdelete(eventheap, heapsize, 0);
+    heapsize--;
+    check4events = 1;
+    if (nextevent->xkey < m->xmin) {
+      decode(nextevent->eventptr, fliptri);
+      oprev(fliptri, farlefttri);
+      check4deadevent(&farlefttri, &freeevents, eventheap, &heapsize);
+      onext(fliptri, farrighttri);
+      check4deadevent(&farrighttri, &freeevents, eventheap, &heapsize);
+
+      if (otriequal(farlefttri, bottommost)) {
+        lprev(fliptri, bottommost);
+      }
+      flip(m, b, &fliptri);
+      setapex(fliptri, NULL);
+      lprev(fliptri, lefttri);
+      lnext(fliptri, righttri);
+      sym(lefttri, farlefttri);
+
+      if (randomnation(SAMPLERATE) == 0) {
+        symself(fliptri);
+        dest(fliptri, leftvertex);
+        apex(fliptri, midvertex);
+        org(fliptri, rightvertex);
+        splayroot = circletopinsert(m, b, splayroot, &lefttri, leftvertex,
+                                    midvertex, rightvertex, nextevent->ykey);
+      }
+    } else {
+      nextvertex = (vertex) nextevent->eventptr;
+      if ((nextvertex[0] == lastvertex[0]) &&
+          (nextvertex[1] == lastvertex[1])) {
+        if (!b->quiet) {
+          printf(
+"Warning:  A duplicate vertex at (%.12g, %.12g) appeared and was ignored.\n",
+                 nextvertex[0], nextvertex[1]);
+        }
+        setvertextype(nextvertex, UNDEADVERTEX);
+        m->undeads++;
+        check4events = 0;
+      } else {
+        lastvertex = nextvertex;
+
+        splayroot = frontlocate(m, splayroot, &bottommost, nextvertex,
+                                &searchtri, &farrightflag);
+/*
+        otricopy(bottommost, searchtri);
+        farrightflag = 0;
+        while (!farrightflag && rightofhyperbola(m, &searchtri, nextvertex)) {
+          onextself(searchtri);
+          farrightflag = otriequal(searchtri, bottommost);
+        }
+*/
+
+        check4deadevent(&searchtri, &freeevents, eventheap, &heapsize);
+
+        otricopy(searchtri, farrighttri);
+        sym(searchtri, farlefttri);
+        maketriangle(m, b, &lefttri);
+        maketriangle(m, b, &righttri);
+        dest(farrighttri, connectvertex);
+        setorg(lefttri, connectvertex);
+        setdest(lefttri, nextvertex);
+        setorg(righttri, nextvertex);
+        setdest(righttri, connectvertex);
+        bond(lefttri, righttri);
+        lnextself(lefttri);
+        lprevself(righttri);
+        bond(lefttri, righttri);
+        lnextself(lefttri);
+        lprevself(righttri);
+        bond(lefttri, farlefttri);
+        bond(righttri, farrighttri);
+        if (!farrightflag && otriequal(farrighttri, bottommost)) {
+          otricopy(lefttri, bottommost);
+        }
+
+        if (randomnation(SAMPLERATE) == 0) {
+          splayroot = splayinsert(m, splayroot, &lefttri, nextvertex);
+        } else if (randomnation(SAMPLERATE) == 0) {
+          lnext(righttri, inserttri);
+          splayroot = splayinsert(m, splayroot, &inserttri, nextvertex);
+        }
+      }
+    }
+    nextevent->eventptr = (VOID *) freeevents;
+    freeevents = nextevent;
+
+    if (check4events) {
+      apex(farlefttri, leftvertex);
+      dest(lefttri, midvertex);
+      apex(lefttri, rightvertex);
+      lefttest = counterclockwise(m, b, leftvertex, midvertex, rightvertex);
+      if (lefttest > 0.0) {
+        newevent = freeevents;
+        freeevents = (struct event *) freeevents->eventptr;
+        newevent->xkey = m->xminextreme;
+        newevent->ykey = circletop(m, leftvertex, midvertex, rightvertex,
+                                   lefttest);
+        newevent->eventptr = (VOID *) encode(lefttri);
+        eventheapinsert(eventheap, heapsize, newevent);
+        heapsize++;
+        setorg(lefttri, newevent);
+      }
+      apex(righttri, leftvertex);
+      org(righttri, midvertex);
+      apex(farrighttri, rightvertex);
+      righttest = counterclockwise(m, b, leftvertex, midvertex, rightvertex);
+      if (righttest > 0.0) {
+        newevent = freeevents;
+        freeevents = (struct event *) freeevents->eventptr;
+        newevent->xkey = m->xminextreme;
+        newevent->ykey = circletop(m, leftvertex, midvertex, rightvertex,
+                                   righttest);
+        newevent->eventptr = (VOID *) encode(farrighttri);
+        eventheapinsert(eventheap, heapsize, newevent);
+        heapsize++;
+        setorg(farrighttri, newevent);
+      }
+    }
+  }
+
+  pooldeinit(&m->splaynodes);
+  lprevself(bottommost);
+  return removeghosts(m, b, &bottommost);
+}
+
+#endif /* not REDUCED */
+
+/**                                                                         **/
+/**                                                                         **/
+/********* Sweepline Delaunay triangulation ends here                *********/
+
+/********* General mesh construction routines begin here             *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  delaunay()   Form a Delaunay triangulation.                              */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+long delaunay(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+long delaunay(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  long hulledges;
+
+  m->eextras = 0;
+  initializetrisubpools(m, b);
+
+#ifdef REDUCED
+  if (!b->quiet) {
+    printf(
+      "Constructing Delaunay triangulation by divide-and-conquer method.\n");
+  }
+  hulledges = divconqdelaunay(m, b);
+#else /* not REDUCED */
+  if (!b->quiet) {
+    printf("Constructing Delaunay triangulation ");
+    if (b->incremental) {
+      printf("by incremental method.\n");
+    } else if (b->sweepline) {
+      printf("by sweepline method.\n");
+    } else {
+      printf("by divide-and-conquer method.\n");
+    }
+  }
+  if (b->incremental) {
+    hulledges = incrementaldelaunay(m, b);
+  } else if (b->sweepline) {
+    hulledges = sweeplinedelaunay(m, b);
+  } else {
+    hulledges = divconqdelaunay(m, b);
+  }
+#endif /* not REDUCED */
+
+  if (m->triangles.items == 0) {
+    /* The input vertices were all collinear, so there are no triangles. */
+    return 0l;
+  } else {
+    return hulledges;
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  reconstruct()   Reconstruct a triangulation from its .ele (and possibly  */
+/*                  .poly) file.  Used when the -r switch is used.           */
+/*                                                                           */
+/*  Reads an .ele file and reconstructs the original mesh.  If the -p switch */
+/*  is used, this procedure will also read a .poly file and reconstruct the  */
+/*  subsegments of the original mesh.  If the -a switch is used, this        */
+/*  procedure will also read an .area file and set a maximum area constraint */
+/*  on each triangle.                                                        */
+/*                                                                           */
+/*  Vertices that are not corners of triangles, such as nodes on edges of    */
+/*  subparametric elements, are discarded.                                   */
+/*                                                                           */
+/*  This routine finds the adjacencies between triangles (and subsegments)   */
+/*  by forming one stack of triangles for each vertex.  Each triangle is on  */
+/*  three different stacks simultaneously.  Each triangle's subsegment       */
+/*  pointers are used to link the items in each stack.  This memory-saving   */
+/*  feature makes the code harder to read.  The most important thing to keep */
+/*  in mind is that each triangle is removed from a stack precisely when     */
+/*  the corresponding pointer is adjusted to refer to a subsegment rather    */
+/*  than the next triangle of the stack.                                     */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+int reconstruct(struct mesh *m, struct behavior *b, int *trianglelist,
+                REAL *triangleattriblist, REAL *trianglearealist,
+                int elements, int corners, int attribs,
+                int *segmentlist,int *segmentmarkerlist, int numberofsegments)
+#else /* not ANSI_DECLARATORS */
+int reconstruct(m, b, trianglelist, triangleattriblist, trianglearealist,
+                elements, corners, attribs, segmentlist, segmentmarkerlist,
+                numberofsegments)
+struct mesh *m;
+struct behavior *b;
+int *trianglelist;
+REAL *triangleattriblist;
+REAL *trianglearealist;
+int elements;
+int corners;
+int attribs;
+int *segmentlist;
+int *segmentmarkerlist;
+int numberofsegments;
+#endif /* not ANSI_DECLARATORS */
+
+#else /* not TRILIBRARY */
+
+#ifdef ANSI_DECLARATORS
+long reconstruct(struct mesh *m, struct behavior *b, char *elefilename,
+                 char *areafilename, char *polyfilename, FILE *polyfile)
+#else /* not ANSI_DECLARATORS */
+long reconstruct(m, b, elefilename, areafilename, polyfilename, polyfile)
+struct mesh *m;
+struct behavior *b;
+char *elefilename;
+char *areafilename;
+char *polyfilename;
+FILE *polyfile;
+#endif /* not ANSI_DECLARATORS */
+
+#endif /* not TRILIBRARY */
+
+{
+#ifdef TRILIBRARY
+  int vertexindex;
+  int attribindex;
+#else /* not TRILIBRARY */
+  FILE *elefile;
+  FILE *areafile;
+  char inputline[INPUTLINESIZE];
+  char *stringptr;
+  int areaelements;
+#endif /* not TRILIBRARY */
+  struct otri triangleloop;
+  struct otri triangleleft;
+  struct otri checktri;
+  struct otri checkleft;
+  struct otri checkneighbor;
+  struct osub subsegloop;
+  triangle *vertexarray;
+  triangle *prevlink;
+  triangle nexttri;
+  vertex tdest, tapex;
+  vertex checkdest, checkapex;
+  vertex shorg;
+  vertex killvertex;
+  REAL area;
+  int corner[3];
+  int end[2];
+  int killvertexindex;
+  int incorners;
+  int segmentmarkers;
+  int boundmarker;
+  int aroundvertex;
+  long hullsize;
+  int notfound;
+  long elementnumber, segmentnumber;
+  int i, j;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+
+#ifdef TRILIBRARY
+  m->inelements = elements;
+  incorners = corners;
+  if (incorners < 3) {
+    printf("Error:  Triangles must have at least 3 vertices.\n");
+    exit(1);
+  }
+  m->eextras = attribs;
+#else /* not TRILIBRARY */
+  /* Read the triangles from an .ele file. */
+  if (!b->quiet) {
+    printf("Opening %s.\n", elefilename);
+  }
+  elefile = fopen(elefilename, "r");
+  if (elefile == (FILE *) NULL) {
+    printf("  Error:  Cannot access file %s.\n", elefilename);
+    exit(1);
+  }
+  /* Read number of triangles, number of vertices per triangle, and */
+  /*   number of triangle attributes from .ele file.                */
+  stringptr = readline(inputline, elefile, elefilename);
+  m->inelements = (int) strtol(stringptr, &stringptr, 0);
+  stringptr = findfield(stringptr);
+  if (*stringptr == '\0') {
+    incorners = 3;
+  } else {
+    incorners = (int) strtol(stringptr, &stringptr, 0);
+    if (incorners < 3) {
+      printf("Error:  Triangles in %s must have at least 3 vertices.\n",
+             elefilename);
+      exit(1);
+    }
+  }
+  stringptr = findfield(stringptr);
+  if (*stringptr == '\0') {
+    m->eextras = 0;
+  } else {
+    m->eextras = (int) strtol(stringptr, &stringptr, 0);
+  }
+#endif /* not TRILIBRARY */
+
+  initializetrisubpools(m, b);
+
+  /* Create the triangles. */
+  for (elementnumber = 1; elementnumber <= m->inelements; elementnumber++) {
+    maketriangle(m, b, &triangleloop);
+    /* Mark the triangle as living. */
+    triangleloop.tri[3] = (triangle) triangleloop.tri;
+  }
+
+  if (b->poly) {
+#ifdef TRILIBRARY
+    m->insegments = numberofsegments;
+    segmentmarkers = segmentmarkerlist != (int *) NULL;
+#else /* not TRILIBRARY */
+    /* Read number of segments and number of segment */
+    /*   boundary markers from .poly file.           */
+    stringptr = readline(inputline, polyfile, b->inpolyfilename);
+    m->insegments = (int) strtol(stringptr, &stringptr, 0);
+    stringptr = findfield(stringptr);
+    if (*stringptr == '\0') {
+      segmentmarkers = 0;
+    } else {
+      segmentmarkers = (int) strtol(stringptr, &stringptr, 0);
+    }
+#endif /* not TRILIBRARY */
+
+    /* Create the subsegments. */
+    for (segmentnumber = 1; segmentnumber <= m->insegments; segmentnumber++) {
+      makesubseg(m, &subsegloop);
+      /* Mark the subsegment as living. */
+      subsegloop.ss[2] = (subseg) subsegloop.ss;
+    }
+  }
+
+#ifdef TRILIBRARY
+  vertexindex = 0;
+  attribindex = 0;
+#else /* not TRILIBRARY */
+  if (b->vararea) {
+    /* Open an .area file, check for consistency with the .ele file. */
+    if (!b->quiet) {
+      printf("Opening %s.\n", areafilename);
+    }
+    areafile = fopen(areafilename, "r");
+    if (areafile == (FILE *) NULL) {
+      printf("  Error:  Cannot access file %s.\n", areafilename);
+      exit(1);
+    }
+    stringptr = readline(inputline, areafile, areafilename);
+    areaelements = (int) strtol(stringptr, &stringptr, 0);
+    if (areaelements != m->inelements) {
+      printf("Error:  %s and %s disagree on number of triangles.\n",
+             elefilename, areafilename);
+      exit(1);
+    }
+  }
+#endif /* not TRILIBRARY */
+
+  if (!b->quiet) {
+    printf("Reconstructing mesh.\n");
+  }
+  /* Allocate a temporary array that maps each vertex to some adjacent */
+  /*   triangle.  I took care to allocate all the permanent memory for */
+  /*   triangles and subsegments first.                                */
+  vertexarray = (triangle *)
+                trimalloc(m->vertices.items * (int) sizeof(triangle));
+  /* Each vertex is initially unrepresented. */
+  for (i = 0; i < m->vertices.items; i++) {
+    vertexarray[i] = (triangle) m->dummytri;
+  }
+
+  if (b->verbose) {
+    printf("  Assembling triangles.\n");
+  }
+  /* Read the triangles from the .ele file, and link */
+  /*   together those that share an edge.            */
+  traversalinit(&m->triangles);
+  triangleloop.tri = triangletraverse(m);
+  elementnumber = b->firstnumber;
+  while (triangleloop.tri != (triangle *) NULL) {
+#ifdef TRILIBRARY
+    /* Copy the triangle's three corners. */
+    for (j = 0; j < 3; j++) {
+      corner[j] = trianglelist[vertexindex++];
+      if ((corner[j] < b->firstnumber) ||
+          (corner[j] >= b->firstnumber + m->invertices)) {
+        printf("Error:  Triangle %ld has an invalid vertex index.\n",
+               elementnumber);
+        exit(1);
+      }
+    }
+#else /* not TRILIBRARY */
+    /* Read triangle number and the triangle's three corners. */
+    stringptr = readline(inputline, elefile, elefilename);
+    for (j = 0; j < 3; j++) {
+      stringptr = findfield(stringptr);
+      if (*stringptr == '\0') {
+        printf("Error:  Triangle %ld is missing vertex %d in %s.\n",
+               elementnumber, j + 1, elefilename);
+        exit(1);
+      } else {
+        corner[j] = (int) strtol(stringptr, &stringptr, 0);
+        if ((corner[j] < b->firstnumber) ||
+            (corner[j] >= b->firstnumber + m->invertices)) {
+          printf("Error:  Triangle %ld has an invalid vertex index.\n",
+                 elementnumber);
+          exit(1);
+        }
+      }
+    }
+#endif /* not TRILIBRARY */
+
+    /* Find out about (and throw away) extra nodes. */
+    for (j = 3; j < incorners; j++) {
+#ifdef TRILIBRARY
+      killvertexindex = trianglelist[vertexindex++];
+#else /* not TRILIBRARY */
+      stringptr = findfield(stringptr);
+      if (*stringptr != '\0') {
+        killvertexindex = (int) strtol(stringptr, &stringptr, 0);
+#endif /* not TRILIBRARY */
+        if ((killvertexindex >= b->firstnumber) &&
+            (killvertexindex < b->firstnumber + m->invertices)) {
+          /* Delete the non-corner vertex if it's not already deleted. */
+          killvertex = getvertex(m, b, killvertexindex);
+          if (vertextype(killvertex) != DEADVERTEX) {
+            vertexdealloc(m, killvertex);
+          }
+        }
+#ifndef TRILIBRARY
+      }
+#endif /* not TRILIBRARY */
+    }
+
+    /* Read the triangle's attributes. */
+    for (j = 0; j < m->eextras; j++) {
+#ifdef TRILIBRARY
+      setelemattribute(triangleloop, j, triangleattriblist[attribindex++]);
+#else /* not TRILIBRARY */
+      stringptr = findfield(stringptr);
+      if (*stringptr == '\0') {
+        setelemattribute(triangleloop, j, 0);
+      } else {
+        setelemattribute(triangleloop, j,
+                         (REAL) strtod(stringptr, &stringptr));
+      }
+#endif /* not TRILIBRARY */
+    }
+
+    if (b->vararea) {
+#ifdef TRILIBRARY
+      area = trianglearealist[elementnumber - b->firstnumber];
+#else /* not TRILIBRARY */
+      /* Read an area constraint from the .area file. */
+      stringptr = readline(inputline, areafile, areafilename);
+      stringptr = findfield(stringptr);
+      if (*stringptr == '\0') {
+        area = -1.0;                      /* No constraint on this triangle. */
+      } else {
+        area = (REAL) strtod(stringptr, &stringptr);
+      }
+#endif /* not TRILIBRARY */
+      setareabound(triangleloop, area);
+    }
+
+    /* Set the triangle's vertices. */
+    triangleloop.orient = 0;
+    setorg(triangleloop, getvertex(m, b, corner[0]));
+    setdest(triangleloop, getvertex(m, b, corner[1]));
+    setapex(triangleloop, getvertex(m, b, corner[2]));
+    /* Try linking the triangle to others that share these vertices. */
+    for (triangleloop.orient = 0; triangleloop.orient < 3;
+         triangleloop.orient++) {
+      /* Take the number for the origin of triangleloop. */
+      aroundvertex = corner[triangleloop.orient];
+      /* Look for other triangles having this vertex. */
+      nexttri = vertexarray[aroundvertex - b->firstnumber];
+      /* Link the current triangle to the next one in the stack. */
+      triangleloop.tri[6 + triangleloop.orient] = nexttri;
+      /* Push the current triangle onto the stack. */
+      vertexarray[aroundvertex - b->firstnumber] = encode(triangleloop);
+      decode(nexttri, checktri);
+      if (checktri.tri != m->dummytri) {
+        dest(triangleloop, tdest);
+        apex(triangleloop, tapex);
+        /* Look for other triangles that share an edge. */
+        do {
+          dest(checktri, checkdest);
+          apex(checktri, checkapex);
+          if (tapex == checkdest) {
+            /* The two triangles share an edge; bond them together. */
+            lprev(triangleloop, triangleleft);
+            bond(triangleleft, checktri);
+          }
+          if (tdest == checkapex) {
+            /* The two triangles share an edge; bond them together. */
+            lprev(checktri, checkleft);
+            bond(triangleloop, checkleft);
+          }
+          /* Find the next triangle in the stack. */
+          nexttri = checktri.tri[6 + checktri.orient];
+          decode(nexttri, checktri);
+        } while (checktri.tri != m->dummytri);
+      }
+    }
+    triangleloop.tri = triangletraverse(m);
+    elementnumber++;
+  }
+
+#ifdef TRILIBRARY
+  vertexindex = 0;
+#else /* not TRILIBRARY */
+  fclose(elefile);
+  if (b->vararea) {
+    fclose(areafile);
+  }
+#endif /* not TRILIBRARY */
+
+  hullsize = 0;                      /* Prepare to count the boundary edges. */
+  if (b->poly) {
+    if (b->verbose) {
+      printf("  Marking segments in triangulation.\n");
+    }
+    /* Read the segments from the .poly file, and link them */
+    /*   to their neighboring triangles.                    */
+    boundmarker = 0;
+    traversalinit(&m->subsegs);
+    subsegloop.ss = subsegtraverse(m);
+    segmentnumber = b->firstnumber;
+    while (subsegloop.ss != (subseg *) NULL) {
+#ifdef TRILIBRARY
+      end[0] = segmentlist[vertexindex++];
+      end[1] = segmentlist[vertexindex++];
+      if (segmentmarkers) {
+        boundmarker = segmentmarkerlist[segmentnumber - b->firstnumber];
+      }
+#else /* not TRILIBRARY */
+      /* Read the endpoints of each segment, and possibly a boundary marker. */
+      stringptr = readline(inputline, polyfile, b->inpolyfilename);
+      /* Skip the first (segment number) field. */
+      stringptr = findfield(stringptr);
+      if (*stringptr == '\0') {
+        printf("Error:  Segment %ld has no endpoints in %s.\n", segmentnumber,
+               polyfilename);
+        exit(1);
+      } else {
+        end[0] = (int) strtol(stringptr, &stringptr, 0);
+      }
+      stringptr = findfield(stringptr);
+      if (*stringptr == '\0') {
+        printf("Error:  Segment %ld is missing its second endpoint in %s.\n",
+               segmentnumber, polyfilename);
+        exit(1);
+      } else {
+        end[1] = (int) strtol(stringptr, &stringptr, 0);
+      }
+      if (segmentmarkers) {
+        stringptr = findfield(stringptr);
+        if (*stringptr == '\0') {
+          boundmarker = 0;
+        } else {
+          boundmarker = (int) strtol(stringptr, &stringptr, 0);
+        }
+      }
+#endif /* not TRILIBRARY */
+      for (j = 0; j < 2; j++) {
+        if ((end[j] < b->firstnumber) ||
+            (end[j] >= b->firstnumber + m->invertices)) {
+          printf("Error:  Segment %ld has an invalid vertex index.\n", 
+                 segmentnumber);
+          exit(1);
+        }
+      }
+
+      /* set the subsegment's vertices. */
+      subsegloop.ssorient = 0;
+      setsorg(subsegloop, getvertex(m, b, end[0]));
+      setsdest(subsegloop, getvertex(m, b, end[1]));
+      setmark(subsegloop, boundmarker);
+      /* Try linking the subsegment to triangles that share these vertices. */
+      for (subsegloop.ssorient = 0; subsegloop.ssorient < 2;
+           subsegloop.ssorient++) {
+        /* Take the number for the destination of subsegloop. */
+        aroundvertex = end[1 - subsegloop.ssorient];
+        /* Look for triangles having this vertex. */
+        prevlink = &vertexarray[aroundvertex - b->firstnumber];
+        nexttri = vertexarray[aroundvertex - b->firstnumber];
+        decode(nexttri, checktri);
+        sorg(subsegloop, shorg);
+        notfound = 1;
+        /* Look for triangles having this edge.  Note that I'm only       */
+        /*   comparing each triangle's destination with the subsegment;   */
+        /*   each triangle's apex is handled through a different vertex.  */
+        /*   Because each triangle appears on three vertices' lists, each */
+        /*   occurrence of a triangle on a list can (and does) represent  */
+        /*   an edge.  In this way, most edges are represented twice, and */
+        /*   every triangle-subsegment bond is represented once.          */
+        while (notfound && (checktri.tri != m->dummytri)) {
+          dest(checktri, checkdest);
+          if (shorg == checkdest) {
+            /* We have a match.  Remove this triangle from the list. */
+            *prevlink = checktri.tri[6 + checktri.orient];
+            /* Bond the subsegment to the triangle. */
+            tsbond(checktri, subsegloop);
+            /* Check if this is a boundary edge. */
+            sym(checktri, checkneighbor);
+            if (checkneighbor.tri == m->dummytri) {
+              /* The next line doesn't insert a subsegment (because there's */
+              /*   already one there), but it sets the boundary markers of  */
+              /*   the existing subsegment and its vertices.                */
+              insertsubseg(m, b, &checktri, 1);
+              hullsize++;
+            }
+            notfound = 0;
+          }
+          /* Find the next triangle in the stack. */
+          prevlink = &checktri.tri[6 + checktri.orient];
+          nexttri = checktri.tri[6 + checktri.orient];
+          decode(nexttri, checktri);
+        }
+      }
+      subsegloop.ss = subsegtraverse(m);
+      segmentnumber++;
+    }
+  }
+
+  /* Mark the remaining edges as not being attached to any subsegment. */
+  /* Also, count the (yet uncounted) boundary edges.                   */
+  for (i = 0; i < m->vertices.items; i++) {
+    /* Search the stack of triangles adjacent to a vertex. */
+    nexttri = vertexarray[i];
+    decode(nexttri, checktri);
+    while (checktri.tri != m->dummytri) {
+      /* Find the next triangle in the stack before this */
+      /*   information gets overwritten.                 */
+      nexttri = checktri.tri[6 + checktri.orient];
+      /* No adjacent subsegment.  (This overwrites the stack info.) */
+      tsdissolve(checktri);
+      sym(checktri, checkneighbor);
+      if (checkneighbor.tri == m->dummytri) {
+        insertsubseg(m, b, &checktri, 1);
+        hullsize++;
+      }
+      decode(nexttri, checktri);
+    }
+  }
+
+  trifree((VOID *) vertexarray);
+  return hullsize;
+}
+
+#endif /* not CDT_ONLY */
+
+/**                                                                         **/
+/**                                                                         **/
+/********* General mesh construction routines end here               *********/
+
+/********* Segment insertion begins here                             *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  finddirection()   Find the first triangle on the path from one point     */
+/*                    to another.                                            */
+/*                                                                           */
+/*  Finds the triangle that intersects a line segment drawn from the         */
+/*  origin of `searchtri' to the point `searchpoint', and returns the result */
+/*  in `searchtri'.  The origin of `searchtri' does not change, even though  */
+/*  the triangle returned may differ from the one passed in.  This routine   */
+/*  is used to find the direction to move in to get from one point to        */
+/*  another.                                                                 */
+/*                                                                           */
+/*  The return value notes whether the destination or apex of the found      */
+/*  triangle is collinear with the two points in question.                   */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+enum finddirectionresult finddirection(struct mesh *m, struct behavior *b,
+                                       struct otri *searchtri,
+                                       vertex searchpoint)
+#else /* not ANSI_DECLARATORS */
+enum finddirectionresult finddirection(m, b, searchtri, searchpoint)
+struct mesh *m;
+struct behavior *b;
+struct otri *searchtri;
+vertex searchpoint;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri checktri;
+  vertex startvertex;
+  vertex leftvertex, rightvertex;
+  REAL leftccw, rightccw;
+  int leftflag, rightflag;
+  triangle ptr;           /* Temporary variable used by onext() and oprev(). */
+
+  org(*searchtri, startvertex);
+  dest(*searchtri, rightvertex);
+  apex(*searchtri, leftvertex);
+  /* Is `searchpoint' to the left? */
+  leftccw = counterclockwise(m, b, searchpoint, startvertex, leftvertex);
+  leftflag = leftccw > 0.0;
+  /* Is `searchpoint' to the right? */
+  rightccw = counterclockwise(m, b, startvertex, searchpoint, rightvertex);
+  rightflag = rightccw > 0.0;
+  if (leftflag && rightflag) {
+    /* `searchtri' faces directly away from `searchpoint'.  We could go left */
+    /*   or right.  Ask whether it's a triangle or a boundary on the left.   */
+    onext(*searchtri, checktri);
+    if (checktri.tri == m->dummytri) {
+      leftflag = 0;
+    } else {
+      rightflag = 0;
+    }
+  }
+  while (leftflag) {
+    /* Turn left until satisfied. */
+    onextself(*searchtri);
+    if (searchtri->tri == m->dummytri) {
+      printf("Internal error in finddirection():  Unable to find a\n");
+      printf("  triangle leading from (%.12g, %.12g) to", startvertex[0],
+             startvertex[1]);
+      printf("  (%.12g, %.12g).\n", searchpoint[0], searchpoint[1]);
+      internalerror();
+    }
+    apex(*searchtri, leftvertex);
+    rightccw = leftccw;
+    leftccw = counterclockwise(m, b, searchpoint, startvertex, leftvertex);
+    leftflag = leftccw > 0.0;
+  }
+  while (rightflag) {
+    /* Turn right until satisfied. */
+    oprevself(*searchtri);
+    if (searchtri->tri == m->dummytri) {
+      printf("Internal error in finddirection():  Unable to find a\n");
+      printf("  triangle leading from (%.12g, %.12g) to", startvertex[0],
+             startvertex[1]);
+      printf("  (%.12g, %.12g).\n", searchpoint[0], searchpoint[1]);
+      internalerror();
+    }
+    dest(*searchtri, rightvertex);
+    leftccw = rightccw;
+    rightccw = counterclockwise(m, b, startvertex, searchpoint, rightvertex);
+    rightflag = rightccw > 0.0;
+  }
+  if (leftccw == 0.0) {
+    return LEFTCOLLINEAR;
+  } else if (rightccw == 0.0) {
+    return RIGHTCOLLINEAR;
+  } else {
+    return WITHIN;
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  segmentintersection()   Find the intersection of an existing segment     */
+/*                          and a segment that is being inserted.  Insert    */
+/*                          a vertex at the intersection, splitting an       */
+/*                          existing subsegment.                             */
+/*                                                                           */
+/*  The segment being inserted connects the apex of splittri to endpoint2.   */
+/*  splitsubseg is the subsegment being split, and MUST adjoin splittri.     */
+/*  Hence, endpoints of the subsegment being split are the origin and        */
+/*  destination of splittri.                                                 */
+/*                                                                           */
+/*  On completion, splittri is a handle having the newly inserted            */
+/*  intersection point as its origin, and endpoint1 as its destination.      */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void segmentintersection(struct mesh *m, struct behavior *b,
+                         struct otri *splittri, struct osub *splitsubseg,
+                         vertex endpoint2)
+#else /* not ANSI_DECLARATORS */
+void segmentintersection(m, b, splittri, splitsubseg, endpoint2)
+struct mesh *m;
+struct behavior *b;
+struct otri *splittri;
+struct osub *splitsubseg;
+vertex endpoint2;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  vertex endpoint1;
+  vertex torg, tdest;
+  vertex leftvertex, rightvertex;
+  vertex newvertex;
+  enum insertvertexresult success;
+  enum finddirectionresult collinear;
+  REAL ex, ey;
+  REAL tx, ty;
+  REAL etx, ety;
+  REAL split, denom;
+  int i;
+  triangle ptr;                       /* Temporary variable used by onext(). */
+
+  /* Find the other three segment endpoints. */
+  apex(*splittri, endpoint1);
+  org(*splittri, torg);
+  dest(*splittri, tdest);
+  /* Segment intersection formulae; see the Antonio reference. */
+  tx = tdest[0] - torg[0];
+  ty = tdest[1] - torg[1];
+  ex = endpoint2[0] - endpoint1[0];
+  ey = endpoint2[1] - endpoint1[1];
+  etx = torg[0] - endpoint2[0];
+  ety = torg[1] - endpoint2[1];
+  denom = ty * ex - tx * ey;
+  if (denom == 0.0) {
+    printf("Internal error in segmentintersection():");
+    printf("  Attempt to find intersection of parallel segments.\n");
+    internalerror();
+  }
+  split = (ey * etx - ex * ety) / denom;
+  /* Create the new vertex. */
+  newvertex = (vertex) poolalloc(&m->vertices);
+  /* Interpolate its coordinate and attributes. */
+  for (i = 0; i < 2 + m->nextras; i++) {
+    newvertex[i] = torg[i] + split * (tdest[i] - torg[i]);
+  }
+  setvertexmark(newvertex, mark(*splitsubseg));
+  setvertextype(newvertex, INPUTVERTEX);
+  if (b->verbose > 1) {
+    printf(
+  "  Splitting subsegment (%.12g, %.12g) (%.12g, %.12g) at (%.12g, %.12g).\n",
+           torg[0], torg[1], tdest[0], tdest[1], newvertex[0], newvertex[1]);
+  }
+  /* Insert the intersection vertex.  This should always succeed. */
+  success = insertvertex(m, b, newvertex, splittri, splitsubseg, 0, 0, 0.0);
+  if (success != SUCCESSFULVERTEX) {
+    printf("Internal error in segmentintersection():\n");
+    printf("  Failure to split a segment.\n");
+    internalerror();
+  }
+  if (m->steinerleft > 0) {
+    m->steinerleft--;
+  }
+  /* Inserting the vertex may have caused edge flips.  We wish to rediscover */
+  /*   the edge connecting endpoint1 to the new intersection vertex.         */
+  collinear = finddirection(m, b, splittri, endpoint1);
+  dest(*splittri, rightvertex);
+  apex(*splittri, leftvertex);
+  if ((leftvertex[0] == endpoint1[0]) && (leftvertex[1] == endpoint1[1])) {
+    onextself(*splittri);
+  } else if ((rightvertex[0] != endpoint1[0]) ||
+             (rightvertex[1] != endpoint1[1])) {
+    printf("Internal error in segmentintersection():\n");
+    printf("  Topological inconsistency after splitting a segment.\n");
+    internalerror();
+  }
+  /* `splittri' should have destination endpoint1. */
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  scoutsegment()   Scout the first triangle on the path from one endpoint  */
+/*                   to another, and check for completion (reaching the      */
+/*                   second endpoint), a collinear vertex, or the            */
+/*                   intersection of two segments.                           */
+/*                                                                           */
+/*  Returns one if the entire segment is successfully inserted, and zero if  */
+/*  the job must be finished by conformingedge() or constrainededge().       */
+/*                                                                           */
+/*  If the first triangle on the path has the second endpoint as its         */
+/*  destination or apex, a subsegment is inserted and the job is done.       */
+/*                                                                           */
+/*  If the first triangle on the path has a destination or apex that lies on */
+/*  the segment, a subsegment is inserted connecting the first endpoint to   */
+/*  the collinear vertex, and the search is continued from the collinear     */
+/*  vertex.                                                                  */
+/*                                                                           */
+/*  If the first triangle on the path has a subsegment opposite its origin,  */
+/*  then there is a segment that intersects the segment being inserted.      */
+/*  Their intersection vertex is inserted, splitting the subsegment.         */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+int scoutsegment(struct mesh *m, struct behavior *b, struct otri *searchtri,
+                 vertex endpoint2, int newmark)
+#else /* not ANSI_DECLARATORS */
+int scoutsegment(m, b, searchtri, endpoint2, newmark)
+struct mesh *m;
+struct behavior *b;
+struct otri *searchtri;
+vertex endpoint2;
+int newmark;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri crosstri;
+  struct osub crosssubseg;
+  vertex leftvertex, rightvertex;
+  enum finddirectionresult collinear;
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  collinear = finddirection(m, b, searchtri, endpoint2);
+  dest(*searchtri, rightvertex);
+  apex(*searchtri, leftvertex);
+  if (((leftvertex[0] == endpoint2[0]) && (leftvertex[1] == endpoint2[1])) ||
+      ((rightvertex[0] == endpoint2[0]) && (rightvertex[1] == endpoint2[1]))) {
+    /* The segment is already an edge in the mesh. */
+    if ((leftvertex[0] == endpoint2[0]) && (leftvertex[1] == endpoint2[1])) {
+      lprevself(*searchtri);
+    }
+    /* Insert a subsegment, if there isn't already one there. */
+    insertsubseg(m, b, searchtri, newmark);
+    return 1;
+  } else if (collinear == LEFTCOLLINEAR) {
+    /* We've collided with a vertex between the segment's endpoints. */
+    /* Make the collinear vertex be the triangle's origin. */
+    lprevself(*searchtri);
+    insertsubseg(m, b, searchtri, newmark);
+    /* Insert the remainder of the segment. */
+    return scoutsegment(m, b, searchtri, endpoint2, newmark);
+  } else if (collinear == RIGHTCOLLINEAR) {
+    /* We've collided with a vertex between the segment's endpoints. */
+    insertsubseg(m, b, searchtri, newmark);
+    /* Make the collinear vertex be the triangle's origin. */
+    lnextself(*searchtri);
+    /* Insert the remainder of the segment. */
+    return scoutsegment(m, b, searchtri, endpoint2, newmark);
+  } else {
+    lnext(*searchtri, crosstri);
+    tspivot(crosstri, crosssubseg);
+    /* Check for a crossing segment. */
+    if (crosssubseg.ss == m->dummysub) {
+      return 0;
+    } else {
+      /* Insert a vertex at the intersection. */
+      segmentintersection(m, b, &crosstri, &crosssubseg, endpoint2);
+      otricopy(crosstri, *searchtri);
+      insertsubseg(m, b, searchtri, newmark);
+      /* Insert the remainder of the segment. */
+      return scoutsegment(m, b, searchtri, endpoint2, newmark);
+    }
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  conformingedge()   Force a segment into a conforming Delaunay            */
+/*                     triangulation by inserting a vertex at its midpoint,  */
+/*                     and recursively forcing in the two half-segments if   */
+/*                     necessary.                                            */
+/*                                                                           */
+/*  Generates a sequence of subsegments connecting `endpoint1' to            */
+/*  `endpoint2'.  `newmark' is the boundary marker of the segment, assigned  */
+/*  to each new splitting vertex and subsegment.                             */
+/*                                                                           */
+/*  Note that conformingedge() does not always maintain the conforming       */
+/*  Delaunay property.  Once inserted, segments are locked into place;       */
+/*  vertices inserted later (to force other segments in) may render these    */
+/*  fixed segments non-Delaunay.  The conforming Delaunay property will be   */
+/*  restored by enforcequality() by splitting encroached subsegments.        */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef REDUCED
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void conformingedge(struct mesh *m, struct behavior *b,
+                    vertex endpoint1, vertex endpoint2, int newmark)
+#else /* not ANSI_DECLARATORS */
+void conformingedge(m, b, endpoint1, endpoint2, newmark)
+struct mesh *m;
+struct behavior *b;
+vertex endpoint1;
+vertex endpoint2;
+int newmark;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri searchtri1, searchtri2;
+  struct osub brokensubseg;
+  vertex newvertex;
+  vertex midvertex1, midvertex2;
+  enum insertvertexresult success;
+  int i;
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  if (b->verbose > 2) {
+    printf("Forcing segment into triangulation by recursive splitting:\n");
+    printf("  (%.12g, %.12g) (%.12g, %.12g)\n", endpoint1[0], endpoint1[1],
+           endpoint2[0], endpoint2[1]);
+  }
+  /* Create a new vertex to insert in the middle of the segment. */
+  newvertex = (vertex) poolalloc(&m->vertices);
+  /* Interpolate coordinates and attributes. */
+  for (i = 0; i < 2 + m->nextras; i++) {
+    newvertex[i] = 0.5 * (endpoint1[i] + endpoint2[i]);
+  }
+  setvertexmark(newvertex, newmark);
+  setvertextype(newvertex, SEGMENTVERTEX);
+  /* No known triangle to search from. */
+  searchtri1.tri = m->dummytri;
+  /* Attempt to insert the new vertex. */
+  success = insertvertex(m, b, newvertex, &searchtri1, (struct osub *) NULL,
+                         0, 0, 0.0);
+  if (success == DUPLICATEVERTEX) {
+    if (b->verbose > 2) {
+      printf("  Segment intersects existing vertex (%.12g, %.12g).\n",
+             newvertex[0], newvertex[1]);
+    }
+    /* Use the vertex that's already there. */
+    vertexdealloc(m, newvertex);
+    org(searchtri1, newvertex);
+  } else {
+    if (success == VIOLATINGVERTEX) {
+      if (b->verbose > 2) {
+        printf("  Two segments intersect at (%.12g, %.12g).\n",
+               newvertex[0], newvertex[1]);
+      }
+      /* By fluke, we've landed right on another segment.  Split it. */
+      tspivot(searchtri1, brokensubseg);
+      success = insertvertex(m, b, newvertex, &searchtri1, &brokensubseg,
+                             0, 0, 0.0);
+      if (success != SUCCESSFULVERTEX) {
+        printf("Internal error in conformingedge():\n");
+        printf("  Failure to split a segment.\n");
+        internalerror();
+      }
+    }
+    /* The vertex has been inserted successfully. */
+    if (m->steinerleft > 0) {
+      m->steinerleft--;
+    }
+  }
+  otricopy(searchtri1, searchtri2);
+  /* `searchtri1' and `searchtri2' are fastened at their origins to         */
+  /*   `newvertex', and will be directed toward `endpoint1' and `endpoint2' */
+  /*   respectively.  First, we must get `searchtri2' out of the way so it  */
+  /*   won't be invalidated during the insertion of the first half of the   */
+  /*   segment.                                                             */
+  finddirection(m, b, &searchtri2, endpoint2);
+  if (!scoutsegment(m, b, &searchtri1, endpoint1, newmark)) {
+    /* The origin of searchtri1 may have changed if a collision with an */
+    /*   intervening vertex on the segment occurred.                    */
+    org(searchtri1, midvertex1);
+    conformingedge(m, b, midvertex1, endpoint1, newmark);
+  }
+  if (!scoutsegment(m, b, &searchtri2, endpoint2, newmark)) {
+    /* The origin of searchtri2 may have changed if a collision with an */
+    /*   intervening vertex on the segment occurred.                    */
+    org(searchtri2, midvertex2);
+    conformingedge(m, b, midvertex2, endpoint2, newmark);
+  }
+}
+
+#endif /* not CDT_ONLY */
+#endif /* not REDUCED */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  delaunayfixup()   Enforce the Delaunay condition at an edge, fanning out */
+/*                    recursively from an existing vertex.  Pay special      */
+/*                    attention to stacking inverted triangles.              */
+/*                                                                           */
+/*  This is a support routine for inserting segments into a constrained      */
+/*  Delaunay triangulation.                                                  */
+/*                                                                           */
+/*  The origin of fixuptri is treated as if it has just been inserted, and   */
+/*  the local Delaunay condition needs to be enforced.  It is only enforced  */
+/*  in one sector, however, that being the angular range defined by          */
+/*  fixuptri.                                                                */
+/*                                                                           */
+/*  This routine also needs to make decisions regarding the "stacking" of    */
+/*  triangles.  (Read the description of constrainededge() below before      */
+/*  reading on here, so you understand the algorithm.)  If the position of   */
+/*  the new vertex (the origin of fixuptri) indicates that the vertex before */
+/*  it on the polygon is a reflex vertex, then "stack" the triangle by       */
+/*  doing nothing.  (fixuptri is an inverted triangle, which is how stacked  */
+/*  triangles are identified.)                                               */
+/*                                                                           */
+/*  Otherwise, check whether the vertex before that was a reflex vertex.     */
+/*  If so, perform an edge flip, thereby eliminating an inverted triangle    */
+/*  (popping it off the stack).  The edge flip may result in the creation    */
+/*  of a new inverted triangle, depending on whether or not the new vertex   */
+/*  is visible to the vertex three edges behind on the polygon.              */
+/*                                                                           */
+/*  If neither of the two vertices behind the new vertex are reflex          */
+/*  vertices, fixuptri and fartri, the triangle opposite it, are not         */
+/*  inverted; hence, ensure that the edge between them is locally Delaunay.  */
+/*                                                                           */
+/*  `leftside' indicates whether or not fixuptri is to the left of the       */
+/*  segment being inserted.  (Imagine that the segment is pointing up from   */
+/*  endpoint1 to endpoint2.)                                                 */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void delaunayfixup(struct mesh *m, struct behavior *b,
+                   struct otri *fixuptri, int leftside)
+#else /* not ANSI_DECLARATORS */
+void delaunayfixup(m, b, fixuptri, leftside)
+struct mesh *m;
+struct behavior *b;
+struct otri *fixuptri;
+int leftside;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri neartri;
+  struct otri fartri;
+  struct osub faredge;
+  vertex nearvertex, leftvertex, rightvertex, farvertex;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  lnext(*fixuptri, neartri);
+  sym(neartri, fartri);
+  /* Check if the edge opposite the origin of fixuptri can be flipped. */
+  if (fartri.tri == m->dummytri) {
+    return;
+  }
+  tspivot(neartri, faredge);
+  if (faredge.ss != m->dummysub) {
+    return;
+  }
+  /* Find all the relevant vertices. */
+  apex(neartri, nearvertex);
+  org(neartri, leftvertex);
+  dest(neartri, rightvertex);
+  apex(fartri, farvertex);
+  /* Check whether the previous polygon vertex is a reflex vertex. */
+  if (leftside) {
+    if (counterclockwise(m, b, nearvertex, leftvertex, farvertex) <= 0.0) {
+      /* leftvertex is a reflex vertex too.  Nothing can */
+      /*   be done until a convex section is found.      */
+      return;
+    }
+  } else {
+    if (counterclockwise(m, b, farvertex, rightvertex, nearvertex) <= 0.0) {
+      /* rightvertex is a reflex vertex too.  Nothing can */
+      /*   be done until a convex section is found.       */
+      return;
+    }
+  }
+  if (counterclockwise(m, b, rightvertex, leftvertex, farvertex) > 0.0) {
+    /* fartri is not an inverted triangle, and farvertex is not a reflex */
+    /*   vertex.  As there are no reflex vertices, fixuptri isn't an     */
+    /*   inverted triangle, either.  Hence, test the edge between the    */
+    /*   triangles to ensure it is locally Delaunay.                     */
+    if (incircle(m, b, leftvertex, farvertex, rightvertex, nearvertex) <=
+        0.0) {
+      return;
+    }
+    /* Not locally Delaunay; go on to an edge flip. */
+  }        /* else fartri is inverted; remove it from the stack by flipping. */
+  flip(m, b, &neartri);
+  lprevself(*fixuptri);    /* Restore the origin of fixuptri after the flip. */
+  /* Recursively process the two triangles that result from the flip. */
+  delaunayfixup(m, b, fixuptri, leftside);
+  delaunayfixup(m, b, &fartri, leftside);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  constrainededge()   Force a segment into a constrained Delaunay          */
+/*                      triangulation by deleting the triangles it           */
+/*                      intersects, and triangulating the polygons that      */
+/*                      form on each side of it.                             */
+/*                                                                           */
+/*  Generates a single subsegment connecting `endpoint1' to `endpoint2'.     */
+/*  The triangle `starttri' has `endpoint1' as its origin.  `newmark' is the */
+/*  boundary marker of the segment.                                          */
+/*                                                                           */
+/*  To insert a segment, every triangle whose interior intersects the        */
+/*  segment is deleted.  The union of these deleted triangles is a polygon   */
+/*  (which is not necessarily monotone, but is close enough), which is       */
+/*  divided into two polygons by the new segment.  This routine's task is    */
+/*  to generate the Delaunay triangulation of these two polygons.            */
+/*                                                                           */
+/*  You might think of this routine's behavior as a two-step process.  The   */
+/*  first step is to walk from endpoint1 to endpoint2, flipping each edge    */
+/*  encountered.  This step creates a fan of edges connected to endpoint1,   */
+/*  including the desired edge to endpoint2.  The second step enforces the   */
+/*  Delaunay condition on each side of the segment in an incremental manner: */
+/*  proceeding along the polygon from endpoint1 to endpoint2 (this is done   */
+/*  independently on each side of the segment), each vertex is "enforced"    */
+/*  as if it had just been inserted, but affecting only the previous         */
+/*  vertices.  The result is the same as if the vertices had been inserted   */
+/*  in the order they appear on the polygon, so the result is Delaunay.      */
+/*                                                                           */
+/*  In truth, constrainededge() interleaves these two steps.  The procedure  */
+/*  walks from endpoint1 to endpoint2, and each time an edge is encountered  */
+/*  and flipped, the newly exposed vertex (at the far end of the flipped     */
+/*  edge) is "enforced" upon the previously flipped edges, usually affecting */
+/*  only one side of the polygon (depending upon which side of the segment   */
+/*  the vertex falls on).                                                    */
+/*                                                                           */
+/*  The algorithm is complicated by the need to handle polygons that are not */
+/*  convex.  Although the polygon is not necessarily monotone, it can be     */
+/*  triangulated in a manner similar to the stack-based algorithms for       */
+/*  monotone polygons.  For each reflex vertex (local concavity) of the      */
+/*  polygon, there will be an inverted triangle formed by one of the edge    */
+/*  flips.  (An inverted triangle is one with negative area - that is, its   */
+/*  vertices are arranged in clockwise order - and is best thought of as a   */
+/*  wrinkle in the fabric of the mesh.)  Each inverted triangle can be       */
+/*  thought of as a reflex vertex pushed on the stack, waiting to be fixed   */
+/*  later.                                                                   */
+/*                                                                           */
+/*  A reflex vertex is popped from the stack when a vertex is inserted that  */
+/*  is visible to the reflex vertex.  (However, if the vertex behind the     */
+/*  reflex vertex is not visible to the reflex vertex, a new inverted        */
+/*  triangle will take its place on the stack.)  These details are handled   */
+/*  by the delaunayfixup() routine above.                                    */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void constrainededge(struct mesh *m, struct behavior *b,
+                     struct otri *starttri, vertex endpoint2, int newmark)
+#else /* not ANSI_DECLARATORS */
+void constrainededge(m, b, starttri, endpoint2, newmark)
+struct mesh *m;
+struct behavior *b;
+struct otri *starttri;
+vertex endpoint2;
+int newmark;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri fixuptri, fixuptri2;
+  struct osub crosssubseg;
+  vertex endpoint1;
+  vertex farvertex;
+  REAL area;
+  int collision;
+  int done;
+  triangle ptr;             /* Temporary variable used by sym() and oprev(). */
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  org(*starttri, endpoint1);
+  lnext(*starttri, fixuptri);
+  flip(m, b, &fixuptri);
+  /* `collision' indicates whether we have found a vertex directly */
+  /*   between endpoint1 and endpoint2.                            */
+  collision = 0;
+  done = 0;
+  do {
+    org(fixuptri, farvertex);
+    /* `farvertex' is the extreme point of the polygon we are "digging" */
+    /*   to get from endpoint1 to endpoint2.                           */
+    if ((farvertex[0] == endpoint2[0]) && (farvertex[1] == endpoint2[1])) {
+      oprev(fixuptri, fixuptri2);
+      /* Enforce the Delaunay condition around endpoint2. */
+      delaunayfixup(m, b, &fixuptri, 0);
+      delaunayfixup(m, b, &fixuptri2, 1);
+      done = 1;
+    } else {
+      /* Check whether farvertex is to the left or right of the segment */
+      /*   being inserted, to decide which edge of fixuptri to dig      */
+      /*   through next.                                                */
+      area = counterclockwise(m, b, endpoint1, endpoint2, farvertex);
+      if (area == 0.0) {
+        /* We've collided with a vertex between endpoint1 and endpoint2. */
+        collision = 1;
+        oprev(fixuptri, fixuptri2);
+        /* Enforce the Delaunay condition around farvertex. */
+        delaunayfixup(m, b, &fixuptri, 0);
+        delaunayfixup(m, b, &fixuptri2, 1);
+        done = 1;
+      } else {
+        if (area > 0.0) {        /* farvertex is to the left of the segment. */
+          oprev(fixuptri, fixuptri2);
+          /* Enforce the Delaunay condition around farvertex, on the */
+          /*   left side of the segment only.                        */
+          delaunayfixup(m, b, &fixuptri2, 1);
+          /* Flip the edge that crosses the segment.  After the edge is */
+          /*   flipped, one of its endpoints is the fan vertex, and the */
+          /*   destination of fixuptri is the fan vertex.               */
+          lprevself(fixuptri);
+        } else {                /* farvertex is to the right of the segment. */
+          delaunayfixup(m, b, &fixuptri, 0);
+          /* Flip the edge that crosses the segment.  After the edge is */
+          /*   flipped, one of its endpoints is the fan vertex, and the */
+          /*   destination of fixuptri is the fan vertex.               */
+          oprevself(fixuptri);
+        }
+        /* Check for two intersecting segments. */
+        tspivot(fixuptri, crosssubseg);
+        if (crosssubseg.ss == m->dummysub) {
+          flip(m, b, &fixuptri);    /* May create inverted triangle at left. */
+        } else {
+          /* We've collided with a segment between endpoint1 and endpoint2. */
+          collision = 1;
+          /* Insert a vertex at the intersection. */
+          segmentintersection(m, b, &fixuptri, &crosssubseg, endpoint2);
+          done = 1;
+        }
+      }
+    }
+  } while (!done);
+  /* Insert a subsegment to make the segment permanent. */
+  insertsubseg(m, b, &fixuptri, newmark);
+  /* If there was a collision with an interceding vertex, install another */
+  /*   segment connecting that vertex with endpoint2.                     */
+  if (collision) {
+    /* Insert the remainder of the segment. */
+    if (!scoutsegment(m, b, &fixuptri, endpoint2, newmark)) {
+      constrainededge(m, b, &fixuptri, endpoint2, newmark);
+    }
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  insertsegment()   Insert a PSLG segment into a triangulation.            */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void insertsegment(struct mesh *m, struct behavior *b,
+                   vertex endpoint1, vertex endpoint2, int newmark)
+#else /* not ANSI_DECLARATORS */
+void insertsegment(m, b, endpoint1, endpoint2, newmark)
+struct mesh *m;
+struct behavior *b;
+vertex endpoint1;
+vertex endpoint2;
+int newmark;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri searchtri1, searchtri2;
+  triangle encodedtri;
+  vertex checkvertex;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+
+  if (b->verbose > 1) {
+    printf("  Connecting (%.12g, %.12g) to (%.12g, %.12g).\n",
+           endpoint1[0], endpoint1[1], endpoint2[0], endpoint2[1]);
+  }
+
+  /* Find a triangle whose origin is the segment's first endpoint. */
+  checkvertex = (vertex) NULL;
+  encodedtri = vertex2tri(endpoint1);
+  if (encodedtri != (triangle) NULL) {
+    decode(encodedtri, searchtri1);
+    org(searchtri1, checkvertex);
+  }
+  if (checkvertex != endpoint1) {
+    /* Find a boundary triangle to search from. */
+    searchtri1.tri = m->dummytri;
+    searchtri1.orient = 0;
+    symself(searchtri1);
+    /* Search for the segment's first endpoint by point location. */
+    if (locate(m, b, endpoint1, &searchtri1) != ONVERTEX) {
+      printf(
+        "Internal error in insertsegment():  Unable to locate PSLG vertex\n");
+      printf("  (%.12g, %.12g) in triangulation.\n",
+             endpoint1[0], endpoint1[1]);
+      internalerror();
+    }
+  }
+  /* Remember this triangle to improve subsequent point location. */
+  otricopy(searchtri1, m->recenttri);
+  /* Scout the beginnings of a path from the first endpoint */
+  /*   toward the second.                                   */
+  if (scoutsegment(m, b, &searchtri1, endpoint2, newmark)) {
+    /* The segment was easily inserted. */
+    return;
+  }
+  /* The first endpoint may have changed if a collision with an intervening */
+  /*   vertex on the segment occurred.                                      */
+  org(searchtri1, endpoint1);
+
+  /* Find a triangle whose origin is the segment's second endpoint. */
+  checkvertex = (vertex) NULL;
+  encodedtri = vertex2tri(endpoint2);
+  if (encodedtri != (triangle) NULL) {
+    decode(encodedtri, searchtri2);
+    org(searchtri2, checkvertex);
+  }
+  if (checkvertex != endpoint2) {
+    /* Find a boundary triangle to search from. */
+    searchtri2.tri = m->dummytri;
+    searchtri2.orient = 0;
+    symself(searchtri2);
+    /* Search for the segment's second endpoint by point location. */
+    if (locate(m, b, endpoint2, &searchtri2) != ONVERTEX) {
+      printf(
+        "Internal error in insertsegment():  Unable to locate PSLG vertex\n");
+      printf("  (%.12g, %.12g) in triangulation.\n",
+             endpoint2[0], endpoint2[1]);
+      internalerror();
+    }
+  }
+  /* Remember this triangle to improve subsequent point location. */
+  otricopy(searchtri2, m->recenttri);
+  /* Scout the beginnings of a path from the second endpoint */
+  /*   toward the first.                                     */
+  if (scoutsegment(m, b, &searchtri2, endpoint1, newmark)) {
+    /* The segment was easily inserted. */
+    return;
+  }
+  /* The second endpoint may have changed if a collision with an intervening */
+  /*   vertex on the segment occurred.                                       */
+  org(searchtri2, endpoint2);
+
+#ifndef REDUCED
+#ifndef CDT_ONLY
+  if (b->splitseg) {
+    /* Insert vertices to force the segment into the triangulation. */
+    conformingedge(m, b, endpoint1, endpoint2, newmark);
+  } else {
+#endif /* not CDT_ONLY */
+#endif /* not REDUCED */
+    /* Insert the segment directly into the triangulation. */
+    constrainededge(m, b, &searchtri1, endpoint2, newmark);
+#ifndef REDUCED
+#ifndef CDT_ONLY
+  }
+#endif /* not CDT_ONLY */
+#endif /* not REDUCED */
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  markhull()   Cover the convex hull of a triangulation with subsegments.  */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void markhull(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void markhull(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri hulltri;
+  struct otri nexttri;
+  struct otri starttri;
+  triangle ptr;             /* Temporary variable used by sym() and oprev(). */
+
+  /* Find a triangle handle on the hull. */
+  hulltri.tri = m->dummytri;
+  hulltri.orient = 0;
+  symself(hulltri);
+  /* Remember where we started so we know when to stop. */
+  otricopy(hulltri, starttri);
+  /* Go once counterclockwise around the convex hull. */
+  do {
+    /* Create a subsegment if there isn't already one here. */
+    insertsubseg(m, b, &hulltri, 1);
+    /* To find the next hull edge, go clockwise around the next vertex. */
+    lnextself(hulltri);
+    oprev(hulltri, nexttri);
+    while (nexttri.tri != m->dummytri) {
+      otricopy(nexttri, hulltri);
+      oprev(hulltri, nexttri);
+    }
+  } while (!otriequal(hulltri, starttri));
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  formskeleton()   Create the segments of a triangulation, including PSLG  */
+/*                   segments and edges on the convex hull.                  */
+/*                                                                           */
+/*  The PSLG segments are read from a .poly file.  The return value is the   */
+/*  number of segments in the file.                                          */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void formskeleton(struct mesh *m, struct behavior *b, int *segmentlist,
+                  int *segmentmarkerlist, int numberofsegments)
+#else /* not ANSI_DECLARATORS */
+void formskeleton(m, b, segmentlist, segmentmarkerlist, numberofsegments)
+struct mesh *m;
+struct behavior *b;
+int *segmentlist;
+int *segmentmarkerlist;
+int numberofsegments;
+#endif /* not ANSI_DECLARATORS */
+
+#else /* not TRILIBRARY */
+
+#ifdef ANSI_DECLARATORS
+void formskeleton(struct mesh *m, struct behavior *b,
+                  FILE *polyfile, char *polyfilename)
+#else /* not ANSI_DECLARATORS */
+void formskeleton(m, b, polyfile, polyfilename)
+struct mesh *m;
+struct behavior *b;
+FILE *polyfile;
+char *polyfilename;
+#endif /* not ANSI_DECLARATORS */
+
+#endif /* not TRILIBRARY */
+
+{
+#ifdef TRILIBRARY
+  char polyfilename[6];
+  int index;
+#else /* not TRILIBRARY */
+  char inputline[INPUTLINESIZE];
+  char *stringptr;
+#endif /* not TRILIBRARY */
+  vertex endpoint1, endpoint2;
+  int segmentmarkers;
+  int end1, end2;
+  int boundmarker;
+  int i;
+
+  if (b->poly) {
+    if (!b->quiet) {
+      printf("Recovering segments in Delaunay triangulation.\n");
+    }
+#ifdef TRILIBRARY
+    strcpy(polyfilename, "input");
+    m->insegments = numberofsegments;
+    segmentmarkers = segmentmarkerlist != (int *) NULL;
+    index = 0;
+#else /* not TRILIBRARY */
+    /* Read the segments from a .poly file. */
+    /* Read number of segments and number of boundary markers. */
+    stringptr = readline(inputline, polyfile, polyfilename);
+    m->insegments = (int) strtol(stringptr, &stringptr, 0);
+    stringptr = findfield(stringptr);
+    if (*stringptr == '\0') {
+      segmentmarkers = 0;
+    } else {
+      segmentmarkers = (int) strtol(stringptr, &stringptr, 0);
+    }
+#endif /* not TRILIBRARY */
+    /* If the input vertices are collinear, there is no triangulation, */
+    /*   so don't try to insert segments.                              */
+    if (m->triangles.items == 0) {
+      return;
+    }
+
+    /* If segments are to be inserted, compute a mapping */
+    /*   from vertices to triangles.                     */
+    if (m->insegments > 0) {
+      makevertexmap(m, b);
+      if (b->verbose) {
+        printf("  Recovering PSLG segments.\n");
+      }
+    }
+
+    boundmarker = 0;
+    /* Read and insert the segments. */
+    for (i = 0; i < m->insegments; i++) {
+#ifdef TRILIBRARY
+      end1 = segmentlist[index++];
+      end2 = segmentlist[index++];
+      if (segmentmarkers) {
+        boundmarker = segmentmarkerlist[i];
+      }
+#else /* not TRILIBRARY */
+      stringptr = readline(inputline, polyfile, b->inpolyfilename);
+      stringptr = findfield(stringptr);
+      if (*stringptr == '\0') {
+        printf("Error:  Segment %d has no endpoints in %s.\n",
+               b->firstnumber + i, polyfilename);
+        exit(1);
+      } else {
+        end1 = (int) strtol(stringptr, &stringptr, 0);
+      }
+      stringptr = findfield(stringptr);
+      if (*stringptr == '\0') {
+        printf("Error:  Segment %d is missing its second endpoint in %s.\n",
+               b->firstnumber + i, polyfilename);
+        exit(1);
+      } else {
+        end2 = (int) strtol(stringptr, &stringptr, 0);
+      }
+      if (segmentmarkers) {
+        stringptr = findfield(stringptr);
+        if (*stringptr == '\0') {
+          boundmarker = 0;
+        } else {
+          boundmarker = (int) strtol(stringptr, &stringptr, 0);
+        }
+      }
+#endif /* not TRILIBRARY */
+      if ((end1 < b->firstnumber) ||
+          (end1 >= b->firstnumber + m->invertices)) {
+        if (!b->quiet) {
+          printf("Warning:  Invalid first endpoint of segment %d in %s.\n",
+                 b->firstnumber + i, polyfilename);
+        }
+      } else if ((end2 < b->firstnumber) ||
+                 (end2 >= b->firstnumber + m->invertices)) {
+        if (!b->quiet) {
+          printf("Warning:  Invalid second endpoint of segment %d in %s.\n",
+                 b->firstnumber + i, polyfilename);
+        }
+      } else {
+        endpoint1 = getvertex(m, b, end1);
+        endpoint2 = getvertex(m, b, end2);
+        if ((endpoint1[0] == endpoint2[0]) && (endpoint1[1] == endpoint2[1])) {
+          if (!b->quiet) {
+            printf("Warning:  Endpoints of segment %d are coincident in %s.\n",
+                   b->firstnumber + i, polyfilename);
+          }
+        } else {
+          insertsegment(m, b, endpoint1, endpoint2, boundmarker);
+        }
+      }
+    }
+  } else {
+    m->insegments = 0;
+  }
+  if (b->convex || !b->poly) {
+    /* Enclose the convex hull with subsegments. */
+    if (b->verbose) {
+      printf("  Enclosing convex hull with segments.\n");
+    }
+    markhull(m, b);
+  }
+}
+
+/**                                                                         **/
+/**                                                                         **/
+/********* Segment insertion ends here                               *********/
+
+/********* Carving out holes and concavities begins here             *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  infecthull()   Virally infect all of the triangles of the convex hull    */
+/*                 that are not protected by subsegments.  Where there are   */
+/*                 subsegments, set boundary markers as appropriate.         */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void infecthull(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void infecthull(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri hulltri;
+  struct otri nexttri;
+  struct otri starttri;
+  struct osub hullsubseg;
+  triangle **deadtriangle;
+  vertex horg, hdest;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  if (b->verbose) {
+    printf("  Marking concavities (external triangles) for elimination.\n");
+  }
+  /* Find a triangle handle on the hull. */
+  hulltri.tri = m->dummytri;
+  hulltri.orient = 0;
+  symself(hulltri);
+  /* Remember where we started so we know when to stop. */
+  otricopy(hulltri, starttri);
+  /* Go once counterclockwise around the convex hull. */
+  do {
+    /* Ignore triangles that are already infected. */
+    if (!infected(hulltri)) {
+      /* Is the triangle protected by a subsegment? */
+      tspivot(hulltri, hullsubseg);
+      if (hullsubseg.ss == m->dummysub) {
+        /* The triangle is not protected; infect it. */
+        if (!infected(hulltri)) {
+          infect(hulltri);
+          deadtriangle = (triangle **) poolalloc(&m->viri);
+          *deadtriangle = hulltri.tri;
+        }
+      } else {
+        /* The triangle is protected; set boundary markers if appropriate. */
+        if (mark(hullsubseg) == 0) {
+          setmark(hullsubseg, 1);
+          org(hulltri, horg);
+          dest(hulltri, hdest);
+          if (vertexmark(horg) == 0) {
+            setvertexmark(horg, 1);
+          }
+          if (vertexmark(hdest) == 0) {
+            setvertexmark(hdest, 1);
+          }
+        }
+      }
+    }
+    /* To find the next hull edge, go clockwise around the next vertex. */
+    lnextself(hulltri);
+    oprev(hulltri, nexttri);
+    while (nexttri.tri != m->dummytri) {
+      otricopy(nexttri, hulltri);
+      oprev(hulltri, nexttri);
+    }
+  } while (!otriequal(hulltri, starttri));
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  plague()   Spread the virus from all infected triangles to any neighbors */
+/*             not protected by subsegments.  Delete all infected triangles. */
+/*                                                                           */
+/*  This is the procedure that actually creates holes and concavities.       */
+/*                                                                           */
+/*  This procedure operates in two phases.  The first phase identifies all   */
+/*  the triangles that will die, and marks them as infected.  They are       */
+/*  marked to ensure that each triangle is added to the virus pool only      */
+/*  once, so the procedure will terminate.                                   */
+/*                                                                           */
+/*  The second phase actually eliminates the infected triangles.  It also    */
+/*  eliminates orphaned vertices.                                            */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void plague(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void plague(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri testtri;
+  struct otri neighbor;
+  triangle **virusloop;
+  triangle **deadtriangle;
+  struct osub neighborsubseg;
+  vertex testvertex;
+  vertex norg, ndest;
+  vertex deadorg, deaddest, deadapex;
+  int killorg;
+  triangle ptr;             /* Temporary variable used by sym() and onext(). */
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  if (b->verbose) {
+    printf("  Marking neighbors of marked triangles.\n");
+  }
+  /* Loop through all the infected triangles, spreading the virus to */
+  /*   their neighbors, then to their neighbors' neighbors.          */
+  traversalinit(&m->viri);
+  virusloop = (triangle **) traverse(&m->viri);
+  while (virusloop != (triangle **) NULL) {
+    testtri.tri = *virusloop;
+    /* A triangle is marked as infected by messing with one of its pointers */
+    /*   to subsegments, setting it to an illegal value.  Hence, we have to */
+    /*   temporarily uninfect this triangle so that we can examine its      */
+    /*   adjacent subsegments.                                              */
+    uninfect(testtri);
+    if (b->verbose > 2) {
+      /* Assign the triangle an orientation for convenience in */
+      /*   checking its vertices.                              */
+      testtri.orient = 0;
+      org(testtri, deadorg);
+      dest(testtri, deaddest);
+      apex(testtri, deadapex);
+      printf("    Checking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n",
+             deadorg[0], deadorg[1], deaddest[0], deaddest[1],
+             deadapex[0], deadapex[1]);
+    }
+    /* Check each of the triangle's three neighbors. */
+    for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) {
+      /* Find the neighbor. */
+      sym(testtri, neighbor);
+      /* Check for a subsegment between the triangle and its neighbor. */
+      tspivot(testtri, neighborsubseg);
+      /* Check if the neighbor is nonexistent or already infected. */
+      if ((neighbor.tri == m->dummytri) || infected(neighbor)) {
+        if (neighborsubseg.ss != m->dummysub) {
+          /* There is a subsegment separating the triangle from its      */
+          /*   neighbor, but both triangles are dying, so the subsegment */
+          /*   dies too.                                                 */
+          subsegdealloc(m, neighborsubseg.ss);
+          if (neighbor.tri != m->dummytri) {
+            /* Make sure the subsegment doesn't get deallocated again */
+            /*   later when the infected neighbor is visited.         */
+            uninfect(neighbor);
+            tsdissolve(neighbor);
+            infect(neighbor);
+          }
+        }
+      } else {                   /* The neighbor exists and is not infected. */
+        if (neighborsubseg.ss == m->dummysub) {
+          /* There is no subsegment protecting the neighbor, so */
+          /*   the neighbor becomes infected.                   */
+          if (b->verbose > 2) {
+            org(neighbor, deadorg);
+            dest(neighbor, deaddest);
+            apex(neighbor, deadapex);
+            printf(
+              "    Marking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n",
+                   deadorg[0], deadorg[1], deaddest[0], deaddest[1],
+                   deadapex[0], deadapex[1]);
+          }
+          infect(neighbor);
+          /* Ensure that the neighbor's neighbors will be infected. */
+          deadtriangle = (triangle **) poolalloc(&m->viri);
+          *deadtriangle = neighbor.tri;
+        } else {               /* The neighbor is protected by a subsegment. */
+          /* Remove this triangle from the subsegment. */
+          stdissolve(neighborsubseg);
+          /* The subsegment becomes a boundary.  Set markers accordingly. */
+          if (mark(neighborsubseg) == 0) {
+            setmark(neighborsubseg, 1);
+          }
+          org(neighbor, norg);
+          dest(neighbor, ndest);
+          if (vertexmark(norg) == 0) {
+            setvertexmark(norg, 1);
+          }
+          if (vertexmark(ndest) == 0) {
+            setvertexmark(ndest, 1);
+          }
+        }
+      }
+    }
+    /* Remark the triangle as infected, so it doesn't get added to the */
+    /*   virus pool again.                                             */
+    infect(testtri);
+    virusloop = (triangle **) traverse(&m->viri);
+  }
+
+  if (b->verbose) {
+    printf("  Deleting marked triangles.\n");
+  }
+
+  traversalinit(&m->viri);
+  virusloop = (triangle **) traverse(&m->viri);
+  while (virusloop != (triangle **) NULL) {
+    testtri.tri = *virusloop;
+
+    /* Check each of the three corners of the triangle for elimination. */
+    /*   This is done by walking around each vertex, checking if it is  */
+    /*   still connected to at least one live triangle.                 */
+    for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) {
+      org(testtri, testvertex);
+      /* Check if the vertex has already been tested. */
+      if (testvertex != (vertex) NULL) {
+        killorg = 1;
+        /* Mark the corner of the triangle as having been tested. */
+        setorg(testtri, NULL);
+        /* Walk counterclockwise about the vertex. */
+        onext(testtri, neighbor);
+        /* Stop upon reaching a boundary or the starting triangle. */
+        while ((neighbor.tri != m->dummytri) &&
+               (!otriequal(neighbor, testtri))) {
+          if (infected(neighbor)) {
+            /* Mark the corner of this triangle as having been tested. */
+            setorg(neighbor, NULL);
+          } else {
+            /* A live triangle.  The vertex survives. */
+            killorg = 0;
+          }
+          /* Walk counterclockwise about the vertex. */
+          onextself(neighbor);
+        }
+        /* If we reached a boundary, we must walk clockwise as well. */
+        if (neighbor.tri == m->dummytri) {
+          /* Walk clockwise about the vertex. */
+          oprev(testtri, neighbor);
+          /* Stop upon reaching a boundary. */
+          while (neighbor.tri != m->dummytri) {
+            if (infected(neighbor)) {
+            /* Mark the corner of this triangle as having been tested. */
+              setorg(neighbor, NULL);
+            } else {
+              /* A live triangle.  The vertex survives. */
+              killorg = 0;
+            }
+            /* Walk clockwise about the vertex. */
+            oprevself(neighbor);
+          }
+        }
+        if (killorg) {
+          if (b->verbose > 1) {
+            printf("    Deleting vertex (%.12g, %.12g)\n",
+                   testvertex[0], testvertex[1]);
+          }
+          setvertextype(testvertex, UNDEADVERTEX);
+          m->undeads++;
+        }
+      }
+    }
+
+    /* Record changes in the number of boundary edges, and disconnect */
+    /*   dead triangles from their neighbors.                         */
+    for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) {
+      sym(testtri, neighbor);
+      if (neighbor.tri == m->dummytri) {
+        /* There is no neighboring triangle on this edge, so this edge    */
+        /*   is a boundary edge.  This triangle is being deleted, so this */
+        /*   boundary edge is deleted.                                    */
+        m->hullsize--;
+      } else {
+        /* Disconnect the triangle from its neighbor. */
+        dissolve(neighbor);
+        /* There is a neighboring triangle on this edge, so this edge */
+        /*   becomes a boundary edge when this triangle is deleted.   */
+        m->hullsize++;
+      }
+    }
+    /* Return the dead triangle to the pool of triangles. */
+    triangledealloc(m, testtri.tri);
+    virusloop = (triangle **) traverse(&m->viri);
+  }
+  /* Empty the virus pool. */
+  poolrestart(&m->viri);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  regionplague()   Spread regional attributes and/or area constraints      */
+/*                   (from a .poly file) throughout the mesh.                */
+/*                                                                           */
+/*  This procedure operates in two phases.  The first phase spreads an       */
+/*  attribute and/or an area constraint through a (segment-bounded) region.  */
+/*  The triangles are marked to ensure that each triangle is added to the    */
+/*  virus pool only once, so the procedure will terminate.                   */
+/*                                                                           */
+/*  The second phase uninfects all infected triangles, returning them to     */
+/*  normal.                                                                  */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void regionplague(struct mesh *m, struct behavior *b,
+                  REAL attribute, REAL area)
+#else /* not ANSI_DECLARATORS */
+void regionplague(m, b, attribute, area)
+struct mesh *m;
+struct behavior *b;
+REAL attribute;
+REAL area;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri testtri;
+  struct otri neighbor;
+  triangle **virusloop;
+  triangle **regiontri;
+  struct osub neighborsubseg;
+  vertex regionorg, regiondest, regionapex;
+  triangle ptr;             /* Temporary variable used by sym() and onext(). */
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  if (b->verbose > 1) {
+    printf("  Marking neighbors of marked triangles.\n");
+  }
+  /* Loop through all the infected triangles, spreading the attribute      */
+  /*   and/or area constraint to their neighbors, then to their neighbors' */
+  /*   neighbors.                                                          */
+  traversalinit(&m->viri);
+  virusloop = (triangle **) traverse(&m->viri);
+  while (virusloop != (triangle **) NULL) {
+    testtri.tri = *virusloop;
+    /* A triangle is marked as infected by messing with one of its pointers */
+    /*   to subsegments, setting it to an illegal value.  Hence, we have to */
+    /*   temporarily uninfect this triangle so that we can examine its      */
+    /*   adjacent subsegments.                                              */
+    uninfect(testtri);
+    if (b->regionattrib) {
+      /* Set an attribute. */
+      setelemattribute(testtri, m->eextras, attribute);
+    }
+    if (b->vararea) {
+      /* Set an area constraint. */
+      setareabound(testtri, area);
+    }
+    if (b->verbose > 2) {
+      /* Assign the triangle an orientation for convenience in */
+      /*   checking its vertices.                              */
+      testtri.orient = 0;
+      org(testtri, regionorg);
+      dest(testtri, regiondest);
+      apex(testtri, regionapex);
+      printf("    Checking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n",
+             regionorg[0], regionorg[1], regiondest[0], regiondest[1],
+             regionapex[0], regionapex[1]);
+    }
+    /* Check each of the triangle's three neighbors. */
+    for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) {
+      /* Find the neighbor. */
+      sym(testtri, neighbor);
+      /* Check for a subsegment between the triangle and its neighbor. */
+      tspivot(testtri, neighborsubseg);
+      /* Make sure the neighbor exists, is not already infected, and */
+      /*   isn't protected by a subsegment.                          */
+      if ((neighbor.tri != m->dummytri) && !infected(neighbor)
+          && (neighborsubseg.ss == m->dummysub)) {
+        if (b->verbose > 2) {
+          org(neighbor, regionorg);
+          dest(neighbor, regiondest);
+          apex(neighbor, regionapex);
+          printf("    Marking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n",
+                 regionorg[0], regionorg[1], regiondest[0], regiondest[1],
+                 regionapex[0], regionapex[1]);
+        }
+        /* Infect the neighbor. */
+        infect(neighbor);
+        /* Ensure that the neighbor's neighbors will be infected. */
+        regiontri = (triangle **) poolalloc(&m->viri);
+        *regiontri = neighbor.tri;
+      }
+    }
+    /* Remark the triangle as infected, so it doesn't get added to the */
+    /*   virus pool again.                                             */
+    infect(testtri);
+    virusloop = (triangle **) traverse(&m->viri);
+  }
+
+  /* Uninfect all triangles. */
+  if (b->verbose > 1) {
+    printf("  Unmarking marked triangles.\n");
+  }
+  traversalinit(&m->viri);
+  virusloop = (triangle **) traverse(&m->viri);
+  while (virusloop != (triangle **) NULL) {
+    testtri.tri = *virusloop;
+    uninfect(testtri);
+    virusloop = (triangle **) traverse(&m->viri);
+  }
+  /* Empty the virus pool. */
+  poolrestart(&m->viri);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  carveholes()   Find the holes and infect them.  Find the area            */
+/*                 constraints and infect them.  Infect the convex hull.     */
+/*                 Spread the infection and kill triangles.  Spread the      */
+/*                 area constraints.                                         */
+/*                                                                           */
+/*  This routine mainly calls other routines to carry out all these          */
+/*  functions.                                                               */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void carveholes(struct mesh *m, struct behavior *b, REAL *holelist, int holes,
+                REAL *regionlist, int regions)
+#else /* not ANSI_DECLARATORS */
+void carveholes(m, b, holelist, holes, regionlist, regions)
+struct mesh *m;
+struct behavior *b;
+REAL *holelist;
+int holes;
+REAL *regionlist;
+int regions;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri searchtri;
+  struct otri triangleloop;
+  struct otri *regiontris;
+  triangle **holetri;
+  triangle **regiontri;
+  vertex searchorg, searchdest;
+  enum locateresult intersect;
+  int i;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+
+  if (!(b->quiet || (b->noholes && b->convex))) {
+    printf("Removing unwanted triangles.\n");
+    if (b->verbose && (holes > 0)) {
+      printf("  Marking holes for elimination.\n");
+    }
+  }
+
+  if (regions > 0) {
+    /* Allocate storage for the triangles in which region points fall. */
+    regiontris = (struct otri *)
+                 trimalloc(regions * (int) sizeof(struct otri));
+  }
+
+  if (((holes > 0) && !b->noholes) || !b->convex || (regions > 0)) {
+    /* Initialize a pool of viri to be used for holes, concavities, */
+    /*   regional attributes, and/or regional area constraints.     */
+    poolinit(&m->viri, sizeof(triangle *), VIRUSPERBLOCK, VIRUSPERBLOCK,
+             POINTER, 0);
+  }
+
+  if (!b->convex) {
+    /* Mark as infected any unprotected triangles on the boundary. */
+    /*   This is one way by which concavities are created.         */
+    infecthull(m, b);
+  }
+
+  if ((holes > 0) && !b->noholes) {
+    /* Infect each triangle in which a hole lies. */
+    for (i = 0; i < 2 * holes; i += 2) {
+      /* Ignore holes that aren't within the bounds of the mesh. */
+      if ((holelist[i] >= m->xmin) && (holelist[i] <= m->xmax)
+          && (holelist[i + 1] >= m->ymin) && (holelist[i + 1] <= m->ymax)) {
+        /* Start searching from some triangle on the outer boundary. */
+        searchtri.tri = m->dummytri;
+        searchtri.orient = 0;
+        symself(searchtri);
+        /* Ensure that the hole is to the left of this boundary edge; */
+        /*   otherwise, locate() will falsely report that the hole    */
+        /*   falls within the starting triangle.                      */
+        org(searchtri, searchorg);
+        dest(searchtri, searchdest);
+        if (counterclockwise(m, b, searchorg, searchdest, &holelist[i]) >
+            0.0) {
+          /* Find a triangle that contains the hole. */
+          intersect = locate(m, b, &holelist[i], &searchtri);
+          if ((intersect != OUTSIDE) && (!infected(searchtri))) {
+            /* Infect the triangle.  This is done by marking the triangle  */
+            /*   as infected and including the triangle in the virus pool. */
+            infect(searchtri);
+            holetri = (triangle **) poolalloc(&m->viri);
+            *holetri = searchtri.tri;
+          }
+        }
+      }
+    }
+  }
+
+  /* Now, we have to find all the regions BEFORE we carve the holes, because */
+  /*   locate() won't work when the triangulation is no longer convex.       */
+  /*   (Incidentally, this is the reason why regional attributes and area    */
+  /*   constraints can't be used when refining a preexisting mesh, which     */
+  /*   might not be convex; they can only be used with a freshly             */
+  /*   triangulated PSLG.)                                                   */
+  if (regions > 0) {
+    /* Find the starting triangle for each region. */
+    for (i = 0; i < regions; i++) {
+      regiontris[i].tri = m->dummytri;
+      /* Ignore region points that aren't within the bounds of the mesh. */
+      if ((regionlist[4 * i] >= m->xmin) && (regionlist[4 * i] <= m->xmax) &&
+          (regionlist[4 * i + 1] >= m->ymin) &&
+          (regionlist[4 * i + 1] <= m->ymax)) {
+        /* Start searching from some triangle on the outer boundary. */
+        searchtri.tri = m->dummytri;
+        searchtri.orient = 0;
+        symself(searchtri);
+        /* Ensure that the region point is to the left of this boundary */
+        /*   edge; otherwise, locate() will falsely report that the     */
+        /*   region point falls within the starting triangle.           */
+        org(searchtri, searchorg);
+        dest(searchtri, searchdest);
+        if (counterclockwise(m, b, searchorg, searchdest, &regionlist[4 * i]) >
+            0.0) {
+          /* Find a triangle that contains the region point. */
+          intersect = locate(m, b, &regionlist[4 * i], &searchtri);
+          if ((intersect != OUTSIDE) && (!infected(searchtri))) {
+            /* Record the triangle for processing after the */
+            /*   holes have been carved.                    */
+            otricopy(searchtri, regiontris[i]);
+          }
+        }
+      }
+    }
+  }
+
+  if (m->viri.items > 0) {
+    /* Carve the holes and concavities. */
+    plague(m, b);
+  }
+  /* The virus pool should be empty now. */
+
+  if (regions > 0) {
+    if (!b->quiet) {
+      if (b->regionattrib) {
+        if (b->vararea) {
+          printf("Spreading regional attributes and area constraints.\n");
+        } else {
+          printf("Spreading regional attributes.\n");
+        }
+      } else { 
+        printf("Spreading regional area constraints.\n");
+      }
+    }
+    if (b->regionattrib && !b->refine) {
+      /* Assign every triangle a regional attribute of zero. */
+      traversalinit(&m->triangles);
+      triangleloop.orient = 0;
+      triangleloop.tri = triangletraverse(m);
+      while (triangleloop.tri != (triangle *) NULL) {
+        setelemattribute(triangleloop, m->eextras, 0.0);
+        triangleloop.tri = triangletraverse(m);
+      }
+    }
+    for (i = 0; i < regions; i++) {
+      if (regiontris[i].tri != m->dummytri) {
+        /* Make sure the triangle under consideration still exists. */
+        /*   It may have been eaten by the virus.                   */
+        if (!deadtri(regiontris[i].tri)) {
+          /* Put one triangle in the virus pool. */
+          infect(regiontris[i]);
+          regiontri = (triangle **) poolalloc(&m->viri);
+          *regiontri = regiontris[i].tri;
+          /* Apply one region's attribute and/or area constraint. */
+          regionplague(m, b, regionlist[4 * i + 2], regionlist[4 * i + 3]);
+          /* The virus pool should be empty now. */
+        }
+      }
+    }
+    if (b->regionattrib && !b->refine) {
+      /* Note the fact that each triangle has an additional attribute. */
+      m->eextras++;
+    }
+  }
+
+  /* Free up memory. */
+  if (((holes > 0) && !b->noholes) || !b->convex || (regions > 0)) {
+    pooldeinit(&m->viri);
+  }
+  if (regions > 0) {
+    trifree((VOID *) regiontris);
+  }
+}
+
+/**                                                                         **/
+/**                                                                         **/
+/********* Carving out holes and concavities ends here               *********/
+
+/********* Mesh quality maintenance begins here                      *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  tallyencs()   Traverse the entire list of subsegments, and check each    */
+/*                to see if it is encroached.  If so, add it to the list.    */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void tallyencs(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void tallyencs(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct osub subsegloop;
+  int dummy;
+
+  traversalinit(&m->subsegs);
+  subsegloop.ssorient = 0;
+  subsegloop.ss = subsegtraverse(m);
+  while (subsegloop.ss != (subseg *) NULL) {
+    /* If the segment is encroached, add it to the list. */
+    dummy = checkseg4encroach(m, b, &subsegloop, 0.0);
+    subsegloop.ss = subsegtraverse(m);
+  }
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  precisionerror()  Print an error message for precision problems.         */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+void precisionerror()
+{
+  printf("Try increasing the area criterion and/or reducing the minimum\n");
+  printf("  allowable angle so that tiny triangles are not created.\n");
+#ifdef SINGLE
+  printf("Alternatively, try recompiling me with double precision\n");
+  printf("  arithmetic (by removing \"#define SINGLE\" from the\n");
+  printf("  source file or \"-DSINGLE\" from the makefile).\n");
+#endif /* SINGLE */
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  splitencsegs()   Split all the encroached subsegments.                   */
+/*                                                                           */
+/*  Each encroached subsegment is repaired by splitting it - inserting a     */
+/*  vertex at or near its midpoint.  Newly inserted vertices may encroach    */
+/*  upon other subsegments; these are also repaired.                         */
+/*                                                                           */
+/*  `triflaws' is a flag that specifies whether one should take note of new  */
+/*  bad triangles that result from inserting vertices to repair encroached   */
+/*  subsegments.                                                             */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void splitencsegs(struct mesh *m, struct behavior *b, int triflaws)
+#else /* not ANSI_DECLARATORS */
+void splitencsegs(m, b, triflaws)
+struct mesh *m;
+struct behavior *b;
+int triflaws;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri enctri;
+  struct otri testtri;
+  struct osub testsh;
+  struct osub currentenc;
+  struct badsubseg *encloop;
+  vertex eorg, edest, eapex;
+  vertex newvertex;
+  enum insertvertexresult success;
+  REAL segmentlength, nearestpoweroftwo;
+  REAL split;
+  REAL multiplier, divisor;
+  int acuteorg, acuteorg2, acutedest, acutedest2;
+  int dummy;
+  int i;
+  triangle ptr;                     /* Temporary variable used by stpivot(). */
+  subseg sptr;                        /* Temporary variable used by snext(). */
+
+  /* Note that steinerleft == -1 if an unlimited number */
+  /*   of Steiner points is allowed.                    */
+  while ((m->badsubsegs.items > 0) && (m->steinerleft != 0)) {
+    traversalinit(&m->badsubsegs);
+    encloop = badsubsegtraverse(m);
+    while ((encloop != (struct badsubseg *) NULL) && (m->steinerleft != 0)) {
+      sdecode(encloop->encsubseg, currentenc);
+      sorg(currentenc, eorg);
+      sdest(currentenc, edest);
+      /* Make sure that this segment is still the same segment it was   */
+      /*   when it was determined to be encroached.  If the segment was */
+      /*   enqueued multiple times (because several newly inserted      */
+      /*   vertices encroached it), it may have already been split.     */
+      if (!deadsubseg(currentenc.ss) &&
+          (eorg == encloop->subsegorg) && (edest == encloop->subsegdest)) {
+        /* To decide where to split a segment, we need to know if the   */
+        /*   segment shares an endpoint with an adjacent segment.       */
+        /*   The concern is that, if we simply split every encroached   */
+        /*   segment in its center, two adjacent segments with a small  */
+        /*   angle between them might lead to an infinite loop; each    */
+        /*   vertex added to split one segment will encroach upon the   */
+        /*   other segment, which must then be split with a vertex that */
+        /*   will encroach upon the first segment, and so on forever.   */
+        /* To avoid this, imagine a set of concentric circles, whose    */
+        /*   radii are powers of two, about each segment endpoint.      */
+        /*   These concentric circles determine where the segment is    */
+        /*   split.  (If both endpoints are shared with adjacent        */
+        /*   segments, split the segment in the middle, and apply the   */
+        /*   concentric circles for later splittings.)                  */
+
+        /* Is the origin shared with another segment? */
+        stpivot(currentenc, enctri);
+        lnext(enctri, testtri);
+        tspivot(testtri, testsh);
+        acuteorg = testsh.ss != m->dummysub;
+        /* Is the destination shared with another segment? */
+        lnextself(testtri);
+        tspivot(testtri, testsh);
+        acutedest = testsh.ss != m->dummysub;
+
+        /* If we're using diametral lenses (rather than diametral circles) */
+        /*   to define encroachment, delete free vertices from the         */
+        /*   subsegment's diametral circle.                                */
+        if (!b->nolenses && !acuteorg && !acutedest) {
+          apex(enctri, eapex);
+          while ((vertextype(eapex) == FREEVERTEX) &&
+                 ((eorg[0] - eapex[0]) * (edest[0] - eapex[0]) +
+                  (eorg[1] - eapex[1]) * (edest[1] - eapex[1]) < 0.0)) {
+            deletevertex(m, b, &testtri);
+            stpivot(currentenc, enctri);
+            apex(enctri, eapex);
+            lprev(enctri, testtri);
+          }
+        }
+
+        /* Now, check the other side of the segment, if there's a triangle */
+        /*   there.                                                        */
+        sym(enctri, testtri);
+        if (testtri.tri != m->dummytri) {
+          /* Is the destination shared with another segment? */
+          lnextself(testtri);
+          tspivot(testtri, testsh);
+          acutedest2 = testsh.ss != m->dummysub;
+          acutedest = acutedest || acutedest2;
+          /* Is the origin shared with another segment? */
+          lnextself(testtri);
+          tspivot(testtri, testsh);
+          acuteorg2 = testsh.ss != m->dummysub;
+          acuteorg = acuteorg || acuteorg2;
+
+          /* Delete free vertices from the subsegment's diametral circle. */
+          if (!b->nolenses && !acuteorg2 && !acutedest2) {
+            org(testtri, eapex);
+            while ((vertextype(eapex) == FREEVERTEX) &&
+                   ((eorg[0] - eapex[0]) * (edest[0] - eapex[0]) +
+                    (eorg[1] - eapex[1]) * (edest[1] - eapex[1]) < 0.0)) {
+              deletevertex(m, b, &testtri);
+              sym(enctri, testtri);
+              apex(testtri, eapex);
+              lprevself(testtri);
+            }
+          }
+        }
+
+        /* Use the concentric circles if exactly one endpoint is shared */
+        /*   with another adjacent segment.                             */
+        if (acuteorg || acutedest) {
+          segmentlength = sqrt((edest[0] - eorg[0]) * (edest[0] - eorg[0]) +
+                               (edest[1] - eorg[1]) * (edest[1] - eorg[1]));
+          /* Find the power of two that most evenly splits the segment.  */
+          /*   The worst case is a 2:1 ratio between subsegment lengths. */
+          nearestpoweroftwo = 1.0;
+          while (segmentlength > 3.0 * nearestpoweroftwo) {
+            nearestpoweroftwo *= 2.0;
+          }
+          while (segmentlength < 1.5 * nearestpoweroftwo) {
+            nearestpoweroftwo *= 0.5;
+          }
+          /* Where do we split the segment? */
+          split = nearestpoweroftwo / segmentlength;
+          if (acutedest) {
+            split = 1.0 - split;
+          }
+        } else {
+          /* If we're not worried about adjacent segments, split */
+          /*   this segment in the middle.                       */
+          split = 0.5;
+        }
+
+        /* Create the new vertex. */
+        newvertex = (vertex) poolalloc(&m->vertices);
+        /* Interpolate its coordinate and attributes. */
+        for (i = 0; i < 2 + m->nextras; i++) {
+          newvertex[i] = eorg[i] + split * (edest[i] - eorg[i]);
+        }
+
+        if (!b->noexact) {
+          /* Roundoff in the above calculation may yield a `newvertex'   */
+          /*   that is not precisely collinear with `eorg' and `edest'.  */
+          /*   Improve collinearity by one step of iterative refinement. */
+          multiplier = counterclockwise(m, b, eorg, edest, newvertex);
+          divisor = ((eorg[0] - edest[0]) * (eorg[0] - edest[0]) +
+                     (eorg[1] - edest[1]) * (eorg[1] - edest[1]));
+          if ((multiplier != 0.0) && (divisor != 0.0)) {
+            multiplier = multiplier / divisor;
+            /* Watch out for NANs. */
+            if (multiplier == multiplier) {
+              newvertex[0] += multiplier * (edest[1] - eorg[1]);
+              newvertex[1] += multiplier * (eorg[0] - edest[0]);
+            }
+          }
+        }
+
+        setvertexmark(newvertex, mark(currentenc));
+        setvertextype(newvertex, SEGMENTVERTEX);
+        if (b->verbose > 1) {
+          printf(
+  "  Splitting subsegment (%.12g, %.12g) (%.12g, %.12g) at (%.12g, %.12g).\n",
+                 eorg[0], eorg[1], edest[0], edest[1],
+                 newvertex[0], newvertex[1]);
+        }
+        /* Check whether the new vertex lies on an endpoint. */
+        if (((newvertex[0] == eorg[0]) && (newvertex[1] == eorg[1])) ||
+            ((newvertex[0] == edest[0]) && (newvertex[1] == edest[1]))) {
+          printf("Error:  Ran out of precision at (%.12g, %.12g).\n",
+                 newvertex[0], newvertex[1]);
+          printf("I attempted to split a segment to a smaller size than\n");
+          printf("  can be accommodated by the finite precision of\n");
+          printf("  floating point arithmetic.\n");
+          precisionerror();
+          exit(1);
+        }
+        /* Insert the splitting vertex.  This should always succeed. */
+        success = insertvertex(m, b, newvertex, &enctri, &currentenc,
+                               1, triflaws, 0.0);
+        if ((success != SUCCESSFULVERTEX) && (success != ENCROACHINGVERTEX)) {
+          printf("Internal error in splitencsegs():\n");
+          printf("  Failure to split a segment.\n");
+          internalerror();
+        }
+        if (m->steinerleft > 0) {
+          m->steinerleft--;
+        }
+        /* Check the two new subsegments to see if they're encroached. */
+        dummy = checkseg4encroach(m, b, &currentenc, 0.0);
+        snextself(currentenc);
+        dummy = checkseg4encroach(m, b, &currentenc, 0.0);
+      }
+
+      badsubsegdealloc(m, encloop);
+      encloop = badsubsegtraverse(m);
+    }
+  }
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  tallyfaces()   Test every triangle in the mesh for quality measures.     */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void tallyfaces(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void tallyfaces(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri triangleloop;
+
+  if (b->verbose) {
+    printf("  Making a list of bad triangles.\n");
+  }
+  traversalinit(&m->triangles);
+  triangleloop.orient = 0;
+  triangleloop.tri = triangletraverse(m);
+  while (triangleloop.tri != (triangle *) NULL) {
+    /* If the triangle is bad, enqueue it. */
+    testtriangle(m, b, &triangleloop);
+    triangleloop.tri = triangletraverse(m);
+  }
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  splittriangle()   Inserts a vertex at the circumcenter of a triangle.    */
+/*                    Deletes the newly inserted vertex if it encroaches     */
+/*                    upon a segment.                                        */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void splittriangle(struct mesh *m, struct behavior *b,
+                   struct badtriang *badtri)
+#else /* not ANSI_DECLARATORS */
+void splittriangle(m, b, badtri)
+struct mesh *m;
+struct behavior *b;
+struct badtriang *badtri;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri badotri;
+  vertex borg, bdest, bapex;
+  vertex newvertex;
+  REAL xi, eta;
+  REAL minedge;
+  enum insertvertexresult success;
+  int errorflag;
+  int i;
+
+  decode(badtri->poortri, badotri);
+  org(badotri, borg);
+  dest(badotri, bdest);
+  apex(badotri, bapex);
+  /* Make sure that this triangle is still the same triangle it was      */
+  /*   when it was tested and determined to be of bad quality.           */
+  /*   Subsequent transformations may have made it a different triangle. */
+  if (!deadtri(badotri.tri) && (borg == badtri->triangorg) &&
+      (bdest == badtri->triangdest) && (bapex == badtri->triangapex)) {
+    if (b->verbose > 1) {
+      printf("  Splitting this triangle at its circumcenter:\n");
+      printf("    (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", borg[0],
+             borg[1], bdest[0], bdest[1], bapex[0], bapex[1]);
+    }
+
+    errorflag = 0;
+    /* Create a new vertex at the triangle's circumcenter. */
+    newvertex = (vertex) poolalloc(&m->vertices);
+    findcircumcenter(m, b, borg, bdest, bapex, newvertex, &xi, &eta, &minedge,
+                     1);
+
+    /* Check whether the new vertex lies on a triangle vertex. */
+    if (((newvertex[0] == borg[0]) && (newvertex[1] == borg[1])) ||
+        ((newvertex[0] == bdest[0]) && (newvertex[1] == bdest[1])) ||
+        ((newvertex[0] == bapex[0]) && (newvertex[1] == bapex[1]))) {
+      if (!b->quiet) {
+        printf(
+            "Warning:  New vertex (%.12g, %.12g) falls on existing vertex.\n",
+               newvertex[0], newvertex[1]);
+        errorflag = 1;
+      }
+      vertexdealloc(m, newvertex);
+    } else {
+      for (i = 2; i < 2 + m->nextras; i++) {
+        /* Interpolate the vertex attributes at the circumcenter. */
+        newvertex[i] = borg[i] + xi * (bdest[i] - borg[i])
+                              + eta * (bapex[i] - borg[i]);
+      }
+      /* The new vertex must be in the interior, and therefore is a */
+      /*   free vertex with a marker of zero.                       */
+      setvertexmark(newvertex, 0);
+      setvertextype(newvertex, FREEVERTEX);
+
+      /* Ensure that the handle `badotri' does not represent the longest  */
+      /*   edge of the triangle.  This ensures that the circumcenter must */
+      /*   fall to the left of this edge, so point location will work.    */
+      /*   (If the angle org-apex-dest exceeds 90 degrees, then the       */
+      /*   circumcenter lies outside the org-dest edge, and eta is        */
+      /*   negative.  Roundoff error might prevent eta from being         */
+      /*   negative when it should be, so I test eta against xi.)         */
+      if (eta < xi) {
+        lprevself(badotri);
+      }
+
+      /* Insert the circumcenter, searching from the edge of the triangle, */
+      /*   and maintain the Delaunay property of the triangulation.        */
+      success = insertvertex(m, b, newvertex, &badotri, (struct osub *) NULL,
+                             1, 1, minedge);
+      if (success == SUCCESSFULVERTEX) {
+        if (m->steinerleft > 0) {
+          m->steinerleft--;
+        }
+      } else if (success == ENCROACHINGVERTEX) {
+        /* If the newly inserted vertex encroaches upon a subsegment, */
+        /*   delete the new vertex.                                   */
+        undovertex(m, b);
+        if (b->verbose > 1) {
+          printf("  Rejecting (%.12g, %.12g).\n", newvertex[0], newvertex[1]);
+        }
+        vertexdealloc(m, newvertex);
+      } else if (success == VIOLATINGVERTEX) {
+        /* Failed to insert the new vertex, but some subsegment was */
+        /*   marked as being encroached.                            */
+        vertexdealloc(m, newvertex);
+      } else {                                 /* success == DUPLICATEVERTEX */
+        /* Couldn't insert the new vertex because a vertex is already there. */
+        if (!b->quiet) {
+          printf(
+            "Warning:  New vertex (%.12g, %.12g) falls on existing vertex.\n",
+                 newvertex[0], newvertex[1]);
+          errorflag = 1;
+        }
+        vertexdealloc(m, newvertex);
+      }
+    }
+    if (errorflag) {
+      if (b->verbose) {
+        printf("  The new vertex is at the circumcenter of triangle\n");
+        printf("    (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n",
+               borg[0], borg[1], bdest[0], bdest[1], bapex[0], bapex[1]);
+      }
+      printf("This probably means that I am trying to refine triangles\n");
+      printf("  to a smaller size than can be accommodated by the finite\n");
+      printf("  precision of floating point arithmetic.  (You can be\n");
+      printf("  sure of this if I fail to terminate.)\n");
+      precisionerror();
+    }
+  }
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  enforcequality()   Remove all the encroached subsegments and bad         */
+/*                     triangles from the triangulation.                     */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void enforcequality(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void enforcequality(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct badtriang *badtri;
+  int i;
+
+  if (!b->quiet) {
+    printf("Adding Steiner points to enforce quality.\n");
+  }
+  /* Initialize the pool of encroached subsegments. */
+  poolinit(&m->badsubsegs, sizeof(struct badsubseg), BADSUBSEGPERBLOCK,
+           BADSUBSEGPERBLOCK, POINTER, 0);
+  if (b->verbose) {
+    printf("  Looking for encroached subsegments.\n");
+  }
+  /* Test all segments to see if they're encroached. */
+  tallyencs(m, b);
+  if (b->verbose && (m->badsubsegs.items > 0)) {
+    printf("  Splitting encroached subsegments.\n");
+  }
+  /* Fix encroached subsegments without noting bad triangles. */
+  splitencsegs(m, b, 0);
+  /* At this point, if we haven't run out of Steiner points, the */
+  /*   triangulation should be (conforming) Delaunay.            */
+
+  /* Next, we worry about enforcing triangle quality. */
+  if ((b->minangle > 0.0) || b->vararea || b->fixedarea || b->usertest) {
+    /* Initialize the pool of bad triangles. */
+    poolinit(&m->badtriangles, sizeof(struct badtriang), BADTRIPERBLOCK,
+             BADTRIPERBLOCK, POINTER, 0);
+    /* Initialize the queues of bad triangles. */
+    for (i = 0; i < 64; i++) {
+      m->queuefront[i] = (struct badtriang *) NULL;
+    }
+    m->firstnonemptyq = -1;
+    /* Test all triangles to see if they're bad. */
+    tallyfaces(m, b);
+    /* Initialize the pool of recently flipped triangles. */
+    poolinit(&m->flipstackers, sizeof(struct flipstacker), FLIPSTACKERPERBLOCK,
+             FLIPSTACKERPERBLOCK, POINTER, 0);
+    m->checkquality = 1;
+    if (b->verbose) {
+      printf("  Splitting bad triangles.\n");
+    }
+    while ((m->badtriangles.items > 0) && (m->steinerleft != 0)) {
+      /* Fix one bad triangle by inserting a vertex at its circumcenter. */
+      badtri = dequeuebadtriang(m);
+      splittriangle(m, b, badtri);
+      if (m->badsubsegs.items > 0) {
+        /* Put bad triangle back in queue for another try later. */
+        enqueuebadtriang(m, b, badtri);
+        /* Fix any encroached subsegments that resulted. */
+        /*   Record any new bad triangles that result.   */
+        splitencsegs(m, b, 1);
+      } else {
+        /* Return the bad triangle to the pool. */
+        pooldealloc(&m->badtriangles, (VOID *) badtri);
+      }
+    }
+  }
+  /* At this point, if we haven't run out of Steiner points, the */
+  /*   triangulation should be (conforming) Delaunay and have no */
+  /*   low-quality triangles.                                    */
+
+  /* Might we have run out of Steiner points too soon? */
+  if (!b->quiet && (m->badsubsegs.items > 0) && (m->steinerleft == 0)) {
+    printf("\nWarning:  I ran out of Steiner points, but the mesh has\n");
+    if (m->badsubsegs.items == 1) {
+      printf("  an encroached subsegment, and therefore might not be truly\n");
+    } else {
+      printf("  %ld encroached subsegments, and therefore might not be truly\n"
+             , m->badsubsegs.items);
+    }
+    printf("  Delaunay.  If the Delaunay property is important to you,\n");
+    printf("  try increasing the number of Steiner points (controlled by\n");
+    printf("  the -S switch) slightly and try again.\n\n");
+  }
+}
+
+#endif /* not CDT_ONLY */
+
+/**                                                                         **/
+/**                                                                         **/
+/********* Mesh quality maintenance ends here                        *********/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  highorder()   Create extra nodes for quadratic subparametric elements.   */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void highorder(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void highorder(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri triangleloop, trisym;
+  struct osub checkmark;
+  vertex newvertex;
+  vertex torg, tdest;
+  int i;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+  if (!b->quiet) {
+    printf("Adding vertices for second-order triangles.\n");
+  }
+  /* The following line ensures that dead items in the pool of nodes    */
+  /*   cannot be allocated for the extra nodes associated with high     */
+  /*   order elements.  This ensures that the primary nodes (at the     */
+  /*   corners of elements) will occur earlier in the output files, and */
+  /*   have lower indices, than the extra nodes.                        */
+  m->vertices.deaditemstack = (VOID *) NULL;
+
+  traversalinit(&m->triangles);
+  triangleloop.tri = triangletraverse(m);
+  /* To loop over the set of edges, loop over all triangles, and look at   */
+  /*   the three edges of each triangle.  If there isn't another triangle  */
+  /*   adjacent to the edge, operate on the edge.  If there is another     */
+  /*   adjacent triangle, operate on the edge only if the current triangle */
+  /*   has a smaller pointer than its neighbor.  This way, each edge is    */
+  /*   considered only once.                                               */
+  while (triangleloop.tri != (triangle *) NULL) {
+    for (triangleloop.orient = 0; triangleloop.orient < 3;
+         triangleloop.orient++) {
+      sym(triangleloop, trisym);
+      if ((triangleloop.tri < trisym.tri) || (trisym.tri == m->dummytri)) {
+        org(triangleloop, torg);
+        dest(triangleloop, tdest);
+        /* Create a new node in the middle of the edge.  Interpolate */
+        /*   its attributes.                                         */
+        newvertex = (vertex) poolalloc(&m->vertices);
+        for (i = 0; i < 2 + m->nextras; i++) {
+          newvertex[i] = 0.5 * (torg[i] + tdest[i]);
+        }
+        /* Set the new node's marker to zero or one, depending on */
+        /*   whether it lies on a boundary.                       */
+        setvertexmark(newvertex, trisym.tri == m->dummytri);
+        setvertextype(newvertex,
+                      trisym.tri == m->dummytri ? FREEVERTEX : SEGMENTVERTEX);
+        if (b->usesegments) {
+          tspivot(triangleloop, checkmark);
+          /* If this edge is a segment, transfer the marker to the new node. */
+          if (checkmark.ss != m->dummysub) {
+            setvertexmark(newvertex, mark(checkmark));
+            setvertextype(newvertex, SEGMENTVERTEX);
+          }
+        }
+        if (b->verbose > 1) {
+          printf("  Creating (%.12g, %.12g).\n", newvertex[0], newvertex[1]);
+        }
+        /* Record the new node in the (one or two) adjacent elements. */
+        triangleloop.tri[m->highorderindex + triangleloop.orient] =
+                (triangle) newvertex;
+        if (trisym.tri != m->dummytri) {
+          trisym.tri[m->highorderindex + trisym.orient] = (triangle) newvertex;
+        }
+      }
+    }
+    triangleloop.tri = triangletraverse(m);
+  }
+}
+
+/********* File I/O routines begin here                              *********/
+/**                                                                         **/
+/**                                                                         **/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  readline()   Read a nonempty line from a file.                           */
+/*                                                                           */
+/*  A line is considered "nonempty" if it contains something that looks like */
+/*  a number.  Comments (prefaced by `#') are ignored.                       */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+char *readline(char *string, FILE *infile, char *infilename)
+#else /* not ANSI_DECLARATORS */
+char *readline(string, infile, infilename)
+char *string;
+FILE *infile;
+char *infilename;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  char *result;
+
+  /* Search for something that looks like a number. */
+  do {
+    result = fgets(string, INPUTLINESIZE, infile);
+    if (result == (char *) NULL) {
+      printf("  Error:  Unexpected end of file in %s.\n", infilename);
+      exit(1);
+    }
+    /* Skip anything that doesn't look like a number, a comment, */
+    /*   or the end of a line.                                   */
+    while ((*result != '\0') && (*result != '#')
+           && (*result != '.') && (*result != '+') && (*result != '-')
+           && ((*result < '0') || (*result > '9'))) {
+      result++;
+    }
+  /* If it's a comment or end of line, read another line and try again. */
+  } while ((*result == '#') || (*result == '\0'));
+  return result;
+}
+
+#endif /* not TRILIBRARY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  findfield()   Find the next field of a string.                           */
+/*                                                                           */
+/*  Jumps past the current field by searching for whitespace, then jumps     */
+/*  past the whitespace to find the next field.                              */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+char *findfield(char *string)
+#else /* not ANSI_DECLARATORS */
+char *findfield(string)
+char *string;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  char *result;
+
+  result = string;
+  /* Skip the current field.  Stop upon reaching whitespace. */
+  while ((*result != '\0') && (*result != '#')
+         && (*result != ' ') && (*result != '\t')) {
+    result++;
+  }
+  /* Now skip the whitespace and anything else that doesn't look like a */
+  /*   number, a comment, or the end of a line.                         */
+  while ((*result != '\0') && (*result != '#')
+         && (*result != '.') && (*result != '+') && (*result != '-')
+         && ((*result < '0') || (*result > '9'))) {
+    result++;
+  }
+  /* Check for a comment (prefixed with `#'). */
+  if (*result == '#') {
+    *result = '\0';
+  }
+  return result;
+}
+
+#endif /* not TRILIBRARY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  readnodes()   Read the vertices from a file, which may be a .node or     */
+/*                .poly file.                                                */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void readnodes(struct mesh *m, struct behavior *b, char *nodefilename,
+               char *polyfilename, FILE **polyfile)
+#else /* not ANSI_DECLARATORS */
+void readnodes(m, b, nodefilename, polyfilename, polyfile)
+struct mesh *m;
+struct behavior *b;
+char *nodefilename;
+char *polyfilename;
+FILE **polyfile;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  FILE *infile;
+  vertex vertexloop;
+  char inputline[INPUTLINESIZE];
+  char *stringptr;
+  char *infilename;
+  REAL x, y;
+  int firstnode;
+  int nodemarkers;
+  int currentmarker;
+  int i, j;
+
+  if (b->poly) {
+    /* Read the vertices from a .poly file. */
+    if (!b->quiet) {
+      printf("Opening %s.\n", polyfilename);
+    }
+    *polyfile = fopen(polyfilename, "r");
+    if (*polyfile == (FILE *) NULL) {
+      printf("  Error:  Cannot access file %s.\n", polyfilename);
+      exit(1);
+    }
+    /* Read number of vertices, number of dimensions, number of vertex */
+    /*   attributes, and number of boundary markers.                   */
+    stringptr = readline(inputline, *polyfile, polyfilename);
+    m->invertices = (int) strtol(stringptr, &stringptr, 0);
+    stringptr = findfield(stringptr);
+    if (*stringptr == '\0') {
+      m->mesh_dim = 2;
+    } else {
+      m->mesh_dim = (int) strtol(stringptr, &stringptr, 0);
+    }
+    stringptr = findfield(stringptr);
+    if (*stringptr == '\0') {
+      m->nextras = 0;
+    } else {
+      m->nextras = (int) strtol(stringptr, &stringptr, 0);
+    }
+    stringptr = findfield(stringptr);
+    if (*stringptr == '\0') {
+      nodemarkers = 0;
+    } else {
+      nodemarkers = (int) strtol(stringptr, &stringptr, 0);
+    }
+    if (m->invertices > 0) {
+      infile = *polyfile;
+      infilename = polyfilename;
+      m->readnodefile = 0;
+    } else {
+      /* If the .poly file claims there are zero vertices, that means that */
+      /*   the vertices should be read from a separate .node file.         */
+      m->readnodefile = 1;
+      infilename = nodefilename;
+    }
+  } else {
+    m->readnodefile = 1;
+    infilename = nodefilename;
+    *polyfile = (FILE *) NULL;
+  }
+
+  if (m->readnodefile) {
+    /* Read the vertices from a .node file. */
+    if (!b->quiet) {
+      printf("Opening %s.\n", nodefilename);
+    }
+    infile = fopen(nodefilename, "r");
+    if (infile == (FILE *) NULL) {
+      printf("  Error:  Cannot access file %s.\n", nodefilename);
+      exit(1);
+    }
+    /* Read number of vertices, number of dimensions, number of vertex */
+    /*   attributes, and number of boundary markers.                   */
+    stringptr = readline(inputline, infile, nodefilename);
+    m->invertices = (int) strtol(stringptr, &stringptr, 0);
+    stringptr = findfield(stringptr);
+    if (*stringptr == '\0') {
+      m->mesh_dim = 2;
+    } else {
+      m->mesh_dim = (int) strtol(stringptr, &stringptr, 0);
+    }
+    stringptr = findfield(stringptr);
+    if (*stringptr == '\0') {
+      m->nextras = 0;
+    } else {
+      m->nextras = (int) strtol(stringptr, &stringptr, 0);
+    }
+    stringptr = findfield(stringptr);
+    if (*stringptr == '\0') {
+      nodemarkers = 0;
+    } else {
+      nodemarkers = (int) strtol(stringptr, &stringptr, 0);
+    }
+  }
+
+  if (m->invertices < 3) {
+    printf("Error:  Input must have at least three input vertices.\n");
+    exit(1);
+  }
+  if (m->mesh_dim != 2) {
+    printf("Error:  Triangle only works with two-dimensional meshes.\n");
+    exit(1);
+  }
+  if (m->nextras == 0) {
+    b->weighted = 0;
+  }
+
+  initializevertexpool(m, b);
+
+  /* Read the vertices. */
+  for (i = 0; i < m->invertices; i++) {
+    vertexloop = (vertex) poolalloc(&m->vertices);
+    stringptr = readline(inputline, infile, infilename);
+    if (i == 0) {
+      firstnode = (int) strtol(stringptr, &stringptr, 0);
+      if ((firstnode == 0) || (firstnode == 1)) {
+        b->firstnumber = firstnode;
+      }
+    }
+    stringptr = findfield(stringptr);
+    if (*stringptr == '\0') {
+      printf("Error:  Vertex %d has no x coordinate.\n", b->firstnumber + i);
+      exit(1);
+    }
+    x = (REAL) strtod(stringptr, &stringptr);
+    stringptr = findfield(stringptr);
+    if (*stringptr == '\0') {
+      printf("Error:  Vertex %d has no y coordinate.\n", b->firstnumber + i);
+      exit(1);
+    }
+    y = (REAL) strtod(stringptr, &stringptr);
+    vertexloop[0] = x;
+    vertexloop[1] = y;
+    /* Read the vertex attributes. */
+    for (j = 2; j < 2 + m->nextras; j++) {
+      stringptr = findfield(stringptr);
+      if (*stringptr == '\0') {
+        vertexloop[j] = 0.0;
+      } else {
+        vertexloop[j] = (REAL) strtod(stringptr, &stringptr);
+      }
+    }
+    if (nodemarkers) {
+      /* Read a vertex marker. */
+      stringptr = findfield(stringptr);
+      if (*stringptr == '\0') {
+        setvertexmark(vertexloop, 0);
+      } else {
+        currentmarker = (int) strtol(stringptr, &stringptr, 0);
+        setvertexmark(vertexloop, currentmarker);
+      }
+    } else {
+      /* If no markers are specified in the file, they default to zero. */
+      setvertexmark(vertexloop, 0);
+    }
+    setvertextype(vertexloop, INPUTVERTEX);
+    /* Determine the smallest and largest x and y coordinates. */
+    if (i == 0) {
+      m->xmin = m->xmax = x;
+      m->ymin = m->ymax = y;
+    } else {
+      m->xmin = (x < m->xmin) ? x : m->xmin;
+      m->xmax = (x > m->xmax) ? x : m->xmax;
+      m->ymin = (y < m->ymin) ? y : m->ymin;
+      m->ymax = (y > m->ymax) ? y : m->ymax;
+    }
+  }
+  if (m->readnodefile) {
+    fclose(infile);
+  }
+
+  /* Nonexistent x value used as a flag to mark circle events in sweepline */
+  /*   Delaunay algorithm.                                                 */
+  m->xminextreme = 10 * m->xmin - 9 * m->xmax;
+}
+
+#endif /* not TRILIBRARY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  transfernodes()   Read the vertices from memory.                         */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void transfernodes(struct mesh *m, struct behavior *b, REAL *pointlist,
+                   REAL *pointattriblist, int *pointmarkerlist,
+                   int numberofpoints, int numberofpointattribs)
+#else /* not ANSI_DECLARATORS */
+void transfernodes(m, b, pointlist, pointattriblist, pointmarkerlist,
+                   numberofpoints, numberofpointattribs)
+struct mesh *m;
+struct behavior *b;
+REAL *pointlist;
+REAL *pointattriblist;
+int *pointmarkerlist;
+int numberofpoints;
+int numberofpointattribs;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  vertex vertexloop;
+  REAL x, y;
+  int i, j;
+  int coordindex;
+  int attribindex;
+
+  m->invertices = numberofpoints;
+  m->mesh_dim = 2;
+  m->nextras = numberofpointattribs;
+  m->readnodefile = 0;
+  if (m->invertices < 3) {
+    printf("Error:  Input must have at least three input vertices.\n");
+    exit(1);
+  }
+  if (m->nextras == 0) {
+    b->weighted = 0;
+  }
+
+  initializevertexpool(m, b);
+
+  /* Read the vertices. */
+  coordindex = 0;
+  attribindex = 0;
+  for (i = 0; i < m->invertices; i++) {
+    vertexloop = (vertex) poolalloc(&m->vertices);
+    /* Read the vertex coordinates. */
+    x = vertexloop[0] = pointlist[coordindex++];
+    y = vertexloop[1] = pointlist[coordindex++];
+    /* Read the vertex attributes. */
+    for (j = 0; j < numberofpointattribs; j++) {
+      vertexloop[2 + j] = pointattriblist[attribindex++];
+    }
+    if (pointmarkerlist != (int *) NULL) {
+      /* Read a vertex marker. */
+      setvertexmark(vertexloop, pointmarkerlist[i]);
+    } else {
+      /* If no markers are specified, they default to zero. */
+      setvertexmark(vertexloop, 0);
+    }
+    setvertextype(vertexloop, INPUTVERTEX);
+    /* Determine the smallest and largest x and y coordinates. */
+    if (i == 0) {
+      m->xmin = m->xmax = x;
+      m->ymin = m->ymax = y;
+    } else {
+      m->xmin = (x < m->xmin) ? x : m->xmin;
+      m->xmax = (x > m->xmax) ? x : m->xmax;
+      m->ymin = (y < m->ymin) ? y : m->ymin;
+      m->ymax = (y > m->ymax) ? y : m->ymax;
+    }
+  }
+
+  /* Nonexistent x value used as a flag to mark circle events in sweepline */
+  /*   Delaunay algorithm.                                                 */
+  m->xminextreme = 10 * m->xmin - 9 * m->xmax;
+}
+
+#endif /* TRILIBRARY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  readholes()   Read the holes, and possibly regional attributes and area  */
+/*                constraints, from a .poly file.                            */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void readholes(struct mesh *m, struct behavior *b,
+               FILE *polyfile, char *polyfilename, REAL **hlist, int *holes,
+               REAL **rlist, int *regions)
+#else /* not ANSI_DECLARATORS */
+void readholes(m, b, polyfile, polyfilename, hlist, holes, rlist, regions)
+struct mesh *m;
+struct behavior *b;
+FILE *polyfile;
+char *polyfilename;
+REAL **hlist;
+int *holes;
+REAL **rlist;
+int *regions;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  REAL *holelist;
+  REAL *regionlist;
+  char inputline[INPUTLINESIZE];
+  char *stringptr;
+  int index;
+  int i;
+
+  /* Read the holes. */
+  stringptr = readline(inputline, polyfile, polyfilename);
+  *holes = (int) strtol(stringptr, &stringptr, 0);
+  if (*holes > 0) {
+    holelist = (REAL *) trimalloc(2 * *holes * (int) sizeof(REAL));
+    *hlist = holelist;
+    for (i = 0; i < 2 * *holes; i += 2) {
+      stringptr = readline(inputline, polyfile, polyfilename);
+      stringptr = findfield(stringptr);
+      if (*stringptr == '\0') {
+        printf("Error:  Hole %d has no x coordinate.\n",
+               b->firstnumber + (i >> 1));
+        exit(1);
+      } else {
+        holelist[i] = (REAL) strtod(stringptr, &stringptr);
+      }
+      stringptr = findfield(stringptr);
+      if (*stringptr == '\0') {
+        printf("Error:  Hole %d has no y coordinate.\n",
+               b->firstnumber + (i >> 1));
+        exit(1);
+      } else {
+        holelist[i + 1] = (REAL) strtod(stringptr, &stringptr);
+      }
+    }
+  } else {
+    *hlist = (REAL *) NULL;
+  }
+
+#ifndef CDT_ONLY
+  if ((b->regionattrib || b->vararea) && !b->refine) {
+    /* Read the area constraints. */
+    stringptr = readline(inputline, polyfile, polyfilename);
+    *regions = (int) strtol(stringptr, &stringptr, 0);
+    if (*regions > 0) {
+      regionlist = (REAL *) trimalloc(4 * *regions * (int) sizeof(REAL));
+      *rlist = regionlist;
+      index = 0;
+      for (i = 0; i < *regions; i++) {
+        stringptr = readline(inputline, polyfile, polyfilename);
+        stringptr = findfield(stringptr);
+        if (*stringptr == '\0') {
+          printf("Error:  Region %d has no x coordinate.\n",
+                 b->firstnumber + i);
+          exit(1);
+        } else {
+          regionlist[index++] = (REAL) strtod(stringptr, &stringptr);
+        }
+        stringptr = findfield(stringptr);
+        if (*stringptr == '\0') {
+          printf("Error:  Region %d has no y coordinate.\n",
+                 b->firstnumber + i);
+          exit(1);
+        } else {
+          regionlist[index++] = (REAL) strtod(stringptr, &stringptr);
+        }
+        stringptr = findfield(stringptr);
+        if (*stringptr == '\0') {
+          printf(
+            "Error:  Region %d has no region attribute or area constraint.\n",
+                 b->firstnumber + i);
+          exit(1);
+        } else {
+          regionlist[index++] = (REAL) strtod(stringptr, &stringptr);
+        }
+        stringptr = findfield(stringptr);
+        if (*stringptr == '\0') {
+          regionlist[index] = regionlist[index - 1];
+        } else {
+          regionlist[index] = (REAL) strtod(stringptr, &stringptr);
+        }
+        index++;
+      }
+    }
+  } else {
+    /* Set `*regions' to zero to avoid an accidental free() later. */
+    *regions = 0;
+    *rlist = (REAL *) NULL;
+  }
+#endif /* not CDT_ONLY */
+
+  fclose(polyfile);
+}
+
+#endif /* not TRILIBRARY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  finishfile()   Write the command line to the output file so the user     */
+/*                 can remember how the file was generated.  Close the file. */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void finishfile(FILE *outfile, int argc, char **argv)
+#else /* not ANSI_DECLARATORS */
+void finishfile(outfile, argc, argv)
+FILE *outfile;
+int argc;
+char **argv;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  int i;
+
+  fprintf(outfile, "# Generated by");
+  for (i = 0; i < argc; i++) {
+    fprintf(outfile, " ");
+    fputs(argv[i], outfile);
+  }
+  fprintf(outfile, "\n");
+  fclose(outfile);
+}
+
+#endif /* not TRILIBRARY */
+
+/*****************************************************************************/
+/*                                                                           */
+/*  writenodes()   Number the vertices and write them to a .node file.       */
+/*                                                                           */
+/*  To save memory, the vertex numbers are written over the boundary markers */
+/*  after the vertices are written to a file.                                */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void writenodes(struct mesh *m, struct behavior *b, REAL **pointlist,
+                REAL **pointattriblist, int **pointmarkerlist)
+#else /* not ANSI_DECLARATORS */
+void writenodes(m, b, pointlist, pointattriblist, pointmarkerlist)
+struct mesh *m;
+struct behavior *b;
+REAL **pointlist;
+REAL **pointattriblist;
+int **pointmarkerlist;
+#endif /* not ANSI_DECLARATORS */
+
+#else /* not TRILIBRARY */
+
+#ifdef ANSI_DECLARATORS
+void writenodes(struct mesh *m, struct behavior *b, char *nodefilename,
+                int argc, char **argv)
+#else /* not ANSI_DECLARATORS */
+void writenodes(m, b, nodefilename, argc, argv)
+struct mesh *m;
+struct behavior *b;
+char *nodefilename;
+int argc;
+char **argv;
+#endif /* not ANSI_DECLARATORS */
+
+#endif /* not TRILIBRARY */
+
+{
+#ifdef TRILIBRARY
+  REAL *plist;
+  REAL *palist;
+  int *pmlist;
+  int coordindex;
+  int attribindex;
+#else /* not TRILIBRARY */
+  FILE *outfile;
+#endif /* not TRILIBRARY */
+  vertex vertexloop;
+  long outvertices;
+  int vertexnumber;
+  int i;
+
+  if (b->jettison) {
+    outvertices = m->vertices.items - m->undeads;
+  } else {
+    outvertices = m->vertices.items;
+  }
+
+#ifdef TRILIBRARY
+  if (!b->quiet) {
+    printf("Writing vertices.\n");
+  }
+  /* Allocate memory for output vertices if necessary. */
+  if (*pointlist == (REAL *) NULL) {
+    *pointlist = (REAL *) trimalloc(outvertices * 2 * sizeof(REAL));
+  }
+  /* Allocate memory for output vertex attributes if necessary. */
+  if ((m->nextras > 0) && (*pointattriblist == (REAL *) NULL)) {
+    *pointattriblist = (REAL *) trimalloc(outvertices * m->nextras *
+                                          sizeof(REAL));
+  }
+  /* Allocate memory for output vertex markers if necessary. */
+  if (!b->nobound && (*pointmarkerlist == (int *) NULL)) {
+    *pointmarkerlist = (int *) trimalloc(outvertices * sizeof(int));
+  }
+  plist = *pointlist;
+  palist = *pointattriblist;
+  pmlist = *pointmarkerlist;
+  coordindex = 0;
+  attribindex = 0;
+#else /* not TRILIBRARY */
+  if (!b->quiet) {
+    printf("Writing %s.\n", nodefilename);
+  }
+  outfile = fopen(nodefilename, "w");
+  if (outfile == (FILE *) NULL) {
+    printf("  Error:  Cannot create file %s.\n", nodefilename);
+    exit(1);
+  }
+  /* Number of vertices, number of dimensions, number of vertex attributes, */
+  /*   and number of boundary markers (zero or one).                        */
+  fprintf(outfile, "%ld  %d  %d  %d\n", outvertices, m->mesh_dim,
+          m->nextras, 1 - b->nobound);
+#endif /* not TRILIBRARY */
+
+  traversalinit(&m->vertices);
+  vertexnumber = b->firstnumber;
+  vertexloop = vertextraverse(m);
+  while (vertexloop != (vertex) NULL) {
+    if (!b->jettison || (vertextype(vertexloop) != UNDEADVERTEX)) {
+#ifdef TRILIBRARY
+      /* X and y coordinates. */
+      plist[coordindex++] = vertexloop[0];
+      plist[coordindex++] = vertexloop[1];
+      /* Vertex attributes. */
+      for (i = 0; i < m->nextras; i++) {
+        palist[attribindex++] = vertexloop[2 + i];
+      }
+      if (!b->nobound) {
+        /* Copy the boundary marker. */
+        pmlist[vertexnumber - b->firstnumber] = vertexmark(vertexloop);
+      }
+#else /* not TRILIBRARY */
+      /* Vertex number, x and y coordinates. */
+      fprintf(outfile, "%4d    %.17g  %.17g", vertexnumber, vertexloop[0],
+              vertexloop[1]);
+      for (i = 0; i < m->nextras; i++) {
+        /* Write an attribute. */
+        fprintf(outfile, "  %.17g", vertexloop[i + 2]);
+      }
+      if (b->nobound) {
+        fprintf(outfile, "\n");
+      } else {
+        /* Write the boundary marker. */
+        fprintf(outfile, "    %d\n", vertexmark(vertexloop));
+      }
+#endif /* not TRILIBRARY */
+
+      setvertexmark(vertexloop, vertexnumber);
+      vertexnumber++;
+    }
+    vertexloop = vertextraverse(m);
+  }
+
+#ifndef TRILIBRARY
+  finishfile(outfile, argc, argv);
+#endif /* not TRILIBRARY */
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  numbernodes()   Number the vertices.                                     */
+/*                                                                           */
+/*  Each vertex is assigned a marker equal to its number.                    */
+/*                                                                           */
+/*  Used when writenodes() is not called because no .node file is written.   */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void numbernodes(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void numbernodes(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  vertex vertexloop;
+  int vertexnumber;
+
+  traversalinit(&m->vertices);
+  vertexnumber = b->firstnumber;
+  vertexloop = vertextraverse(m);
+  while (vertexloop != (vertex) NULL) {
+    setvertexmark(vertexloop, vertexnumber);
+    if (!b->jettison || (vertextype(vertexloop) != UNDEADVERTEX)) {
+      vertexnumber++;
+    }
+    vertexloop = vertextraverse(m);
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  writeelements()   Write the triangles to an .ele file.                   */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void writeelements(struct mesh *m, struct behavior *b,
+                   int **trianglelist, REAL **triangleattriblist)
+#else /* not ANSI_DECLARATORS */
+void writeelements(m, b, trianglelist, triangleattriblist)
+struct mesh *m;
+struct behavior *b;
+int **trianglelist;
+REAL **triangleattriblist;
+#endif /* not ANSI_DECLARATORS */
+
+#else /* not TRILIBRARY */
+
+#ifdef ANSI_DECLARATORS
+void writeelements(struct mesh *m, struct behavior *b, char *elefilename,
+                   int argc, char **argv)
+#else /* not ANSI_DECLARATORS */
+void writeelements(m, b, elefilename, argc, argv)
+struct mesh *m;
+struct behavior *b;
+char *elefilename;
+int argc;
+char **argv;
+#endif /* not ANSI_DECLARATORS */
+
+#endif /* not TRILIBRARY */
+
+{
+#ifdef TRILIBRARY
+  int *tlist;
+  REAL *talist;
+  int vertexindex;
+  int attribindex;
+#else /* not TRILIBRARY */
+  FILE *outfile;
+#endif /* not TRILIBRARY */
+  struct otri triangleloop;
+  vertex p1, p2, p3;
+  vertex mid1, mid2, mid3;
+  long elementnumber;
+  int i;
+
+#ifdef TRILIBRARY
+  if (!b->quiet) {
+    printf("Writing triangles.\n");
+  }
+  /* Allocate memory for output triangles if necessary. */
+  if (*trianglelist == (int *) NULL) {
+    *trianglelist = (int *) trimalloc(m->triangles.items *
+                                      ((b->order + 1) * (b->order + 2) / 2) *
+                                      sizeof(int));
+  }
+  /* Allocate memory for output triangle attributes if necessary. */
+  if ((m->eextras > 0) && (*triangleattriblist == (REAL *) NULL)) {
+    *triangleattriblist = (REAL *) trimalloc(m->triangles.items * m->eextras *
+                                             sizeof(REAL));
+  }
+  tlist = *trianglelist;
+  talist = *triangleattriblist;
+  vertexindex = 0;
+  attribindex = 0;
+#else /* not TRILIBRARY */
+  if (!b->quiet) {
+    printf("Writing %s.\n", elefilename);
+  }
+  outfile = fopen(elefilename, "w");
+  if (outfile == (FILE *) NULL) {
+    printf("  Error:  Cannot create file %s.\n", elefilename);
+    exit(1);
+  }
+  /* Number of triangles, vertices per triangle, attributes per triangle. */
+  fprintf(outfile, "%ld  %d  %d\n", m->triangles.items,
+          (b->order + 1) * (b->order + 2) / 2, m->eextras);
+#endif /* not TRILIBRARY */
+
+  traversalinit(&m->triangles);
+  triangleloop.tri = triangletraverse(m);
+  triangleloop.orient = 0;
+  elementnumber = b->firstnumber;
+  while (triangleloop.tri != (triangle *) NULL) {
+    org(triangleloop, p1);
+    dest(triangleloop, p2);
+    apex(triangleloop, p3);
+    if (b->order == 1) {
+#ifdef TRILIBRARY
+      tlist[vertexindex++] = vertexmark(p1);
+      tlist[vertexindex++] = vertexmark(p2);
+      tlist[vertexindex++] = vertexmark(p3);
+#else /* not TRILIBRARY */
+      /* Triangle number, indices for three vertices. */
+      fprintf(outfile, "%4ld    %4d  %4d  %4d", elementnumber,
+              vertexmark(p1), vertexmark(p2), vertexmark(p3));
+#endif /* not TRILIBRARY */
+    } else {
+      mid1 = (vertex) triangleloop.tri[m->highorderindex + 1];
+      mid2 = (vertex) triangleloop.tri[m->highorderindex + 2];
+      mid3 = (vertex) triangleloop.tri[m->highorderindex];
+#ifdef TRILIBRARY
+      tlist[vertexindex++] = vertexmark(p1);
+      tlist[vertexindex++] = vertexmark(p2);
+      tlist[vertexindex++] = vertexmark(p3);
+      tlist[vertexindex++] = vertexmark(mid1);
+      tlist[vertexindex++] = vertexmark(mid2);
+      tlist[vertexindex++] = vertexmark(mid3);
+#else /* not TRILIBRARY */
+      /* Triangle number, indices for six vertices. */
+      fprintf(outfile, "%4ld    %4d  %4d  %4d  %4d  %4d  %4d", elementnumber,
+              vertexmark(p1), vertexmark(p2), vertexmark(p3), vertexmark(mid1),
+              vertexmark(mid2), vertexmark(mid3));
+#endif /* not TRILIBRARY */
+    }
+
+#ifdef TRILIBRARY
+    for (i = 0; i < m->eextras; i++) {
+      talist[attribindex++] = elemattribute(triangleloop, i);
+    }
+#else /* not TRILIBRARY */
+    for (i = 0; i < m->eextras; i++) {
+      fprintf(outfile, "  %.17g", elemattribute(triangleloop, i));
+    }
+    fprintf(outfile, "\n");
+#endif /* not TRILIBRARY */
+
+    triangleloop.tri = triangletraverse(m);
+    elementnumber++;
+  }
+
+#ifndef TRILIBRARY
+  finishfile(outfile, argc, argv);
+#endif /* not TRILIBRARY */
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  writepoly()   Write the segments and holes to a .poly file.              */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void writepoly(struct mesh *m, struct behavior *b,
+               int **segmentlist, int **segmentmarkerlist)
+#else /* not ANSI_DECLARATORS */
+void writepoly(m, b, segmentlist, segmentmarkerlist)
+struct mesh *m;
+struct behavior *b;
+int **segmentlist;
+int **segmentmarkerlist;
+#endif /* not ANSI_DECLARATORS */
+
+#else /* not TRILIBRARY */
+
+#ifdef ANSI_DECLARATORS
+void writepoly(struct mesh *m, struct behavior *b, char *polyfilename,
+               REAL *holelist, int holes, REAL *regionlist, int regions,
+               int argc, char **argv)
+#else /* not ANSI_DECLARATORS */
+void writepoly(m, b, polyfilename, holelist, holes, regionlist, regions,
+               argc, argv)
+struct mesh *m;
+struct behavior *b;
+char *polyfilename;
+REAL *holelist;
+int holes;
+REAL *regionlist;
+int regions;
+int argc;
+char **argv;
+#endif /* not ANSI_DECLARATORS */
+
+#endif /* not TRILIBRARY */
+
+{
+#ifdef TRILIBRARY
+  int *slist;
+  int *smlist;
+  int index;
+#else /* not TRILIBRARY */
+  FILE *outfile;
+  long holenumber, regionnumber;
+#endif /* not TRILIBRARY */
+  struct osub subsegloop;
+  vertex endpoint1, endpoint2;
+  long subsegnumber;
+
+#ifdef TRILIBRARY
+  if (!b->quiet) {
+    printf("Writing segments.\n");
+  }
+  /* Allocate memory for output segments if necessary. */
+  if (*segmentlist == (int *) NULL) {
+    *segmentlist = (int *) trimalloc(m->subsegs.items * 2 * sizeof(int));
+  }
+  /* Allocate memory for output segment markers if necessary. */
+  if (!b->nobound && (*segmentmarkerlist == (int *) NULL)) {
+    *segmentmarkerlist = (int *) trimalloc(m->subsegs.items * sizeof(int));
+  }
+  slist = *segmentlist;
+  smlist = *segmentmarkerlist;
+  index = 0;
+#else /* not TRILIBRARY */
+  if (!b->quiet) {
+    printf("Writing %s.\n", polyfilename);
+  }
+  outfile = fopen(polyfilename, "w");
+  if (outfile == (FILE *) NULL) {
+    printf("  Error:  Cannot create file %s.\n", polyfilename);
+    exit(1);
+  }
+  /* The zero indicates that the vertices are in a separate .node file. */
+  /*   Followed by number of dimensions, number of vertex attributes,   */
+  /*   and number of boundary markers (zero or one).                    */
+  fprintf(outfile, "%d  %d  %d  %d\n", 0, m->mesh_dim, m->nextras,
+          1 - b->nobound);
+  /* Number of segments, number of boundary markers (zero or one). */
+  fprintf(outfile, "%ld  %d\n", m->subsegs.items, 1 - b->nobound);
+#endif /* not TRILIBRARY */
+
+  traversalinit(&m->subsegs);
+  subsegloop.ss = subsegtraverse(m);
+  subsegloop.ssorient = 0;
+  subsegnumber = b->firstnumber;
+  while (subsegloop.ss != (subseg *) NULL) {
+    sorg(subsegloop, endpoint1);
+    sdest(subsegloop, endpoint2);
+#ifdef TRILIBRARY
+    /* Copy indices of the segment's two endpoints. */
+    slist[index++] = vertexmark(endpoint1);
+    slist[index++] = vertexmark(endpoint2);
+    if (!b->nobound) {
+      /* Copy the boundary marker. */
+      smlist[subsegnumber - b->firstnumber] = mark(subsegloop);
+    }
+#else /* not TRILIBRARY */
+    /* Segment number, indices of its two endpoints, and possibly a marker. */
+    if (b->nobound) {
+      fprintf(outfile, "%4ld    %4d  %4d\n", subsegnumber,
+              vertexmark(endpoint1), vertexmark(endpoint2));
+    } else {
+      fprintf(outfile, "%4ld    %4d  %4d    %4d\n", subsegnumber,
+              vertexmark(endpoint1), vertexmark(endpoint2), mark(subsegloop));
+    }
+#endif /* not TRILIBRARY */
+
+    subsegloop.ss = subsegtraverse(m);
+    subsegnumber++;
+  }
+
+#ifndef TRILIBRARY
+#ifndef CDT_ONLY
+  fprintf(outfile, "%d\n", holes);
+  if (holes > 0) {
+    for (holenumber = 0; holenumber < holes; holenumber++) {
+      /* Hole number, x and y coordinates. */
+      fprintf(outfile, "%4ld   %.17g  %.17g\n", b->firstnumber + holenumber,
+              holelist[2 * holenumber], holelist[2 * holenumber + 1]);
+    }
+  }
+  if (regions > 0) {
+    fprintf(outfile, "%d\n", regions);
+    for (regionnumber = 0; regionnumber < regions; regionnumber++) {
+      /* Region number, x and y coordinates, attribute, maximum area. */
+      fprintf(outfile, "%4ld   %.17g  %.17g  %.17g  %.17g\n",
+              b->firstnumber + regionnumber,
+              regionlist[4 * regionnumber], regionlist[4 * regionnumber + 1],
+              regionlist[4 * regionnumber + 2],
+              regionlist[4 * regionnumber + 3]);
+    }
+  }
+#endif /* not CDT_ONLY */
+
+  finishfile(outfile, argc, argv);
+#endif /* not TRILIBRARY */
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  writeedges()   Write the edges to an .edge file.                         */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void writeedges(struct mesh *m, struct behavior *b,
+                int **edgelist, int **edgemarkerlist)
+#else /* not ANSI_DECLARATORS */
+void writeedges(m, b, edgelist, edgemarkerlist)
+struct mesh *m;
+struct behavior *b;
+int **edgelist;
+int **edgemarkerlist;
+#endif /* not ANSI_DECLARATORS */
+
+#else /* not TRILIBRARY */
+
+#ifdef ANSI_DECLARATORS
+void writeedges(struct mesh *m, struct behavior *b, char *edgefilename,
+                int argc, char **argv)
+#else /* not ANSI_DECLARATORS */
+void writeedges(m, b, edgefilename, argc, argv)
+struct mesh *m;
+struct behavior *b;
+char *edgefilename;
+int argc;
+char **argv;
+#endif /* not ANSI_DECLARATORS */
+
+#endif /* not TRILIBRARY */
+
+{
+#ifdef TRILIBRARY
+  int *elist;
+  int *emlist;
+  int index;
+#else /* not TRILIBRARY */
+  FILE *outfile;
+#endif /* not TRILIBRARY */
+  struct otri triangleloop, trisym;
+  struct osub checkmark;
+  vertex p1, p2;
+  long edgenumber;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+  subseg sptr;                      /* Temporary variable used by tspivot(). */
+
+#ifdef TRILIBRARY
+  if (!b->quiet) {
+    printf("Writing edges.\n");
+  }
+  /* Allocate memory for edges if necessary. */
+  if (*edgelist == (int *) NULL) {
+    *edgelist = (int *) trimalloc(m->edges * 2 * sizeof(int));
+  }
+  /* Allocate memory for edge markers if necessary. */
+  if (!b->nobound && (*edgemarkerlist == (int *) NULL)) {
+    *edgemarkerlist = (int *) trimalloc(m->edges * sizeof(int));
+  }
+  elist = *edgelist;
+  emlist = *edgemarkerlist;
+  index = 0;
+#else /* not TRILIBRARY */
+  if (!b->quiet) {
+    printf("Writing %s.\n", edgefilename);
+  }
+  outfile = fopen(edgefilename, "w");
+  if (outfile == (FILE *) NULL) {
+    printf("  Error:  Cannot create file %s.\n", edgefilename);
+    exit(1);
+  }
+  /* Number of edges, number of boundary markers (zero or one). */
+  fprintf(outfile, "%ld  %d\n", m->edges, 1 - b->nobound);
+#endif /* not TRILIBRARY */
+
+  traversalinit(&m->triangles);
+  triangleloop.tri = triangletraverse(m);
+  edgenumber = b->firstnumber;
+  /* To loop over the set of edges, loop over all triangles, and look at   */
+  /*   the three edges of each triangle.  If there isn't another triangle  */
+  /*   adjacent to the edge, operate on the edge.  If there is another     */
+  /*   adjacent triangle, operate on the edge only if the current triangle */
+  /*   has a smaller pointer than its neighbor.  This way, each edge is    */
+  /*   considered only once.                                               */
+  while (triangleloop.tri != (triangle *) NULL) {
+    for (triangleloop.orient = 0; triangleloop.orient < 3;
+         triangleloop.orient++) {
+      sym(triangleloop, trisym);
+      if ((triangleloop.tri < trisym.tri) || (trisym.tri == m->dummytri)) {
+        org(triangleloop, p1);
+        dest(triangleloop, p2);
+#ifdef TRILIBRARY
+        elist[index++] = vertexmark(p1);
+        elist[index++] = vertexmark(p2);
+#endif /* TRILIBRARY */
+        if (b->nobound) {
+#ifndef TRILIBRARY
+          /* Edge number, indices of two endpoints. */
+          fprintf(outfile, "%4ld   %d  %d\n", edgenumber,
+                  vertexmark(p1), vertexmark(p2));
+#endif /* not TRILIBRARY */
+        } else {
+          /* Edge number, indices of two endpoints, and a boundary marker. */
+          /*   If there's no subsegment, the boundary marker is zero.      */
+          if (b->usesegments) {
+            tspivot(triangleloop, checkmark);
+            if (checkmark.ss == m->dummysub) {
+#ifdef TRILIBRARY
+              emlist[edgenumber - b->firstnumber] = 0;
+#else /* not TRILIBRARY */
+              fprintf(outfile, "%4ld   %d  %d  %d\n", edgenumber,
+                      vertexmark(p1), vertexmark(p2), 0);
+#endif /* not TRILIBRARY */
+            } else {
+#ifdef TRILIBRARY
+              emlist[edgenumber - b->firstnumber] = mark(checkmark);
+#else /* not TRILIBRARY */
+              fprintf(outfile, "%4ld   %d  %d  %d\n", edgenumber,
+                      vertexmark(p1), vertexmark(p2), mark(checkmark));
+#endif /* not TRILIBRARY */
+            }
+          } else {
+#ifdef TRILIBRARY
+            emlist[edgenumber - b->firstnumber] = trisym.tri == m->dummytri;
+#else /* not TRILIBRARY */
+            fprintf(outfile, "%4ld   %d  %d  %d\n", edgenumber,
+                    vertexmark(p1), vertexmark(p2), trisym.tri == m->dummytri);
+#endif /* not TRILIBRARY */
+          }
+        }
+        edgenumber++;
+      }
+    }
+    triangleloop.tri = triangletraverse(m);
+  }
+
+#ifndef TRILIBRARY
+  finishfile(outfile, argc, argv);
+#endif /* not TRILIBRARY */
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  writevoronoi()   Write the Voronoi diagram to a .v.node and .v.edge      */
+/*                   file.                                                   */
+/*                                                                           */
+/*  The Voronoi diagram is the geometric dual of the Delaunay triangulation. */
+/*  Hence, the Voronoi vertices are listed by traversing the Delaunay        */
+/*  triangles, and the Voronoi edges are listed by traversing the Delaunay   */
+/*  edges.                                                                   */
+/*                                                                           */
+/*  WARNING:  In order to assign numbers to the Voronoi vertices, this       */
+/*  procedure messes up the subsegments or the extra nodes of every          */
+/*  element.  Hence, you should call this procedure last.                    */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void writevoronoi(struct mesh *m, struct behavior *b, REAL **vpointlist,
+                  REAL **vpointattriblist, int **vpointmarkerlist,
+                  int **vedgelist, int **vedgemarkerlist, REAL **vnormlist)
+#else /* not ANSI_DECLARATORS */
+void writevoronoi(m, b, vpointlist, vpointattriblist, vpointmarkerlist,
+                  vedgelist, vedgemarkerlist, vnormlist)
+struct mesh *m;
+struct behavior *b;
+REAL **vpointlist;
+REAL **vpointattriblist;
+int **vpointmarkerlist;
+int **vedgelist;
+int **vedgemarkerlist;
+REAL **vnormlist;
+#endif /* not ANSI_DECLARATORS */
+
+#else /* not TRILIBRARY */
+
+#ifdef ANSI_DECLARATORS
+void writevoronoi(struct mesh *m, struct behavior *b, char *vnodefilename,
+                  char *vedgefilename, int argc, char **argv)
+#else /* not ANSI_DECLARATORS */
+void writevoronoi(m, b, vnodefilename, vedgefilename, argc, argv)
+struct mesh *m;
+struct behavior *b;
+char *vnodefilename;
+char *vedgefilename;
+int argc;
+char **argv;
+#endif /* not ANSI_DECLARATORS */
+
+#endif /* not TRILIBRARY */
+
+{
+#ifdef TRILIBRARY
+  REAL *plist;
+  REAL *palist;
+  int *elist;
+  REAL *normlist;
+  int coordindex;
+  int attribindex;
+#else /* not TRILIBRARY */
+  FILE *outfile;
+#endif /* not TRILIBRARY */
+  struct otri triangleloop, trisym;
+  vertex torg, tdest, tapex;
+  REAL circumcenter[2];
+  REAL xi, eta;
+  REAL dum;
+  long vnodenumber, vedgenumber;
+  int p1, p2;
+  int i;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+
+#ifdef TRILIBRARY
+  if (!b->quiet) {
+    printf("Writing Voronoi vertices.\n");
+  }
+  /* Allocate memory for Voronoi vertices if necessary. */
+  if (*vpointlist == (REAL *) NULL) {
+    *vpointlist = (REAL *) trimalloc(m->triangles.items * 2 * sizeof(REAL));
+  }
+  /* Allocate memory for Voronoi vertex attributes if necessary. */
+  if (*vpointattriblist == (REAL *) NULL) {
+    *vpointattriblist = (REAL *) trimalloc(m->triangles.items * m->nextras *
+                                           sizeof(REAL));
+  }
+  *vpointmarkerlist = (int *) NULL;
+  plist = *vpointlist;
+  palist = *vpointattriblist;
+  coordindex = 0;
+  attribindex = 0;
+#else /* not TRILIBRARY */
+  if (!b->quiet) {
+    printf("Writing %s.\n", vnodefilename);
+  }
+  outfile = fopen(vnodefilename, "w");
+  if (outfile == (FILE *) NULL) {
+    printf("  Error:  Cannot create file %s.\n", vnodefilename);
+    exit(1);
+  }
+  /* Number of triangles, two dimensions, number of vertex attributes, */
+  /*   no markers.                                                     */
+  fprintf(outfile, "%ld  %d  %d  %d\n", m->triangles.items, 2, m->nextras, 0);
+#endif /* not TRILIBRARY */
+
+  traversalinit(&m->triangles);
+  triangleloop.tri = triangletraverse(m);
+  triangleloop.orient = 0;
+  vnodenumber = b->firstnumber;
+  while (triangleloop.tri != (triangle *) NULL) {
+    org(triangleloop, torg);
+    dest(triangleloop, tdest);
+    apex(triangleloop, tapex);
+    findcircumcenter(m, b, torg, tdest, tapex, circumcenter, &xi, &eta, &dum,
+                     0);
+#ifdef TRILIBRARY
+    /* X and y coordinates. */
+    plist[coordindex++] = circumcenter[0];
+    plist[coordindex++] = circumcenter[1];
+    for (i = 2; i < 2 + m->nextras; i++) {
+      /* Interpolate the vertex attributes at the circumcenter. */
+      palist[attribindex++] = torg[i] + xi * (tdest[i] - torg[i])
+                                     + eta * (tapex[i] - torg[i]);
+    }
+#else /* not TRILIBRARY */
+    /* Voronoi vertex number, x and y coordinates. */
+    fprintf(outfile, "%4ld    %.17g  %.17g", vnodenumber, circumcenter[0],
+            circumcenter[1]);
+    for (i = 2; i < 2 + m->nextras; i++) {
+      /* Interpolate the vertex attributes at the circumcenter. */
+      fprintf(outfile, "  %.17g", torg[i] + xi * (tdest[i] - torg[i])
+                                         + eta * (tapex[i] - torg[i]));
+    }
+    fprintf(outfile, "\n");
+#endif /* not TRILIBRARY */
+
+    * (int *) (triangleloop.tri + 6) = (int) vnodenumber;
+    triangleloop.tri = triangletraverse(m);
+    vnodenumber++;
+  }
+
+#ifndef TRILIBRARY
+  finishfile(outfile, argc, argv);
+#endif /* not TRILIBRARY */
+
+#ifdef TRILIBRARY
+  if (!b->quiet) {
+    printf("Writing Voronoi edges.\n");
+  }
+  /* Allocate memory for output Voronoi edges if necessary. */
+  if (*vedgelist == (int *) NULL) {
+    *vedgelist = (int *) trimalloc(m->edges * 2 * sizeof(int));
+  }
+  *vedgemarkerlist = (int *) NULL;
+  /* Allocate memory for output Voronoi norms if necessary. */
+  if (*vnormlist == (REAL *) NULL) {
+    *vnormlist = (REAL *) trimalloc(m->edges * 2 * sizeof(REAL));
+  }
+  elist = *vedgelist;
+  normlist = *vnormlist;
+  coordindex = 0;
+#else /* not TRILIBRARY */
+  if (!b->quiet) {
+    printf("Writing %s.\n", vedgefilename);
+  }
+  outfile = fopen(vedgefilename, "w");
+  if (outfile == (FILE *) NULL) {
+    printf("  Error:  Cannot create file %s.\n", vedgefilename);
+    exit(1);
+  }
+  /* Number of edges, zero boundary markers. */
+  fprintf(outfile, "%ld  %d\n", m->edges, 0);
+#endif /* not TRILIBRARY */
+
+  traversalinit(&m->triangles);
+  triangleloop.tri = triangletraverse(m);
+  vedgenumber = b->firstnumber;
+  /* To loop over the set of edges, loop over all triangles, and look at   */
+  /*   the three edges of each triangle.  If there isn't another triangle  */
+  /*   adjacent to the edge, operate on the edge.  If there is another     */
+  /*   adjacent triangle, operate on the edge only if the current triangle */
+  /*   has a smaller pointer than its neighbor.  This way, each edge is    */
+  /*   considered only once.                                               */
+  while (triangleloop.tri != (triangle *) NULL) {
+    for (triangleloop.orient = 0; triangleloop.orient < 3;
+         triangleloop.orient++) {
+      sym(triangleloop, trisym);
+      if ((triangleloop.tri < trisym.tri) || (trisym.tri == m->dummytri)) {
+        /* Find the number of this triangle (and Voronoi vertex). */
+        p1 = * (int *) (triangleloop.tri + 6);
+        if (trisym.tri == m->dummytri) {
+          org(triangleloop, torg);
+          dest(triangleloop, tdest);
+#ifdef TRILIBRARY
+          /* Copy an infinite ray.  Index of one endpoint, and -1. */
+          elist[coordindex] = p1;
+          normlist[coordindex++] = tdest[1] - torg[1];
+          elist[coordindex] = -1;
+          normlist[coordindex++] = torg[0] - tdest[0];
+#else /* not TRILIBRARY */
+          /* Write an infinite ray.  Edge number, index of one endpoint, -1, */
+          /*   and x and y coordinates of a vector representing the          */
+          /*   direction of the ray.                                         */
+          fprintf(outfile, "%4ld   %d  %d   %.17g  %.17g\n", vedgenumber,
+                  p1, -1, tdest[1] - torg[1], torg[0] - tdest[0]);
+#endif /* not TRILIBRARY */
+        } else {
+          /* Find the number of the adjacent triangle (and Voronoi vertex). */
+          p2 = * (int *) (trisym.tri + 6);
+          /* Finite edge.  Write indices of two endpoints. */
+#ifdef TRILIBRARY
+          elist[coordindex] = p1;
+          normlist[coordindex++] = 0.0;
+          elist[coordindex] = p2;
+          normlist[coordindex++] = 0.0;
+#else /* not TRILIBRARY */
+          fprintf(outfile, "%4ld   %d  %d\n", vedgenumber, p1, p2);
+#endif /* not TRILIBRARY */
+        }
+        vedgenumber++;
+      }
+    }
+    triangleloop.tri = triangletraverse(m);
+  }
+
+#ifndef TRILIBRARY
+  finishfile(outfile, argc, argv);
+#endif /* not TRILIBRARY */
+}
+
+#ifdef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void writeneighbors(struct mesh *m, struct behavior *b, int **neighborlist)
+#else /* not ANSI_DECLARATORS */
+void writeneighbors(m, b, neighborlist)
+struct mesh *m;
+struct behavior *b;
+int **neighborlist;
+#endif /* not ANSI_DECLARATORS */
+
+#else /* not TRILIBRARY */
+
+#ifdef ANSI_DECLARATORS
+void writeneighbors(struct mesh *m, struct behavior *b, char *neighborfilename,
+                    int argc, char **argv)
+#else /* not ANSI_DECLARATORS */
+void writeneighbors(m, b, neighborfilename, argc, argv)
+struct mesh *m;
+struct behavior *b;
+char *neighborfilename;
+int argc;
+char **argv;
+#endif /* not ANSI_DECLARATORS */
+
+#endif /* not TRILIBRARY */
+
+{
+#ifdef TRILIBRARY
+  int *nlist;
+  int index;
+#else /* not TRILIBRARY */
+  FILE *outfile;
+#endif /* not TRILIBRARY */
+  struct otri triangleloop, trisym;
+  long elementnumber;
+  int neighbor1, neighbor2, neighbor3;
+  triangle ptr;                         /* Temporary variable used by sym(). */
+
+#ifdef TRILIBRARY
+  if (!b->quiet) {
+    printf("Writing neighbors.\n");
+  }
+  /* Allocate memory for neighbors if necessary. */
+  if (*neighborlist == (int *) NULL) {
+    *neighborlist = (int *) trimalloc(m->triangles.items * 3 * sizeof(int));
+  }
+  nlist = *neighborlist;
+  index = 0;
+#else /* not TRILIBRARY */
+  if (!b->quiet) {
+    printf("Writing %s.\n", neighborfilename);
+  }
+  outfile = fopen(neighborfilename, "w");
+  if (outfile == (FILE *) NULL) {
+    printf("  Error:  Cannot create file %s.\n", neighborfilename);
+    exit(1);
+  }
+  /* Number of triangles, three neighbors per triangle. */
+  fprintf(outfile, "%ld  %d\n", m->triangles.items, 3);
+#endif /* not TRILIBRARY */
+
+  traversalinit(&m->triangles);
+  triangleloop.tri = triangletraverse(m);
+  triangleloop.orient = 0;
+  elementnumber = b->firstnumber;
+  while (triangleloop.tri != (triangle *) NULL) {
+    * (int *) (triangleloop.tri + 6) = (int) elementnumber;
+    triangleloop.tri = triangletraverse(m);
+    elementnumber++;
+  }
+  * (int *) (m->dummytri + 6) = -1;
+
+  traversalinit(&m->triangles);
+  triangleloop.tri = triangletraverse(m);
+  elementnumber = b->firstnumber;
+  while (triangleloop.tri != (triangle *) NULL) {
+    triangleloop.orient = 1;
+    sym(triangleloop, trisym);
+    neighbor1 = * (int *) (trisym.tri + 6);
+    triangleloop.orient = 2;
+    sym(triangleloop, trisym);
+    neighbor2 = * (int *) (trisym.tri + 6);
+    triangleloop.orient = 0;
+    sym(triangleloop, trisym);
+    neighbor3 = * (int *) (trisym.tri + 6);
+#ifdef TRILIBRARY
+    nlist[index++] = neighbor1;
+    nlist[index++] = neighbor2;
+    nlist[index++] = neighbor3;
+#else /* not TRILIBRARY */
+    /* Triangle number, neighboring triangle numbers. */
+    fprintf(outfile, "%4ld    %d  %d  %d\n", elementnumber,
+            neighbor1, neighbor2, neighbor3);
+#endif /* not TRILIBRARY */
+
+    triangleloop.tri = triangletraverse(m);
+    elementnumber++;
+  }
+
+#ifndef TRILIBRARY
+  finishfile(outfile, argc, argv);
+#endif /* not TRILIBRARY */
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  writeoff()   Write the triangulation to an .off file.                    */
+/*                                                                           */
+/*  OFF stands for the Object File Format, a format used by the Geometry     */
+/*  Center's Geomview package.                                               */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifndef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void writeoff(struct mesh *m, struct behavior *b, char *offfilename,
+              int argc, char **argv)
+#else /* not ANSI_DECLARATORS */
+void writeoff(m, b, offfilename, argc, argv)
+struct mesh *m;
+struct behavior *b;
+char *offfilename;
+int argc;
+char **argv;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  FILE *outfile;
+  struct otri triangleloop;
+  vertex vertexloop;
+  vertex p1, p2, p3;
+  long outvertices;
+
+  if (!b->quiet) {
+    printf("Writing %s.\n", offfilename);
+  }
+
+  if (b->jettison) {
+    outvertices = m->vertices.items - m->undeads;
+  } else {
+    outvertices = m->vertices.items;
+  }
+
+  outfile = fopen(offfilename, "w");
+  if (outfile == (FILE *) NULL) {
+    printf("  Error:  Cannot create file %s.\n", offfilename);
+    exit(1);
+  }
+  /* Number of vertices, triangles, and edges. */
+  fprintf(outfile, "OFF\n%ld  %ld  %ld\n", outvertices, m->triangles.items,
+          m->edges);
+
+  /* Write the vertices. */
+  traversalinit(&m->vertices);
+  vertexloop = vertextraverse(m);
+  while (vertexloop != (vertex) NULL) {
+    if (!b->jettison || (vertextype(vertexloop) != UNDEADVERTEX)) {
+      /* The "0.0" is here because the OFF format uses 3D coordinates. */
+      fprintf(outfile, " %.17g  %.17g  %.17g\n", vertexloop[0], vertexloop[1],
+              0.0);
+    }
+    vertexloop = vertextraverse(m);
+  }
+
+  /* Write the triangles. */
+  traversalinit(&m->triangles);
+  triangleloop.tri = triangletraverse(m);
+  triangleloop.orient = 0;
+  while (triangleloop.tri != (triangle *) NULL) {
+    org(triangleloop, p1);
+    dest(triangleloop, p2);
+    apex(triangleloop, p3);
+    /* The "3" means a three-vertex polygon. */
+    fprintf(outfile, " 3   %4d  %4d  %4d\n", vertexmark(p1) - 1,
+            vertexmark(p2) - 1, vertexmark(p3) - 1);
+    triangleloop.tri = triangletraverse(m);
+  }
+  finishfile(outfile, argc, argv);
+}
+
+#endif /* not TRILIBRARY */
+
+/**                                                                         **/
+/**                                                                         **/
+/********* File I/O routines end here                                *********/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  quality_statistics()   Print statistics about the quality of the mesh.   */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void quality_statistics(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void quality_statistics(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  struct otri triangleloop;
+  vertex p[3];
+  REAL cossquaretable[8];
+  REAL ratiotable[16];
+  REAL dx[3], dy[3];
+  REAL edgelength[3];
+  REAL dotproduct;
+  REAL cossquare;
+  REAL triarea;
+  REAL shortest, longest;
+  REAL trilongest2;
+  REAL smallestarea, biggestarea;
+  REAL triminaltitude2;
+  REAL minaltitude;
+  REAL triaspect2;
+  REAL worstaspect;
+  REAL smallestangle, biggestangle;
+  REAL radconst, degconst;
+  int angletable[18];
+  int aspecttable[16];
+  int aspectindex;
+  int tendegree;
+  int acutebiggest;
+  int i, ii, j, k;
+
+  printf("Mesh quality statistics:\n\n");
+  radconst = PI / 18.0;
+  degconst = 180.0 / PI;
+  for (i = 0; i < 8; i++) {
+    cossquaretable[i] = cos(radconst * (REAL) (i + 1));
+    cossquaretable[i] = cossquaretable[i] * cossquaretable[i];
+  }
+  for (i = 0; i < 18; i++) {
+    angletable[i] = 0;
+  }
+
+  ratiotable[0]  =      1.5;      ratiotable[1]  =     2.0;
+  ratiotable[2]  =      2.5;      ratiotable[3]  =     3.0;
+  ratiotable[4]  =      4.0;      ratiotable[5]  =     6.0;
+  ratiotable[6]  =     10.0;      ratiotable[7]  =    15.0;
+  ratiotable[8]  =     25.0;      ratiotable[9]  =    50.0;
+  ratiotable[10] =    100.0;      ratiotable[11] =   300.0;
+  ratiotable[12] =   1000.0;      ratiotable[13] = 10000.0;
+  ratiotable[14] = 100000.0;      ratiotable[15] =     0.0;
+  for (i = 0; i < 16; i++) {
+    aspecttable[i] = 0;
+  }
+
+  worstaspect = 0.0;
+  minaltitude = m->xmax - m->xmin + m->ymax - m->ymin;
+  minaltitude = minaltitude * minaltitude;
+  shortest = minaltitude;
+  longest = 0.0;
+  smallestarea = minaltitude;
+  biggestarea = 0.0;
+  worstaspect = 0.0;
+  smallestangle = 0.0;
+  biggestangle = 2.0;
+  acutebiggest = 1;
+
+  traversalinit(&m->triangles);
+  triangleloop.tri = triangletraverse(m);
+  triangleloop.orient = 0;
+  while (triangleloop.tri != (triangle *) NULL) {
+    org(triangleloop, p[0]);
+    dest(triangleloop, p[1]);
+    apex(triangleloop, p[2]);
+    trilongest2 = 0.0;
+
+    for (i = 0; i < 3; i++) {
+      j = plus1mod3[i];
+      k = minus1mod3[i];
+      dx[i] = p[j][0] - p[k][0];
+      dy[i] = p[j][1] - p[k][1];
+      edgelength[i] = dx[i] * dx[i] + dy[i] * dy[i];
+      if (edgelength[i] > trilongest2) {
+        trilongest2 = edgelength[i];
+      }
+      if (edgelength[i] > longest) {
+        longest = edgelength[i];
+      }
+      if (edgelength[i] < shortest) {
+        shortest = edgelength[i];
+      }
+    }
+
+    triarea = counterclockwise(m, b, p[0], p[1], p[2]);
+    if (triarea < smallestarea) {
+      smallestarea = triarea;
+    }
+    if (triarea > biggestarea) {
+      biggestarea = triarea;
+    }
+    triminaltitude2 = triarea * triarea / trilongest2;
+    if (triminaltitude2 < minaltitude) {
+      minaltitude = triminaltitude2;
+    }
+    triaspect2 = trilongest2 / triminaltitude2;
+    if (triaspect2 > worstaspect) {
+      worstaspect = triaspect2;
+    }
+    aspectindex = 0;
+    while ((triaspect2 > ratiotable[aspectindex] * ratiotable[aspectindex])
+           && (aspectindex < 15)) {
+      aspectindex++;
+    }
+    aspecttable[aspectindex]++;
+
+    for (i = 0; i < 3; i++) {
+      j = plus1mod3[i];
+      k = minus1mod3[i];
+      dotproduct = dx[j] * dx[k] + dy[j] * dy[k];
+      cossquare = dotproduct * dotproduct / (edgelength[j] * edgelength[k]);
+      tendegree = 8;
+      for (ii = 7; ii >= 0; ii--) {
+        if (cossquare > cossquaretable[ii]) {
+          tendegree = ii;
+        }
+      }
+      if (dotproduct <= 0.0) {
+        angletable[tendegree]++;
+        if (cossquare > smallestangle) {
+          smallestangle = cossquare;
+        }
+        if (acutebiggest && (cossquare < biggestangle)) {
+          biggestangle = cossquare;
+        }
+      } else {
+        angletable[17 - tendegree]++;
+        if (acutebiggest || (cossquare > biggestangle)) {
+          biggestangle = cossquare;
+          acutebiggest = 0;
+        }
+      }
+    }
+    triangleloop.tri = triangletraverse(m);
+  }
+
+  shortest = sqrt(shortest);
+  longest = sqrt(longest);
+  minaltitude = sqrt(minaltitude);
+  worstaspect = sqrt(worstaspect);
+  smallestarea *= 0.5;
+  biggestarea *= 0.5;
+  if (smallestangle >= 1.0) {
+    smallestangle = 0.0;
+  } else {
+    smallestangle = degconst * acos(sqrt(smallestangle));
+  }
+  if (biggestangle >= 1.0) {
+    biggestangle = 180.0;
+  } else {
+    if (acutebiggest) {
+      biggestangle = degconst * acos(sqrt(biggestangle));
+    } else {
+      biggestangle = 180.0 - degconst * acos(sqrt(biggestangle));
+    }
+  }
+
+  printf("  Smallest area: %16.5g   |  Largest area: %16.5g\n",
+         smallestarea, biggestarea);
+  printf("  Shortest edge: %16.5g   |  Longest edge: %16.5g\n",
+         shortest, longest);
+  printf("  Shortest altitude: %12.5g   |  Largest aspect ratio: %8.5g\n\n",
+         minaltitude, worstaspect);
+
+  printf("  Triangle aspect ratio histogram:\n");
+  printf("  1.1547 - %-6.6g    :  %8d    | %6.6g - %-6.6g     :  %8d\n",
+         ratiotable[0], aspecttable[0], ratiotable[7], ratiotable[8],
+         aspecttable[8]);
+  for (i = 1; i < 7; i++) {
+    printf("  %6.6g - %-6.6g    :  %8d    | %6.6g - %-6.6g     :  %8d\n",
+           ratiotable[i - 1], ratiotable[i], aspecttable[i],
+           ratiotable[i + 7], ratiotable[i + 8], aspecttable[i + 8]);
+  }
+  printf("  %6.6g - %-6.6g    :  %8d    | %6.6g -            :  %8d\n",
+         ratiotable[6], ratiotable[7], aspecttable[7], ratiotable[14],
+         aspecttable[15]);
+  printf("  (Aspect ratio is longest edge divided by shortest altitude)\n\n");
+
+  printf("  Smallest angle: %15.5g   |  Largest angle: %15.5g\n\n",
+         smallestangle, biggestangle);
+
+  printf("  Angle histogram:\n");
+  for (i = 0; i < 9; i++) {
+    printf("    %3d - %3d degrees:  %8d    |    %3d - %3d degrees:  %8d\n",
+           i * 10, i * 10 + 10, angletable[i],
+           i * 10 + 90, i * 10 + 100, angletable[i + 9]);
+  }
+  printf("\n");
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  statistics()   Print all sorts of cool facts.                            */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void statistics(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void statistics(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+  printf("\nStatistics:\n\n");
+  printf("  Input vertices: %d\n", m->invertices);
+  if (b->refine) {
+    printf("  Input triangles: %d\n", m->inelements);
+  }
+  if (b->poly) {
+    printf("  Input segments: %d\n", m->insegments);
+    if (!b->refine) {
+      printf("  Input holes: %d\n", m->holes);
+    }
+  }
+
+  printf("\n  Mesh vertices: %ld\n", m->vertices.items - m->undeads);
+  printf("  Mesh triangles: %ld\n", m->triangles.items);
+  printf("  Mesh edges: %ld\n", m->edges);
+  printf("  Mesh exterior boundary edges: %ld\n", m->hullsize);
+  if (b->poly || b->refine) {
+    printf("  Mesh interior boundary edges: %ld\n",
+           m->subsegs.items - m->hullsize);
+    printf("  Mesh subsegments (constrained edges): %ld\n",
+           m->subsegs.items);
+  }
+  printf("\n");
+
+  if (b->verbose) {
+    quality_statistics(m, b);
+    printf("Memory allocation statistics:\n\n");
+    printf("  Maximum number of vertices: %ld\n", m->vertices.maxitems);
+    printf("  Maximum number of triangles: %ld\n", m->triangles.maxitems);
+    if (m->subsegs.maxitems > 0) {
+      printf("  Maximum number of subsegments: %ld\n", m->subsegs.maxitems);
+    }
+    if (m->viri.maxitems > 0) {
+      printf("  Maximum number of viri: %ld\n", m->viri.maxitems);
+    }
+    if (m->badsubsegs.maxitems > 0) {
+      printf("  Maximum number of encroached subsegments: %ld\n",
+             m->badsubsegs.maxitems);
+    }
+    if (m->badtriangles.maxitems > 0) {
+      printf("  Maximum number of bad triangles: %ld\n",
+             m->badtriangles.maxitems);
+    }
+    if (m->flipstackers.maxitems > 0) {
+      printf("  Maximum number of stacked triangle flips: %ld\n",
+             m->flipstackers.maxitems);
+    }
+    if (m->splaynodes.maxitems > 0) {
+      printf("  Maximum number of splay tree nodes: %ld\n",
+             m->splaynodes.maxitems);
+    }
+    printf("  Approximate heap memory use (bytes): %ld\n\n",
+           m->vertices.maxitems * m->vertices.itembytes +
+           m->triangles.maxitems * m->triangles.itembytes +
+           m->subsegs.maxitems * m->subsegs.itembytes +
+           m->viri.maxitems * m->viri.itembytes +
+           m->badsubsegs.maxitems * m->badsubsegs.itembytes +
+           m->badtriangles.maxitems * m->badtriangles.itembytes +
+           m->flipstackers.maxitems * m->flipstackers.itembytes +
+           m->splaynodes.maxitems * m->splaynodes.itembytes);
+
+    printf("Algorithmic statistics:\n\n");
+    if (!b->weighted) {
+      printf("  Number of incircle tests: %ld\n", m->incirclecount);
+    } else {
+      printf("  Number of 3D orientation tests: %ld\n", m->orient3dcount);
+    }
+    printf("  Number of 2D orientation tests: %ld\n", m->counterclockcount);
+    if (m->hyperbolacount > 0) {
+      printf("  Number of right-of-hyperbola tests: %ld\n",
+             m->hyperbolacount);
+    }
+    if (m->circletopcount > 0) {
+      printf("  Number of circle top computations: %ld\n",
+             m->circletopcount);
+    }
+    if (m->circumcentercount > 0) {
+      printf("  Number of triangle circumcenter computations: %ld\n",
+             m->circumcentercount);
+    }
+    printf("\n");
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  main() or triangulate()   Gosh, do everything.                           */
+/*                                                                           */
+/*  The sequence is roughly as follows.  Many of these steps can be skipped, */
+/*  depending on the command line switches.                                  */
+/*                                                                           */
+/*  - Initialize constants and parse the command line.                       */
+/*  - Read the vertices from a file and either                               */
+/*    - triangulate them (no -r), or                                         */
+/*    - read an old mesh from files and reconstruct it (-r).                 */
+/*  - Insert the PSLG segments (-p), and possibly segments on the convex     */
+/*      hull (-c).                                                           */
+/*  - Read the holes (-p), regional attributes (-pA), and regional area      */
+/*      constraints (-pa).  Carve the holes and concavities, and spread the  */
+/*      regional attributes and area constraints.                            */
+/*  - Enforce the constraints on minimum angle (-q) and maximum area (-a).   */
+/*      Also enforce the conforming Delaunay property (-q and -a).           */
+/*  - Compute the number of edges in the resulting mesh.                     */
+/*  - Promote the mesh's linear triangles to higher order elements (-o).     */
+/*  - Write the output files and print the statistics.                       */
+/*  - Check the consistency and Delaunay property of the mesh (-C).          */
+/*                                                                           */
+/*****************************************************************************/
+
+#ifdef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void triangulate(char *triswitches, struct triangulateio *in,
+                 struct triangulateio *out, struct triangulateio *vorout)
+#else /* not ANSI_DECLARATORS */
+void triangulate(triswitches, in, out, vorout)
+char *triswitches;
+struct triangulateio *in;
+struct triangulateio *out;
+struct triangulateio *vorout;
+#endif /* not ANSI_DECLARATORS */
+
+#else /* not TRILIBRARY */
+
+#ifdef ANSI_DECLARATORS
+int main(int argc, char **argv)
+#else /* not ANSI_DECLARATORS */
+int main(argc, argv)
+int argc;
+char **argv;
+#endif /* not ANSI_DECLARATORS */
+
+#endif /* not TRILIBRARY */
+
+{
+  struct mesh m;
+  struct behavior b;
+  REAL *holearray;                                        /* Array of holes. */
+  REAL *regionarray;   /* Array of regional attributes and area constraints. */
+#ifndef TRILIBRARY
+  FILE *polyfile;
+#endif /* not TRILIBRARY */
+#ifndef NO_TIMER
+  /* Variables for timing the performance of Triangle.  The types are */
+  /*   defined in sys/time.h.                                         */
+  struct timeval tv0, tv1, tv2, tv3, tv4, tv5, tv6;
+  struct timezone tz;
+#endif /* not NO_TIMER */
+
+#ifndef NO_TIMER
+  gettimeofday(&tv0, &tz);
+#endif /* not NO_TIMER */
+
+  triangleinit(&m);
+#ifdef TRILIBRARY
+  parsecommandline(1, &triswitches, &b);
+#else /* not TRILIBRARY */
+  parsecommandline(argc, argv, &b);
+#endif /* not TRILIBRARY */
+  m.steinerleft = b.steiner;
+
+#ifdef TRILIBRARY
+  transfernodes(&m, &b, in->pointlist, in->pointattributelist,
+                in->pointmarkerlist, in->numberofpoints,
+                in->numberofpointattributes);
+#else /* not TRILIBRARY */
+  readnodes(&m, &b, b.innodefilename, b.inpolyfilename, &polyfile);
+#endif /* not TRILIBRARY */
+
+#ifndef NO_TIMER
+  if (!b.quiet) {
+    gettimeofday(&tv1, &tz);
+  }
+#endif /* not NO_TIMER */
+
+#ifdef CDT_ONLY
+  m.hullsize = delaunay(&m, &b);                /* Triangulate the vertices. */
+#else /* not CDT_ONLY */
+  if (b.refine) {
+    /* Read and reconstruct a mesh. */
+#ifdef TRILIBRARY
+    m.hullsize = reconstruct(&m, &b, in->trianglelist,
+                             in->triangleattributelist, in->trianglearealist,
+                             in->numberoftriangles, in->numberofcorners,
+                             in->numberoftriangleattributes,
+                             in->segmentlist, in->segmentmarkerlist,
+                             in->numberofsegments);
+#else /* not TRILIBRARY */
+    m.hullsize = reconstruct(&m, &b, b.inelefilename, b.areafilename,
+                             b.inpolyfilename, polyfile);
+#endif /* not TRILIBRARY */
+  } else {
+    m.hullsize = delaunay(&m, &b);              /* Triangulate the vertices. */
+  }
+#endif /* not CDT_ONLY */
+
+#ifndef NO_TIMER
+  if (!b.quiet) {
+    gettimeofday(&tv2, &tz);
+    if (b.refine) {
+      printf("Mesh reconstruction");
+    } else {
+      printf("Delaunay");
+    }
+    printf(" milliseconds:  %ld\n", 1000l * (tv2.tv_sec - tv1.tv_sec) +
+           (tv2.tv_usec - tv1.tv_usec) / 1000l);
+  }
+#endif /* not NO_TIMER */
+
+  /* Ensure that no vertex can be mistaken for a triangular bounding */
+  /*   box vertex in insertvertex().                                 */
+  m.infvertex1 = (vertex) NULL;
+  m.infvertex2 = (vertex) NULL;
+  m.infvertex3 = (vertex) NULL;
+
+  if (b.usesegments) {
+    m.checksegments = 1;                /* Segments will be introduced next. */
+    if (!b.refine) {
+      /* Insert PSLG segments and/or convex hull segments. */
+#ifdef TRILIBRARY
+      formskeleton(&m, &b, in->segmentlist,
+                   in->segmentmarkerlist, in->numberofsegments);
+#else /* not TRILIBRARY */
+      formskeleton(&m, &b, polyfile, b.inpolyfilename);
+#endif /* not TRILIBRARY */
+    }
+  }
+
+#ifndef NO_TIMER
+  if (!b.quiet) {
+    gettimeofday(&tv3, &tz);
+    if (b.usesegments && !b.refine) {
+      printf("Segment milliseconds:  %ld\n",
+             1000l * (tv3.tv_sec - tv2.tv_sec) +
+             (tv3.tv_usec - tv2.tv_usec) / 1000l);
+    }
+  }
+#endif /* not NO_TIMER */
+
+  if (b.poly && (m.triangles.items > 0)) {
+#ifdef TRILIBRARY
+    holearray = in->holelist;
+    m.holes = in->numberofholes;
+    regionarray = in->regionlist;
+    m.regions = in->numberofregions;
+#else /* not TRILIBRARY */
+    readholes(&m, &b, polyfile, b.inpolyfilename, &holearray, &m.holes,
+              &regionarray, &m.regions);
+#endif /* not TRILIBRARY */
+    if (!b.refine) {
+      /* Carve out holes and concavities. */
+      carveholes(&m, &b, holearray, m.holes, regionarray, m.regions);
+    }
+  } else {
+    /* Without a PSLG, there can be no holes or regional attributes   */
+    /*   or area constraints.  The following are set to zero to avoid */
+    /*   an accidental free() later.                                  */
+    m.holes = 0;
+    m.regions = 0;
+  }
+
+#ifndef NO_TIMER
+  if (!b.quiet) {
+    gettimeofday(&tv4, &tz);
+    if (b.poly && !b.refine) {
+      printf("Hole milliseconds:  %ld\n", 1000l * (tv4.tv_sec - tv3.tv_sec) +
+             (tv4.tv_usec - tv3.tv_usec) / 1000l);
+    }
+  }
+#endif /* not NO_TIMER */
+
+#ifndef CDT_ONLY
+  if (b.quality && (m.triangles.items > 0)) {
+    enforcequality(&m, &b);           /* Enforce angle and area constraints. */
+  }
+#endif /* not CDT_ONLY */
+
+#ifndef NO_TIMER
+  if (!b.quiet) {
+    gettimeofday(&tv5, &tz);
+#ifndef CDT_ONLY
+    if (b.quality) {
+      printf("Quality milliseconds:  %ld\n",
+             1000l * (tv5.tv_sec - tv4.tv_sec) +
+             (tv5.tv_usec - tv4.tv_usec) / 1000l);
+    }
+#endif /* not CDT_ONLY */
+  }
+#endif /* not NO_TIMER */
+
+  /* Calculate the number of edges. */
+  m.edges = (3l * m.triangles.items + m.hullsize) / 2l;
+
+  if (b.order > 1) {
+    highorder(&m, &b);       /* Promote elements to higher polynomial order. */
+  }
+  if (!b.quiet) {
+    printf("\n");
+  }
+
+#ifdef TRILIBRARY
+  out->numberofpoints = m.vertices.items;
+  out->numberofpointattributes = m.nextras;
+  out->numberoftriangles = m.triangles.items;
+  out->numberofcorners = (b.order + 1) * (b.order + 2) / 2;
+  out->numberoftriangleattributes = m.eextras;
+  out->numberofedges = m.edges;
+  if (b.usesegments) {
+    out->numberofsegments = m.subsegs.items;
+  } else {
+    out->numberofsegments = m.hullsize;
+  }
+  if (vorout != (struct triangulateio *) NULL) {
+    vorout->numberofpoints = m.triangles.items;
+    vorout->numberofpointattributes = m.nextras;
+    vorout->numberofedges = m.edges;
+  }
+#endif /* TRILIBRARY */
+  /* If not using iteration numbers, don't write a .node file if one was */
+  /*   read, because the original one would be overwritten!              */
+  if (b.nonodewritten || (b.noiterationnum && m.readnodefile)) {
+    if (!b.quiet) {
+#ifdef TRILIBRARY
+      printf("NOT writing vertices.\n");
+#else /* not TRILIBRARY */
+      printf("NOT writing a .node file.\n");
+#endif /* not TRILIBRARY */
+    }
+    numbernodes(&m, &b);         /* We must remember to number the vertices. */
+  } else {
+    /* writenodes() numbers the vertices too. */
+#ifdef TRILIBRARY
+    writenodes(&m, &b, &out->pointlist, &out->pointattributelist,
+               &out->pointmarkerlist);
+#else /* not TRILIBRARY */
+    writenodes(&m, &b, b.outnodefilename, argc, argv);
+#endif /* TRILIBRARY */
+  }
+  if (b.noelewritten) {
+    if (!b.quiet) {
+#ifdef TRILIBRARY
+      printf("NOT writing triangles.\n");
+#else /* not TRILIBRARY */
+      printf("NOT writing an .ele file.\n");
+#endif /* not TRILIBRARY */
+    }
+  } else {
+#ifdef TRILIBRARY
+    writeelements(&m, &b, &out->trianglelist, &out->triangleattributelist);
+#else /* not TRILIBRARY */
+    writeelements(&m, &b, b.outelefilename, argc, argv);
+#endif /* not TRILIBRARY */
+  }
+  /* The -c switch (convex switch) causes a PSLG to be written */
+  /*   even if none was read.                                  */
+  if (b.poly || b.convex) {
+    /* If not using iteration numbers, don't overwrite the .poly file. */
+    if (b.nopolywritten || b.noiterationnum) {
+      if (!b.quiet) {
+#ifdef TRILIBRARY
+        printf("NOT writing segments.\n");
+#else /* not TRILIBRARY */
+        printf("NOT writing a .poly file.\n");
+#endif /* not TRILIBRARY */
+      }
+    } else {
+#ifdef TRILIBRARY
+      writepoly(&m, &b, &out->segmentlist, &out->segmentmarkerlist);
+      out->numberofholes = m.holes;
+      out->numberofregions = m.regions;
+      if (b.poly) {
+        out->holelist = in->holelist;
+        out->regionlist = in->regionlist;
+      } else {
+        out->holelist = (REAL *) NULL;
+        out->regionlist = (REAL *) NULL;
+      }
+#else /* not TRILIBRARY */
+      writepoly(&m, &b, b.outpolyfilename, holearray, m.holes, regionarray,
+                m.regions, argc, argv);
+#endif /* not TRILIBRARY */
+    }
+  }
+#ifndef TRILIBRARY
+#ifndef CDT_ONLY
+  if (m.regions > 0) {
+    trifree((VOID *) regionarray);
+  }
+#endif /* not CDT_ONLY */
+  if (m.holes > 0) {
+    trifree((VOID *) holearray);
+  }
+  if (b.geomview) {
+    writeoff(&m, &b, b.offfilename, argc, argv);
+  }
+#endif /* not TRILIBRARY */
+  if (b.edgesout) {
+#ifdef TRILIBRARY
+    writeedges(&m, &b, &out->edgelist, &out->edgemarkerlist);
+#else /* not TRILIBRARY */
+    writeedges(&m, &b, b.edgefilename, argc, argv);
+#endif /* not TRILIBRARY */
+  }
+  if (b.voronoi) {
+#ifdef TRILIBRARY
+    writevoronoi(&m, &b, &vorout->pointlist, &vorout->pointattributelist,
+                 &vorout->pointmarkerlist, &vorout->edgelist,
+                 &vorout->edgemarkerlist, &vorout->normlist);
+#else /* not TRILIBRARY */
+    writevoronoi(&m, &b, b.vnodefilename, b.vedgefilename, argc, argv);
+#endif /* not TRILIBRARY */
+  }
+  if (b.neighbors) {
+#ifdef TRILIBRARY
+    writeneighbors(&m, &b, &out->neighborlist);
+#else /* not TRILIBRARY */
+    writeneighbors(&m, &b, b.neighborfilename, argc, argv);
+#endif /* not TRILIBRARY */
+  }
+
+  if (!b.quiet) {
+#ifndef NO_TIMER
+    gettimeofday(&tv6, &tz);
+    printf("\nOutput milliseconds:  %ld\n",
+           1000l * (tv6.tv_sec - tv5.tv_sec) +
+           (tv6.tv_usec - tv5.tv_usec) / 1000l);
+    printf("Total running milliseconds:  %ld\n",
+           1000l * (tv6.tv_sec - tv0.tv_sec) +
+           (tv6.tv_usec - tv0.tv_usec) / 1000l);
+#endif /* not NO_TIMER */
+
+    statistics(&m, &b);
+  }
+
+#ifndef REDUCED
+  if (b.docheck) {
+    checkmesh(&m, &b);
+    checkdelaunay(&m, &b);
+  }
+#endif /* not REDUCED */
+
+  triangledeinit(&m, &b);
+#ifndef TRILIBRARY
+  return 0;
+#endif /* not TRILIBRARY */
+}
diff --git a/contrib/Triangle/triangle.h b/contrib/Triangle/triangle.h
new file mode 100644
index 0000000000..5b2c5995af
--- /dev/null
+++ b/contrib/Triangle/triangle.h
@@ -0,0 +1,282 @@
+/*****************************************************************************/
+/*                                                                           */
+/*  (triangle.h)                                                             */
+/*                                                                           */
+/*  Include file for programs that call Triangle.                            */
+/*                                                                           */
+/*  Accompanies Triangle Versions 1.3 and 1.4                                */
+/*  July 19, 1996                                                            */
+/*                                                                           */
+/*  Copyright 1996                                                           */
+/*  Jonathan Richard Shewchuk                                                */
+/*  2360 Woolsey #H                                                          */
+/*  Berkeley, California  94705-1927                                         */
+/*  jrs@cs.berkeley.edu                                                      */
+/*                                                                           */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  How to call Triangle from another program                                */
+/*                                                                           */
+/*                                                                           */
+/*  If you haven't read Triangle's instructions (run "triangle -h" to read   */
+/*  them), you won't understand what follows.                                */
+/*                                                                           */
+/*  Triangle must be compiled into an object file (triangle.o) with the      */
+/*  TRILIBRARY symbol defined (preferably by using the -DTRILIBRARY compiler */
+/*  switch).  The makefile included with Triangle will do this for you if    */
+/*  you run "make trilibrary".  The resulting object file can be called via  */
+/*  the procedure triangulate().                                             */
+/*                                                                           */
+/*  If the size of the object file is important to you, you may wish to      */
+/*  generate a reduced version of triangle.o.  The REDUCED symbol gets rid   */
+/*  of all features that are primarily of research interest.  Specifically,  */
+/*  the -DREDUCED switch eliminates Triangle's -i, -F, -s, and -C switches.  */
+/*  The CDT_ONLY symbol gets rid of all meshing algorithms above and beyond  */
+/*  constrained Delaunay triangulation.  Specifically, the -DCDT_ONLY switch */
+/*  eliminates Triangle's -r, -q, -a, -S, and -s switches.                   */
+/*                                                                           */
+/*  IMPORTANT:  These definitions (TRILIBRARY, REDUCED, CDT_ONLY) must be    */
+/*  made in the makefile or in triangle.c itself.  Putting these definitions */
+/*  in this file will not create the desired effect.                         */
+/*                                                                           */
+/*                                                                           */
+/*  The calling convention for triangulate() follows.                        */
+/*                                                                           */
+/*      void triangulate(triswitches, in, out, vorout)                       */
+/*      char *triswitches;                                                   */
+/*      struct triangulateio *in;                                            */
+/*      struct triangulateio *out;                                           */
+/*      struct triangulateio *vorout;                                        */
+/*                                                                           */
+/*  `triswitches' is a string containing the command line switches you wish  */
+/*  to invoke.  No initial dash is required.  Some suggestions:              */
+/*                                                                           */
+/*  - You'll probably find it convenient to use the `z' switch so that       */
+/*    points (and other items) are numbered from zero.  This simplifies      */
+/*    indexing, because the first item of any type always starts at index    */
+/*    [0] of the corresponding array, whether that item's number is zero or  */
+/*    one.                                                                   */
+/*  - You'll probably want to use the `Q' (quiet) switch in your final code, */
+/*    but you can take advantage of Triangle's printed output (including the */
+/*    `V' switch) while debugging.                                           */
+/*  - If you are not using the `q' or `a' switches, then the output points   */
+/*    will be identical to the input points, except possibly for the         */
+/*    boundary markers.  If you don't need the boundary markers, you should  */
+/*    use the `N' (no nodes output) switch to save memory.  (If you do need  */
+/*    boundary markers, but need to save memory, a good nasty trick is to    */
+/*    set out->pointlist equal to in->pointlist before calling triangulate(),*/
+/*    so that Triangle overwrites the input points with identical copies.)   */
+/*  - The `I' (no iteration numbers) and `g' (.off file output) switches     */
+/*    have no effect when Triangle is compiled with TRILIBRARY defined.      */
+/*                                                                           */
+/*  `in', `out', and `vorout' are descriptions of the input, the output,     */
+/*  and the Voronoi output.  If the `v' (Voronoi output) switch is not used, */
+/*  `vorout' may be NULL.  `in' and `out' may never be NULL.                 */
+/*                                                                           */
+/*  Certain fields of the input and output structures must be initialized,   */
+/*  as described below.                                                      */
+/*                                                                           */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  The `triangulateio' structure.                                           */
+/*                                                                           */
+/*  Used to pass data into and out of the triangulate() procedure.           */
+/*                                                                           */
+/*                                                                           */
+/*  Arrays are used to store points, triangles, markers, and so forth.  In   */
+/*  all cases, the first item in any array is stored starting at index [0].  */
+/*  However, that item is item number `1' unless the `z' switch is used, in  */
+/*  which case it is item number `0'.  Hence, you may find it easier to      */
+/*  index points (and triangles in the neighbor list) if you use the `z'     */
+/*  switch.  Unless, of course, you're calling Triangle from a Fortran       */
+/*  program.                                                                 */
+/*                                                                           */
+/*  Description of fields (except the `numberof' fields, which are obvious): */
+/*                                                                           */
+/*  `pointlist':  An array of point coordinates.  The first point's x        */
+/*    coordinate is at index [0] and its y coordinate at index [1], followed */
+/*    by the coordinates of the remaining points.  Each point occupies two   */
+/*    REALs.                                                                 */
+/*  `pointattributelist':  An array of point attributes.  Each point's       */
+/*    attributes occupy `numberofpointattributes' REALs.                     */
+/*  `pointmarkerlist':  An array of point markers; one int per point.        */
+/*                                                                           */
+/*  `trianglelist':  An array of triangle corners.  The first triangle's     */
+/*    first corner is at index [0], followed by its other two corners in     */
+/*    counterclockwise order, followed by any other nodes if the triangle    */
+/*    represents a nonlinear element.  Each triangle occupies                */
+/*    `numberofcorners' ints.                                                */
+/*  `triangleattributelist':  An array of triangle attributes.  Each         */
+/*    triangle's attributes occupy `numberoftriangleattributes' REALs.       */
+/*  `trianglearealist':  An array of triangle area constraints; one REAL per */
+/*    triangle.  Input only.                                                 */
+/*  `neighborlist':  An array of triangle neighbors; three ints per          */
+/*    triangle.  Output only.                                                */
+/*                                                                           */
+/*  `segmentlist':  An array of segment endpoints.  The first segment's      */
+/*    endpoints are at indices [0] and [1], followed by the remaining        */
+/*    segments.  Two ints per segment.                                       */
+/*  `segmentmarkerlist':  An array of segment markers; one int per segment.  */
+/*                                                                           */
+/*  `holelist':  An array of holes.  The first hole's x and y coordinates    */
+/*    are at indices [0] and [1], followed by the remaining holes.  Two      */
+/*    REALs per hole.  Input only, although the pointer is copied to the     */
+/*    output structure for your convenience.                                 */
+/*                                                                           */
+/*  `regionlist':  An array of regional attributes and area constraints.     */
+/*    The first constraint's x and y coordinates are at indices [0] and [1], */
+/*    followed by the regional attribute and index [2], followed by the      */
+/*    maximum area at index [3], followed by the remaining area constraints. */
+/*    Four REALs per area constraint.  Note that each regional attribute is  */
+/*    used only if you select the `A' switch, and each area constraint is    */
+/*    used only if you select the `a' switch (with no number following), but */
+/*    omitting one of these switches does not change the memory layout.      */
+/*    Input only, although the pointer is copied to the output structure for */
+/*    your convenience.                                                      */
+/*                                                                           */
+/*  `edgelist':  An array of edge endpoints.  The first edge's endpoints are */
+/*    at indices [0] and [1], followed by the remaining edges.  Two ints per */
+/*    edge.  Output only.                                                    */
+/*  `edgemarkerlist':  An array of edge markers; one int per edge.  Output   */
+/*    only.                                                                  */
+/*  `normlist':  An array of normal vectors, used for infinite rays in       */
+/*    Voronoi diagrams.  The first normal vector's x and y magnitudes are    */
+/*    at indices [0] and [1], followed by the remaining vectors.  For each   */
+/*    finite edge in a Voronoi diagram, the normal vector written is the     */
+/*    zero vector.  Two REALs per edge.  Output only.                        */
+/*                                                                           */
+/*                                                                           */
+/*  Any input fields that Triangle will examine must be initialized.         */
+/*  Furthermore, for each output array that Triangle will write to, you      */
+/*  must either provide space by setting the appropriate pointer to point    */
+/*  to the space you want the data written to, or you must initialize the    */
+/*  pointer to NULL, which tells Triangle to allocate space for the results. */
+/*  The latter option is preferable, because Triangle always knows exactly   */
+/*  how much space to allocate.  The former option is provided mainly for    */
+/*  people who need to call Triangle from Fortran code, though it also makes */
+/*  possible some nasty space-saving tricks, like writing the output to the  */
+/*  same arrays as the input.                                                */
+/*                                                                           */
+/*  Triangle will not free() any input or output arrays, including those it  */
+/*  allocates itself; that's up to you.                                      */
+/*                                                                           */
+/*  Here's a guide to help you decide which fields you must initialize       */
+/*  before you call triangulate().                                           */
+/*                                                                           */
+/*  `in':                                                                    */
+/*                                                                           */
+/*    - `pointlist' must always point to a list of points; `numberofpoints'  */
+/*      and `numberofpointattributes' must be properly set.                  */
+/*      `pointmarkerlist' must either be set to NULL (in which case all      */
+/*      markers default to zero), or must point to a list of markers.  If    */
+/*      `numberofpointattributes' is not zero, `pointattributelist' must     */
+/*      point to a list of point attributes.                                 */
+/*    - If the `r' switch is used, `trianglelist' must point to a list of    */
+/*      triangles, and `numberoftriangles', `numberofcorners', and           */
+/*      `numberoftriangleattributes' must be properly set.  If               */
+/*      `numberoftriangleattributes' is not zero, `triangleattributelist'    */
+/*      must point to a list of triangle attributes.  If the `a' switch is   */
+/*      used (with no number following), `trianglearealist' must point to a  */
+/*      list of triangle area constraints.  `neighborlist' may be ignored.   */
+/*    - If the `p' switch is used, `segmentlist' must point to a list of     */
+/*      segments, `numberofsegments' must be properly set, and               */
+/*      `segmentmarkerlist' must either be set to NULL (in which case all    */
+/*      markers default to zero), or must point to a list of markers.        */
+/*    - If the `p' switch is used without the `r' switch, then               */
+/*      `numberofholes' and `numberofregions' must be properly set.  If      */
+/*      `numberofholes' is not zero, `holelist' must point to a list of      */
+/*      holes.  If `numberofregions' is not zero, `regionlist' must point to */
+/*      a list of region constraints.                                        */
+/*    - If the `p' switch is used, `holelist', `numberofholes',              */
+/*      `regionlist', and `numberofregions' is copied to `out'.  (You can    */
+/*      nonetheless get away with not initializing them if the `r' switch is */
+/*      used.)                                                               */
+/*    - `edgelist', `edgemarkerlist', `normlist', and `numberofedges' may be */
+/*      ignored.                                                             */
+/*                                                                           */
+/*  `out':                                                                   */
+/*                                                                           */
+/*    - `pointlist' must be initialized (NULL or pointing to memory) unless  */
+/*      the `N' switch is used.  `pointmarkerlist' must be initialized       */
+/*      unless the `N' or `B' switch is used.  If `N' is not used and        */
+/*      `in->numberofpointattributes' is not zero, `pointattributelist' must */
+/*      be initialized.                                                      */
+/*    - `trianglelist' must be initialized unless the `E' switch is used.    */
+/*      `neighborlist' must be initialized if the `n' switch is used.  If    */
+/*      the `E' switch is not used and (`in->numberofelementattributes' is   */
+/*      not zero or the `A' switch is used), `elementattributelist' must be  */
+/*      initialized.  `trianglearealist' may be ignored.                     */
+/*    - `segmentlist' must be initialized if the `p' or `c' switch is used,  */
+/*      and the `P' switch is not used.  `segmentmarkerlist' must also be    */
+/*      initialized under these circumstances unless the `B' switch is used. */
+/*    - `edgelist' must be initialized if the `e' switch is used.            */
+/*      `edgemarkerlist' must be initialized if the `e' switch is used and   */
+/*      the `B' switch is not.                                               */
+/*    - `holelist', `regionlist', `normlist', and all scalars may be ignored.*/
+/*                                                                           */
+/*  `vorout' (only needed if `v' switch is used):                            */
+/*                                                                           */
+/*    - `pointlist' must be initialized.  If `in->numberofpointattributes'   */
+/*      is not zero, `pointattributelist' must be initialized.               */
+/*      `pointmarkerlist' may be ignored.                                    */
+/*    - `edgelist' and `normlist' must both be initialized.                  */
+/*      `edgemarkerlist' may be ignored.                                     */
+/*    - Everything else may be ignored.                                      */
+/*                                                                           */
+/*  After a call to triangulate(), the valid fields of `out' and `vorout'    */
+/*  will depend, in an obvious way, on the choice of switches used.  Note    */
+/*  that when the `p' switch is used, the pointers `holelist' and            */
+/*  `regionlist' are copied from `in' to `out', but no new space is          */
+/*  allocated; be careful that you don't free() the same array twice.  On    */
+/*  the other hand, Triangle will never copy the `pointlist' pointer (or any */
+/*  others); new space is allocated for `out->pointlist', or if the `N'      */
+/*  switch is used, `out->pointlist' remains uninitialized.                  */
+/*                                                                           */
+/*  All of the meaningful `numberof' fields will be properly set; for        */
+/*  instance, `numberofedges' will represent the number of edges in the      */
+/*  triangulation whether or not the edges were written.  If segments are    */
+/*  not used, `numberofsegments' will indicate the number of boundary edges. */
+/*                                                                           */
+/*****************************************************************************/
+
+struct triangulateio {
+  REAL *pointlist;                                               /* In / out */
+  REAL *pointattributelist;                                      /* In / out */
+  int *pointmarkerlist;                                          /* In / out */
+  int numberofpoints;                                            /* In / out */
+  int numberofpointattributes;                                   /* In / out */
+
+  int *trianglelist;                                             /* In / out */
+  REAL *triangleattributelist;                                   /* In / out */
+  REAL *trianglearealist;                                         /* In only */
+  int *neighborlist;                                             /* Out only */
+  int numberoftriangles;                                         /* In / out */
+  int numberofcorners;                                           /* In / out */
+  int numberoftriangleattributes;                                /* In / out */
+
+  int *segmentlist;                                              /* In / out */
+  int *segmentmarkerlist;                                        /* In / out */
+  int numberofsegments;                                          /* In / out */
+
+  REAL *holelist;                        /* In / pointer to array copied out */
+  int numberofholes;                                      /* In / copied out */
+
+  REAL *regionlist;                      /* In / pointer to array copied out */
+  int numberofregions;                                    /* In / copied out */
+
+  int *edgelist;                                                 /* Out only */
+  int *edgemarkerlist;            /* Not used with Voronoi diagram; out only */
+  REAL *normlist;                /* Used only with Voronoi diagram; out only */
+  int numberofedges;                                             /* Out only */
+};
+
+#ifdef ANSI_DECLARATORS
+void triangulate(char *, struct triangulateio *, struct triangulateio *,
+                 struct triangulateio *);
+#else /* not ANSI_DECLARATORS */
+void triangulate();
+#endif /* not ANSI_DECLARATORS */
-- 
GitLab