diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 51c992b3d65bffe8670033265a1b3517612586fe..e0ce0d971c6af9c3f06333aa7202a5a7adaada58 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -5,4 +5,4 @@ ubuntu16.04:
     - cd build
     - cmake ..
     - make -j 4
-    - ctest --output-on-failure
+    - ctest -j 4 --output-on-failure
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 60ce02d777a9cec74a3cd3e221b1cd4f0b19b58c..632a6624cd08369e8353efe019937b374a8073d4 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,3 +1,7 @@
+3.0.2: improved handling of meshing constraints and entity numbering after
+boolean operations; improved handling of fast coarseness transitions in
+MeshAdapt.
+
 3.0.1 (April 14, 2017): fixed OpenCASCADE plane surfaces with holes.
 
 3.0.0 (April 13, 2017): new constructive solid geometry features and boolean
diff --git a/CREDITS.txt b/CREDITS.txt
index 0f97a60a3a304113a62b36e71fe980bac07b073b..d18eee3cfc7f056f1cdf4359b6c0e0e803280d7f 100644
--- a/CREDITS.txt
+++ b/CREDITS.txt
@@ -42,6 +42,25 @@ specific, written prior permission. The University of California makes no
 representations about the suitability of this software for any purpose. It is
 provided "as is" without express or implied warranty.
 
+The picojson code (Common/picojson.h) is Copyright 2009-2010 Cybozu Labs, Inc.,
+Copyright 2011-2014 Kazuho Oku, All rights reserved. Redistribution and use in
+source and binary forms, with or without modification, are permitted provided
+that the following conditions are met: 1. Redistributions of source code must
+retain the above copyright notice, this list of conditions and the following
+disclaimer. 2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution. THIS
+SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
 The trackball code (Graphics/Trackball.{cpp.h}) is copyright (C) 1993, 1994,
 Silicon Graphics, Inc. ALL RIGHTS RESERVED. Permission to use, copy, modify, and
 distribute this software for any purpose and without fee is hereby granted,
diff --git a/Common/CommandLine.cpp b/Common/CommandLine.cpp
index d2fcb73b3640babdbca5f9b8b67085244b566522..1cb76601e4bb296cc083ae4200f291ea8e9521ef 100644
--- a/Common/CommandLine.cpp
+++ b/Common/CommandLine.cpp
@@ -184,9 +184,9 @@ std::vector<std::pair<std::string, std::string> > GetShortcutsUsage(const std::s
   s.push_back(mp("p",              "Go to post-processing module"));
   s.push_back(mp("q",              "Abort selection in geometry creation mode"));
   s.push_back(mp("s",              "Go to solver module"));
-  s.push_back(mp("x",              "Freeze x coordinate in geometry creation mode"));
-  s.push_back(mp("y",              "Freeze y coordinate in geometry creation mode"));
-  s.push_back(mp("z",              "Freeze z coordinate in geometry creation mode"));
+  s.push_back(mp("x",              "Toogle x coordinate freeze in geometry creation mode"));
+  s.push_back(mp("y",              "Toogle y coordinate freeze in geometry creation mode"));
+  s.push_back(mp("z",              "Toogle z coordinate freeze in geometry creation mode"));
   s.push_back(mp("Shift+a",        "Bring all windows to front"));
   s.push_back(mp("Shift+g",        "Show geometry options"));
   s.push_back(mp("Shift+m",        "Show mesh options"));
@@ -195,6 +195,9 @@ std::vector<std::pair<std::string, std::string> > GetShortcutsUsage(const std::s
   s.push_back(mp("Shift+s",        "Show solver options"));
   s.push_back(mp("Shift+u",        "Show post-processing view plugins"));
   s.push_back(mp("Shift+w",        "Show post-processing view options"));
+  s.push_back(mp("Shift+x",        "Move only along x coordinate in geometry creation mode"));
+  s.push_back(mp("Shift+y",        "Move only along y coordinate in geometry creation mode"));
+  s.push_back(mp("Shift+z",        "Move only along z coordinate in geometry creation mode"));
   s.push_back(mp("Shift+Escape",   "Enable full mouse selection"));
   s.push_back(mp(cc + "d",         "Attach/detach menu"));
   s.push_back(mp(cc + "e",         "Export project"));
diff --git a/Common/Context.h b/Common/Context.h
index 7613c162d64623ab46a610cf27aaabd01db6929e..3d021709dc5d4413aba63f9be5852f8c041399bc 100644
--- a/Common/Context.h
+++ b/Common/Context.h
@@ -75,7 +75,7 @@ struct contextGeometryOptions {
   int autoCoherence, highlightOrphans, clip, useTransform;
   double tolerance, toleranceBoolean, snap[3], transform[3][3], offset[3];
   int occFixDegenerated, occFixSmallEdges, occFixSmallFaces;
-  int occSewFaces, occParallel;
+  int occSewFaces, occParallel, occBooleanPreserveNumbering;
   double occScaling;
   int copyMeshingMethod, exactExtrusion;
   int matchGeomAndMesh;
diff --git a/Common/CreateFile.cpp b/Common/CreateFile.cpp
index 721ba257217b6b62760a396c8b505b3a27b22bd2..f744e81f1ec5484322bf62cc01199dc48c4b51c5 100644
--- a/Common/CreateFile.cpp
+++ b/Common/CreateFile.cpp
@@ -71,6 +71,7 @@ int GetFileFormatFromExtension(const std::string &ext)
   else if(ext == ".pdf")  return FORMAT_PDF;
   else if(ext == ".tex")  return FORMAT_TEX;
   else if(ext == ".svg")  return FORMAT_SVG;
+  else if(ext == ".tikz") return FORMAT_TIKZ;
   else if(ext == ".ppm")  return FORMAT_PPM;
   else if(ext == ".yuv")  return FORMAT_YUV;
   else if(ext == ".brep") return FORMAT_BREP;
@@ -120,12 +121,13 @@ std::string GetDefaultFileName(int format)
   case FORMAT_JPEG: name += ".jpg"; break;
   case FORMAT_MPEG: name += ".mpg"; break;
   case FORMAT_PNG:  name += ".png"; break;
-  case FORMAT_PGF:  name += ".todo"; break;
+  case FORMAT_PGF:  name += ".pgf"; break;
   case FORMAT_PS:   name += ".ps"; break;
   case FORMAT_EPS:  name += ".eps"; break;
   case FORMAT_PDF:  name += ".pdf"; break;
   case FORMAT_TEX:  name += ".tex"; break;
   case FORMAT_SVG:  name += ".svg"; break;
+  case FORMAT_TIKZ: name += ".tikz"; break;
   case FORMAT_PPM:  name += ".ppm"; break;
   case FORMAT_YUV:  name += ".yuv"; break;
   case FORMAT_BREP: name += ".brep"; break;
@@ -437,6 +439,7 @@ void CreateOutputFile(const std::string &fileName, int format,
   case FORMAT_EPS:
   case FORMAT_PDF:
   case FORMAT_SVG:
+  case FORMAT_TIKZ:
     {
       if(!FlGui::available()) break;
 
@@ -460,6 +463,7 @@ void CreateOutputFile(const std::string &fileName, int format,
         (format == FORMAT_PDF) ? GL2PS_PDF :
         (format == FORMAT_PS) ? GL2PS_PS :
         (format == FORMAT_SVG) ? GL2PS_SVG :
+        (format == FORMAT_TIKZ) ? GL2PS_PGF :
         GL2PS_EPS;
       int pssort =
         (CTX::instance()->print.epsQuality == 3) ? GL2PS_NO_SORT :
diff --git a/Common/DefaultOptions.h b/Common/DefaultOptions.h
index 7580d87431db8d595614a6ee8f42fb44124ec722..c4f89781352b3b984ccb3cc48b86cbcb62bd8330 100644
--- a/Common/DefaultOptions.h
+++ b/Common/DefaultOptions.h
@@ -870,6 +870,8 @@ StringXNumber GeometryOptions_Number[] = {
     "Sew faces in STEP, IGES and BRep models" },
   { F|O, "OCCParallel" , opt_geometry_occ_parallel , 0. ,
     "Use multi-threaded OCC boolean operators" },
+  { F|O, "OCCBooleanPreserveNumbering" , opt_geometry_occ_boolean_preserve_numbering , 1. ,
+    "Try to preserve numbering of entities through OCC boolean operations" },
   { F|O, "OCCScaling" , opt_geometry_occ_scaling , 1. ,
     "Scale STEP, IGES and BRep model by given factor" },
   { F,   "OffsetX" , opt_geometry_offset0 , 0. ,
diff --git a/Common/GmshDefines.h b/Common/GmshDefines.h
index e80216beef89d4a488c5d2abaaa5db4c56d5844d..630462e3fd90e1cf2571754bc7be9ec00270d903 100644
--- a/Common/GmshDefines.h
+++ b/Common/GmshDefines.h
@@ -54,6 +54,7 @@
 #define FORMAT_PVTU  45
 #define FORMAT_X3D   46
 #define FORMAT_TOCHNOG 47
+#define FORMAT_TIKZ  48
 
 // Element types
 #define TYPE_PNT     1
diff --git a/Common/Options.cpp b/Common/Options.cpp
index 54ff43da23b3bcb820f19e7bf673c9a4271495ba..e5fe4fe33a7ab6bbcabd51a5bd3397d19feb4c72 100644
--- a/Common/Options.cpp
+++ b/Common/Options.cpp
@@ -4741,6 +4741,13 @@ double opt_geometry_occ_parallel(OPT_ARGS_NUM)
   return CTX::instance()->geom.occParallel;
 }
 
+double opt_geometry_occ_boolean_preserve_numbering(OPT_ARGS_NUM)
+{
+  if(action & GMSH_SET)
+    CTX::instance()->geom.occBooleanPreserveNumbering = (int)val;
+  return CTX::instance()->geom.occBooleanPreserveNumbering;
+}
+
 double opt_geometry_occ_scaling(OPT_ARGS_NUM)
 {
   if(action & GMSH_SET)
diff --git a/Common/Options.h b/Common/Options.h
index f9de4ad20b12302a4d5818d49d71a2781b204733..8a2e7791340e4887a731eca98a3eb4ff64597908 100644
--- a/Common/Options.h
+++ b/Common/Options.h
@@ -387,6 +387,7 @@ double opt_geometry_occ_fix_small_edges(OPT_ARGS_NUM);
 double opt_geometry_occ_fix_small_faces(OPT_ARGS_NUM);
 double opt_geometry_occ_sew_faces(OPT_ARGS_NUM);
 double opt_geometry_occ_parallel(OPT_ARGS_NUM);
+double opt_geometry_occ_boolean_preserve_numbering(OPT_ARGS_NUM);
 double opt_geometry_occ_scaling(OPT_ARGS_NUM);
 double opt_geometry_old_circle(OPT_ARGS_NUM);
 double opt_geometry_old_newreg(OPT_ARGS_NUM);
diff --git a/Common/onelab.h b/Common/onelab.h
index 0a49bb04e8e004b3bfbfc556a9da2c7cd7ae2eac..6ae9071ef09ed4f49f2bdeaea0332fa75d73e46f 100644
--- a/Common/onelab.h
+++ b/Common/onelab.h
@@ -38,6 +38,7 @@
 #include <algorithm>
 #include <sstream>
 #include "GmshSocket.h"
+#include "picojson.h"
 
 namespace onelab{
 
@@ -346,11 +347,63 @@ namespace onelab{
           it != getClients().end(); it++){
         if(it != getClients().begin()) sstream << ", ";
         sstream << "\"" << sanitize(it->first) << "\":"
-                << (it->second ? "true" : "false");
+                << it->second;
       }
       sstream << " }";
       return sstream.str();
     }
+    virtual bool fromJSON(const picojson::value::object& par)
+    {
+      for(picojson::value::object::const_iterator it = par.begin(); it != par.end(); ++it){
+        if(it->first == "version"){
+          if(!it->second.is<std::string>()) return false;
+          if(it->second.get<std::string>() != version()) return false;
+        }
+        else if(it->first == "name"){
+          if(!it->second.is<std::string>()) return false;
+          setName(it->second.get<std::string>());
+        }
+        else if(it->first == "label"){
+          if(!it->second.is<std::string>()) return false;
+          setLabel(it->second.get<std::string>());
+        }
+        else if(it->first == "help"){
+          if(!it->second.is<std::string>()) return false;
+          setHelp(it->second.get<std::string>());
+        }
+        else if(it->first == "changedValue"){
+          if(!it->second.is<double>()) return false;
+          setChangedValue((int)it->second.get<double>());
+        }
+        else if(it->first == "visible"){
+          if(!it->second.is<bool>()) return false;
+          setVisible(it->second.get<bool>());
+        }
+        else if(it->first == "readOnly"){
+          if(!it->second.is<bool>()) return false;
+          setReadOnly(it->second.get<bool>());
+        }
+        else if(it->first == "attributes"){
+          if(!it->second.is<picojson::object>()) return false;
+          const picojson::value::object &obj = it->second.get<picojson::object>();
+          for (picojson::value::object::const_iterator i = obj.begin(); i != obj.end(); ++i) {
+            std::string key(i->first);
+            if(!i->second.is<std::string>()) return false;
+            setAttribute(key, i->second.get<std::string>());
+          }
+        }
+        else if(it->first == "clients"){
+          if(!it->second.is<picojson::object>()) return false;
+          const picojson::value::object &obj = it->second.get<picojson::object>();
+          for (picojson::value::object::const_iterator i = obj.begin(); i != obj.end(); ++i) {
+            std::string client(i->first);
+            if(!i->second.is<double>()) return false;
+            addClient(client, (int)i->second.get<double>());
+          }
+        }
+      }
+      return true;
+    }
   };
 
   class parameterLessThan{
@@ -515,6 +568,55 @@ namespace onelab{
       sstream << " } }";
       return sstream.str();
     }
+    bool fromJSON(const picojson::value::object& par)
+    {
+      if(!parameter::fromJSON(par)) return false;
+      for(picojson::value::object::const_iterator it = par.begin(); it != par.end(); ++it){
+        if(it->first == "values"){
+          if(!it->second.is<picojson::array>()) return false;
+          const picojson::value::array &arr = it->second.get<picojson::array>();
+          _values.resize(arr.size());
+          for(unsigned int i = 0; i < arr.size(); i++){
+            if(!arr[i].is<double>()) return false;
+            _values[i] = arr[i].get<double>();
+          }
+        }
+        else if(it->first == "min"){
+          if(!it->second.is<double>()) return false;
+          setMin(it->second.get<double>());
+        }
+        else if(it->first == "max"){
+          if(!it->second.is<double>()) return false;
+          setMax(it->second.get<double>());
+        }
+        else if(it->first == "step"){
+          if(!it->second.is<double>()) return false;
+          setStep(it->second.get<double>());
+        }
+        else if(it->first == "index"){
+          if(!it->second.is<double>()) return false;
+          setIndex((int)it->second.get<double>());
+        }
+        else if(it->first == "choices"){
+          if(!it->second.is<picojson::array>()) return false;
+          const picojson::value::array &arr = it->second.get<picojson::array>();
+          _choices.resize(arr.size());
+          for(unsigned int i = 0; i < arr.size(); i++){
+            if(!arr[i].is<double>()) return false;
+            _choices[i] = arr[i].get<double>();
+          }
+        }
+        else if(it->first == "valueLabels"){
+          if(!it->second.is<picojson::object>()) return false;
+          const picojson::value::object &obj = it->second.get<picojson::object>();
+          for (picojson::value::object::const_iterator i = obj.begin(); i != obj.end(); ++i) {
+            if(!i->second.is<double>()) return false;
+            _valueLabels[i->second.get<double>()] = i->first;
+          }
+        }
+      }
+      return true;
+    }
   };
 
   // The string class. A string has a mutable "kind", that can be changed at
@@ -610,6 +712,35 @@ namespace onelab{
       sstream << " ] }";
       return sstream.str();
     }
+    bool fromJSON(const picojson::value::object& par)
+    {
+      if(!parameter::fromJSON(par)) return false;
+      for(picojson::value::object::const_iterator it = par.begin(); it != par.end(); ++it){
+        if(it->first == "values"){
+          if(!it->second.is<picojson::array>()) return false;
+          const picojson::value::array &arr = it->second.get<picojson::array>();
+          _values.resize(arr.size());
+          for(unsigned int i = 0; i < arr.size(); i++){
+            if(!arr[i].is<std::string>()) return false;
+            _values[i] = arr[i].get<std::string>();
+          }
+        }
+        else if(it->first == "kind"){
+          if(!it->second.is<std::string>()) return false;
+          setKind(it->second.get<std::string>());
+        }
+        else if(it->first == "choices"){
+          if(!it->second.is<picojson::array>()) return false;
+          const picojson::value::array &arr = it->second.get<picojson::array>();
+          _choices.resize(arr.size());
+          for(unsigned int i = 0; i < arr.size(); i++){
+            if(!arr[i].is<std::string>()) return false;
+            _choices[i] = arr[i].get<std::string>();
+          }
+        }
+      }
+      return true;
+    }
   };
 
   // The parameter space, i.e., the set of parameters stored and handled by the
@@ -841,7 +972,7 @@ namespace onelab{
       }
       return true;
     }
-    void toJSON(std::string &json, const std::string &creator="",
+    bool toJSON(std::string &json, const std::string &creator="",
                 const std::string &client="") const
     {
       time_t now;
@@ -865,6 +996,40 @@ namespace onelab{
 	}
       }
       json += "] }\n}\n";
+      return true;
+    }
+    bool fromJSON(const std::string &json, const std::string &client="")
+    {
+      picojson::value v;
+      std::string err = picojson::parse(v, json);
+      if(err.size()) return false;
+      if(!v.is<picojson::object>()) return false;
+      const picojson::value::object &obj = v.get<picojson::object>();
+      for (picojson::value::object::const_iterator i = obj.begin(); i != obj.end(); ++i) {
+        if(i->first == "onelab"){ // onelab database
+          if(!i->second.is<picojson::object>()) return false;
+          const picojson::value::object &db = i->second.get<picojson::object>();
+          for (picojson::value::object::const_iterator j = db.begin(); j != db.end(); ++j) {
+            if(j->first == "parameters"){
+              if(!j->second.is<picojson::array>()) return false;
+              const picojson::value::array &arr = j->second.get<picojson::array>();
+              for(unsigned int k = 0; k < arr.size(); k++){
+                if(!arr[k].is<picojson::object>()) return false;
+                const picojson::value::object &par = arr[k].get<picojson::object>();
+                picojson::value::object::const_iterator it = par.find("type");
+                if(it == par.end()) return false;
+                if(it->second.to_str() == "number"){
+                  number p; p.fromJSON(par); set(p, client);
+                }
+                else if(it->second.to_str() == "string"){
+                  string p; p.fromJSON(par); set(p, client);
+                }
+              }
+            }
+          }
+        }
+      }
+      return true;
     }
   };
 
@@ -1026,9 +1191,13 @@ namespace onelab{
       if(parameter::fromFile(msg, fp)) return fromChar(msg, client);
       return false;
     }
-    void toJSON(std::string &json, const std::string &client="")
+    bool toJSON(std::string &json, const std::string &client="")
+    {
+      return _parameterSpace.toJSON(json, client);
+    }
+    bool fromJSON(const std::string &json, const std::string &client="")
     {
-      _parameterSpace.toJSON(json, client);
+      return _parameterSpace.fromJSON(json, client);
     }
   };
 
diff --git a/Common/picojson.h b/Common/picojson.h
new file mode 100644
index 0000000000000000000000000000000000000000..f75c399b7e7380bdac4fbb25019e6ce499714f26
--- /dev/null
+++ b/Common/picojson.h
@@ -0,0 +1,1160 @@
+/*
+ * Copyright 2009-2010 Cybozu Labs, Inc.
+ * Copyright 2011-2014 Kazuho Oku
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef picojson_h
+#define picojson_h
+
+#include <algorithm>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <cstddef>
+#include <iostream>
+#include <iterator>
+#include <limits>
+#include <map>
+#include <stdexcept>
+#include <string>
+#include <vector>
+#include <utility>
+
+// for isnan/isinf
+#if __cplusplus >= 201103L
+#include <cmath>
+#else
+extern "C" {
+#ifdef _MSC_VER
+#include <float.h>
+#elif defined(__INTEL_COMPILER)
+#include <mathimf.h>
+#else
+#include <math.h>
+#endif
+}
+#endif
+
+#ifndef PICOJSON_USE_RVALUE_REFERENCE
+#if (defined(__cpp_rvalue_references) && __cpp_rvalue_references >= 200610) || (defined(_MSC_VER) && _MSC_VER >= 1600)
+#define PICOJSON_USE_RVALUE_REFERENCE 1
+#else
+#define PICOJSON_USE_RVALUE_REFERENCE 0
+#endif
+#endif // PICOJSON_USE_RVALUE_REFERENCE
+
+// experimental support for int64_t (see README.mkdn for detail)
+#ifdef PICOJSON_USE_INT64
+#define __STDC_FORMAT_MACROS
+#include <errno.h>
+#include <inttypes.h>
+#endif
+
+// to disable the use of localeconv(3), set PICOJSON_USE_LOCALE to 0
+#ifndef PICOJSON_USE_LOCALE
+#define PICOJSON_USE_LOCALE 1
+#endif
+#if PICOJSON_USE_LOCALE
+extern "C" {
+#include <locale.h>
+}
+#endif
+
+#ifndef PICOJSON_ASSERT
+#define PICOJSON_ASSERT(e)                                                                                                         \
+  do {                                                                                                                             \
+    if (!(e))                                                                                                                      \
+      throw std::runtime_error(#e);                                                                                                \
+  } while (0)
+#endif
+
+#ifdef _MSC_VER
+#define SNPRINTF _snprintf_s
+#pragma warning(push)
+#pragma warning(disable : 4244) // conversion from int to char
+#pragma warning(disable : 4127) // conditional expression is constant
+#pragma warning(disable : 4702) // unreachable code
+#else
+#define SNPRINTF snprintf
+#endif
+
+namespace picojson {
+
+enum {
+  null_type,
+  boolean_type,
+  number_type,
+  string_type,
+  array_type,
+  object_type
+#ifdef PICOJSON_USE_INT64
+  ,
+  int64_type
+#endif
+};
+
+enum { INDENT_WIDTH = 2 };
+
+struct null {};
+
+class value {
+public:
+  typedef std::vector<value> array;
+  typedef std::map<std::string, value> object;
+  union _storage {
+    bool boolean_;
+    double number_;
+#ifdef PICOJSON_USE_INT64
+    int64_t int64_;
+#endif
+    std::string *string_;
+    array *array_;
+    object *object_;
+  };
+
+protected:
+  int type_;
+  _storage u_;
+
+public:
+  value();
+  value(int type, bool);
+  explicit value(bool b);
+#ifdef PICOJSON_USE_INT64
+  explicit value(int64_t i);
+#endif
+  explicit value(double n);
+  explicit value(const std::string &s);
+  explicit value(const array &a);
+  explicit value(const object &o);
+#if PICOJSON_USE_RVALUE_REFERENCE
+  explicit value(std::string &&s);
+  explicit value(array &&a);
+  explicit value(object &&o);
+#endif
+  explicit value(const char *s);
+  value(const char *s, size_t len);
+  ~value();
+  value(const value &x);
+  value &operator=(const value &x);
+#if PICOJSON_USE_RVALUE_REFERENCE
+  value(value &&x) throw();
+  value &operator=(value &&x) throw();
+#endif
+  void swap(value &x) throw();
+  template <typename T> bool is() const;
+  template <typename T> const T &get() const;
+  template <typename T> T &get();
+  template <typename T> void set(const T &);
+#if PICOJSON_USE_RVALUE_REFERENCE
+  template <typename T> void set(T &&);
+#endif
+  bool evaluate_as_boolean() const;
+  const value &get(const size_t idx) const;
+  const value &get(const std::string &key) const;
+  value &get(const size_t idx);
+  value &get(const std::string &key);
+
+  bool contains(const size_t idx) const;
+  bool contains(const std::string &key) const;
+  std::string to_str() const;
+  template <typename Iter> void serialize(Iter os, bool prettify = false) const;
+  std::string serialize(bool prettify = false) const;
+
+private:
+  template <typename T> value(const T *); // intentionally defined to block implicit conversion of pointer to bool
+  template <typename Iter> static void _indent(Iter os, int indent);
+  template <typename Iter> void _serialize(Iter os, int indent) const;
+  std::string _serialize(int indent) const;
+  void clear();
+};
+
+typedef value::array array;
+typedef value::object object;
+
+inline value::value() : type_(null_type), u_() {
+}
+
+inline value::value(int type, bool) : type_(type), u_() {
+  switch (type) {
+#define INIT(p, v)                                                                                                                 \
+  case p##type:                                                                                                                    \
+    u_.p = v;                                                                                                                      \
+    break
+    INIT(boolean_, false);
+    INIT(number_, 0.0);
+#ifdef PICOJSON_USE_INT64
+    INIT(int64_, 0);
+#endif
+    INIT(string_, new std::string());
+    INIT(array_, new array());
+    INIT(object_, new object());
+#undef INIT
+  default:
+    break;
+  }
+}
+
+inline value::value(bool b) : type_(boolean_type), u_() {
+  u_.boolean_ = b;
+}
+
+#ifdef PICOJSON_USE_INT64
+inline value::value(int64_t i) : type_(int64_type), u_() {
+  u_.int64_ = i;
+}
+#endif
+
+inline value::value(double n) : type_(number_type), u_() {
+  if (
+#ifdef _MSC_VER
+      !_finite(n)
+#elif __cplusplus >= 201103L || !(defined(isnan) && defined(isinf))
+      std::isnan(n) || std::isinf(n)
+#else
+      isnan(n) || isinf(n)
+#endif
+          ) {
+    throw std::overflow_error("");
+  }
+  u_.number_ = n;
+}
+
+inline value::value(const std::string &s) : type_(string_type), u_() {
+  u_.string_ = new std::string(s);
+}
+
+inline value::value(const array &a) : type_(array_type), u_() {
+  u_.array_ = new array(a);
+}
+
+inline value::value(const object &o) : type_(object_type), u_() {
+  u_.object_ = new object(o);
+}
+
+#if PICOJSON_USE_RVALUE_REFERENCE
+inline value::value(std::string &&s) : type_(string_type), u_() {
+  u_.string_ = new std::string(std::move(s));
+}
+
+inline value::value(array &&a) : type_(array_type), u_() {
+  u_.array_ = new array(std::move(a));
+}
+
+inline value::value(object &&o) : type_(object_type), u_() {
+  u_.object_ = new object(std::move(o));
+}
+#endif
+
+inline value::value(const char *s) : type_(string_type), u_() {
+  u_.string_ = new std::string(s);
+}
+
+inline value::value(const char *s, size_t len) : type_(string_type), u_() {
+  u_.string_ = new std::string(s, len);
+}
+
+inline void value::clear() {
+  switch (type_) {
+#define DEINIT(p)                                                                                                                  \
+  case p##type:                                                                                                                    \
+    delete u_.p;                                                                                                                   \
+    break
+    DEINIT(string_);
+    DEINIT(array_);
+    DEINIT(object_);
+#undef DEINIT
+  default:
+    break;
+  }
+}
+
+inline value::~value() {
+  clear();
+}
+
+inline value::value(const value &x) : type_(x.type_), u_() {
+  switch (type_) {
+#define INIT(p, v)                                                                                                                 \
+  case p##type:                                                                                                                    \
+    u_.p = v;                                                                                                                      \
+    break
+    INIT(string_, new std::string(*x.u_.string_));
+    INIT(array_, new array(*x.u_.array_));
+    INIT(object_, new object(*x.u_.object_));
+#undef INIT
+  default:
+    u_ = x.u_;
+    break;
+  }
+}
+
+inline value &value::operator=(const value &x) {
+  if (this != &x) {
+    value t(x);
+    swap(t);
+  }
+  return *this;
+}
+
+#if PICOJSON_USE_RVALUE_REFERENCE
+inline value::value(value &&x) throw() : type_(null_type), u_() {
+  swap(x);
+}
+inline value &value::operator=(value &&x) throw() {
+  swap(x);
+  return *this;
+}
+#endif
+inline void value::swap(value &x) throw() {
+  std::swap(type_, x.type_);
+  std::swap(u_, x.u_);
+}
+
+#define IS(ctype, jtype)                                                                                                           \
+  template <> inline bool value::is<ctype>() const {                                                                               \
+    return type_ == jtype##_type;                                                                                                  \
+  }
+IS(null, null)
+IS(bool, boolean)
+#ifdef PICOJSON_USE_INT64
+IS(int64_t, int64)
+#endif
+IS(std::string, string)
+IS(array, array)
+IS(object, object)
+#undef IS
+template <> inline bool value::is<double>() const {
+  return type_ == number_type
+#ifdef PICOJSON_USE_INT64
+         || type_ == int64_type
+#endif
+      ;
+}
+
+#define GET(ctype, var)                                                                                                            \
+  template <> inline const ctype &value::get<ctype>() const {                                                                      \
+    PICOJSON_ASSERT("type mismatch! call is<type>() before get<type>()" && is<ctype>());                                           \
+    return var;                                                                                                                    \
+  }                                                                                                                                \
+  template <> inline ctype &value::get<ctype>() {                                                                                  \
+    PICOJSON_ASSERT("type mismatch! call is<type>() before get<type>()" && is<ctype>());                                           \
+    return var;                                                                                                                    \
+  }
+GET(bool, u_.boolean_)
+GET(std::string, *u_.string_)
+GET(array, *u_.array_)
+GET(object, *u_.object_)
+#ifdef PICOJSON_USE_INT64
+GET(double,
+    (type_ == int64_type && (const_cast<value *>(this)->type_ = number_type, const_cast<value *>(this)->u_.number_ = u_.int64_),
+     u_.number_))
+GET(int64_t, u_.int64_)
+#else
+GET(double, u_.number_)
+#endif
+#undef GET
+
+#define SET(ctype, jtype, setter)                                                                                                  \
+  template <> inline void value::set<ctype>(const ctype &_val) {                                                                   \
+    clear();                                                                                                                       \
+    type_ = jtype##_type;                                                                                                          \
+    setter                                                                                                                         \
+  }
+SET(bool, boolean, u_.boolean_ = _val;)
+SET(std::string, string, u_.string_ = new std::string(_val);)
+SET(array, array, u_.array_ = new array(_val);)
+SET(object, object, u_.object_ = new object(_val);)
+SET(double, number, u_.number_ = _val;)
+#ifdef PICOJSON_USE_INT64
+SET(int64_t, int64, u_.int64_ = _val;)
+#endif
+#undef SET
+
+#if PICOJSON_USE_RVALUE_REFERENCE
+#define MOVESET(ctype, jtype, setter)                                                                                              \
+  template <> inline void value::set<ctype>(ctype && _val) {                                                                       \
+    clear();                                                                                                                       \
+    type_ = jtype##_type;                                                                                                          \
+    setter                                                                                                                         \
+  }
+MOVESET(std::string, string, u_.string_ = new std::string(std::move(_val));)
+MOVESET(array, array, u_.array_ = new array(std::move(_val));)
+MOVESET(object, object, u_.object_ = new object(std::move(_val));)
+#undef MOVESET
+#endif
+
+inline bool value::evaluate_as_boolean() const {
+  switch (type_) {
+  case null_type:
+    return false;
+  case boolean_type:
+    return u_.boolean_;
+  case number_type:
+    return u_.number_ != 0;
+#ifdef PICOJSON_USE_INT64
+  case int64_type:
+    return u_.int64_ != 0;
+#endif
+  case string_type:
+    return !u_.string_->empty();
+  default:
+    return true;
+  }
+}
+
+inline const value &value::get(const size_t idx) const {
+  static value s_null;
+  PICOJSON_ASSERT(is<array>());
+  return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null;
+}
+
+inline value &value::get(const size_t idx) {
+  static value s_null;
+  PICOJSON_ASSERT(is<array>());
+  return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null;
+}
+
+inline const value &value::get(const std::string &key) const {
+  static value s_null;
+  PICOJSON_ASSERT(is<object>());
+  object::const_iterator i = u_.object_->find(key);
+  return i != u_.object_->end() ? i->second : s_null;
+}
+
+inline value &value::get(const std::string &key) {
+  static value s_null;
+  PICOJSON_ASSERT(is<object>());
+  object::iterator i = u_.object_->find(key);
+  return i != u_.object_->end() ? i->second : s_null;
+}
+
+inline bool value::contains(const size_t idx) const {
+  PICOJSON_ASSERT(is<array>());
+  return idx < u_.array_->size();
+}
+
+inline bool value::contains(const std::string &key) const {
+  PICOJSON_ASSERT(is<object>());
+  object::const_iterator i = u_.object_->find(key);
+  return i != u_.object_->end();
+}
+
+inline std::string value::to_str() const {
+  switch (type_) {
+  case null_type:
+    return "null";
+  case boolean_type:
+    return u_.boolean_ ? "true" : "false";
+#ifdef PICOJSON_USE_INT64
+  case int64_type: {
+    char buf[sizeof("-9223372036854775808")];
+    SNPRINTF(buf, sizeof(buf), "%" PRId64, u_.int64_);
+    return buf;
+  }
+#endif
+  case number_type: {
+    char buf[256];
+    double tmp;
+    SNPRINTF(buf, sizeof(buf), fabs(u_.number_) < (1ULL << 53) && modf(u_.number_, &tmp) == 0 ? "%.f" : "%.17g", u_.number_);
+#if PICOJSON_USE_LOCALE
+    char *decimal_point = localeconv()->decimal_point;
+    if (strcmp(decimal_point, ".") != 0) {
+      size_t decimal_point_len = strlen(decimal_point);
+      for (char *p = buf; *p != '\0'; ++p) {
+        if (strncmp(p, decimal_point, decimal_point_len) == 0) {
+          return std::string(buf, p) + "." + (p + decimal_point_len);
+        }
+      }
+    }
+#endif
+    return buf;
+  }
+  case string_type:
+    return *u_.string_;
+  case array_type:
+    return "array";
+  case object_type:
+    return "object";
+  default:
+    PICOJSON_ASSERT(0);
+#ifdef _MSC_VER
+    __assume(0);
+#endif
+  }
+  return std::string();
+}
+
+template <typename Iter> void copy(const std::string &s, Iter oi) {
+  std::copy(s.begin(), s.end(), oi);
+}
+
+template <typename Iter> struct serialize_str_char {
+  Iter oi;
+  void operator()(char c) {
+    switch (c) {
+#define MAP(val, sym)                                                                                                              \
+  case val:                                                                                                                        \
+    copy(sym, oi);                                                                                                                 \
+    break
+      MAP('"', "\\\"");
+      MAP('\\', "\\\\");
+      MAP('/', "\\/");
+      MAP('\b', "\\b");
+      MAP('\f', "\\f");
+      MAP('\n', "\\n");
+      MAP('\r', "\\r");
+      MAP('\t', "\\t");
+#undef MAP
+    default:
+      if (static_cast<unsigned char>(c) < 0x20 || c == 0x7f) {
+        char buf[7];
+        SNPRINTF(buf, sizeof(buf), "\\u%04x", c & 0xff);
+        copy(buf, buf + 6, oi);
+      } else {
+        *oi++ = c;
+      }
+      break;
+    }
+  }
+};
+
+template <typename Iter> void serialize_str(const std::string &s, Iter oi) {
+  *oi++ = '"';
+  serialize_str_char<Iter> process_char = {oi};
+  std::for_each(s.begin(), s.end(), process_char);
+  *oi++ = '"';
+}
+
+template <typename Iter> void value::serialize(Iter oi, bool prettify) const {
+  return _serialize(oi, prettify ? 0 : -1);
+}
+
+inline std::string value::serialize(bool prettify) const {
+  return _serialize(prettify ? 0 : -1);
+}
+
+template <typename Iter> void value::_indent(Iter oi, int indent) {
+  *oi++ = '\n';
+  for (int i = 0; i < indent * INDENT_WIDTH; ++i) {
+    *oi++ = ' ';
+  }
+}
+
+template <typename Iter> void value::_serialize(Iter oi, int indent) const {
+  switch (type_) {
+  case string_type:
+    serialize_str(*u_.string_, oi);
+    break;
+  case array_type: {
+    *oi++ = '[';
+    if (indent != -1) {
+      ++indent;
+    }
+    for (array::const_iterator i = u_.array_->begin(); i != u_.array_->end(); ++i) {
+      if (i != u_.array_->begin()) {
+        *oi++ = ',';
+      }
+      if (indent != -1) {
+        _indent(oi, indent);
+      }
+      i->_serialize(oi, indent);
+    }
+    if (indent != -1) {
+      --indent;
+      if (!u_.array_->empty()) {
+        _indent(oi, indent);
+      }
+    }
+    *oi++ = ']';
+    break;
+  }
+  case object_type: {
+    *oi++ = '{';
+    if (indent != -1) {
+      ++indent;
+    }
+    for (object::const_iterator i = u_.object_->begin(); i != u_.object_->end(); ++i) {
+      if (i != u_.object_->begin()) {
+        *oi++ = ',';
+      }
+      if (indent != -1) {
+        _indent(oi, indent);
+      }
+      serialize_str(i->first, oi);
+      *oi++ = ':';
+      if (indent != -1) {
+        *oi++ = ' ';
+      }
+      i->second._serialize(oi, indent);
+    }
+    if (indent != -1) {
+      --indent;
+      if (!u_.object_->empty()) {
+        _indent(oi, indent);
+      }
+    }
+    *oi++ = '}';
+    break;
+  }
+  default:
+    copy(to_str(), oi);
+    break;
+  }
+  if (indent == 0) {
+    *oi++ = '\n';
+  }
+}
+
+inline std::string value::_serialize(int indent) const {
+  std::string s;
+  _serialize(std::back_inserter(s), indent);
+  return s;
+}
+
+template <typename Iter> class input {
+protected:
+  Iter cur_, end_;
+  bool consumed_;
+  int line_;
+
+public:
+  input(const Iter &first, const Iter &last) : cur_(first), end_(last), consumed_(false), line_(1) {
+  }
+  int getc() {
+    if (consumed_) {
+      if (*cur_ == '\n') {
+        ++line_;
+      }
+      ++cur_;
+    }
+    if (cur_ == end_) {
+      consumed_ = false;
+      return -1;
+    }
+    consumed_ = true;
+    return *cur_ & 0xff;
+  }
+  void ungetc() {
+    consumed_ = false;
+  }
+  Iter cur() const {
+    if (consumed_) {
+      input<Iter> *self = const_cast<input<Iter> *>(this);
+      self->consumed_ = false;
+      ++self->cur_;
+    }
+    return cur_;
+  }
+  int line() const {
+    return line_;
+  }
+  void skip_ws() {
+    while (1) {
+      int ch = getc();
+      if (!(ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')) {
+        ungetc();
+        break;
+      }
+    }
+  }
+  bool expect(const int expected) {
+    skip_ws();
+    if (getc() != expected) {
+      ungetc();
+      return false;
+    }
+    return true;
+  }
+  bool match(const std::string &pattern) {
+    for (std::string::const_iterator pi(pattern.begin()); pi != pattern.end(); ++pi) {
+      if (getc() != *pi) {
+        ungetc();
+        return false;
+      }
+    }
+    return true;
+  }
+};
+
+template <typename Iter> inline int _parse_quadhex(input<Iter> &in) {
+  int uni_ch = 0, hex;
+  for (int i = 0; i < 4; i++) {
+    if ((hex = in.getc()) == -1) {
+      return -1;
+    }
+    if ('0' <= hex && hex <= '9') {
+      hex -= '0';
+    } else if ('A' <= hex && hex <= 'F') {
+      hex -= 'A' - 0xa;
+    } else if ('a' <= hex && hex <= 'f') {
+      hex -= 'a' - 0xa;
+    } else {
+      in.ungetc();
+      return -1;
+    }
+    uni_ch = uni_ch * 16 + hex;
+  }
+  return uni_ch;
+}
+
+template <typename String, typename Iter> inline bool _parse_codepoint(String &out, input<Iter> &in) {
+  int uni_ch;
+  if ((uni_ch = _parse_quadhex(in)) == -1) {
+    return false;
+  }
+  if (0xd800 <= uni_ch && uni_ch <= 0xdfff) {
+    if (0xdc00 <= uni_ch) {
+      // a second 16-bit of a surrogate pair appeared
+      return false;
+    }
+    // first 16-bit of surrogate pair, get the next one
+    if (in.getc() != '\\' || in.getc() != 'u') {
+      in.ungetc();
+      return false;
+    }
+    int second = _parse_quadhex(in);
+    if (!(0xdc00 <= second && second <= 0xdfff)) {
+      return false;
+    }
+    uni_ch = ((uni_ch - 0xd800) << 10) | ((second - 0xdc00) & 0x3ff);
+    uni_ch += 0x10000;
+  }
+  if (uni_ch < 0x80) {
+    out.push_back(static_cast<char>(uni_ch));
+  } else {
+    if (uni_ch < 0x800) {
+      out.push_back(static_cast<char>(0xc0 | (uni_ch >> 6)));
+    } else {
+      if (uni_ch < 0x10000) {
+        out.push_back(static_cast<char>(0xe0 | (uni_ch >> 12)));
+      } else {
+        out.push_back(static_cast<char>(0xf0 | (uni_ch >> 18)));
+        out.push_back(static_cast<char>(0x80 | ((uni_ch >> 12) & 0x3f)));
+      }
+      out.push_back(static_cast<char>(0x80 | ((uni_ch >> 6) & 0x3f)));
+    }
+    out.push_back(static_cast<char>(0x80 | (uni_ch & 0x3f)));
+  }
+  return true;
+}
+
+template <typename String, typename Iter> inline bool _parse_string(String &out, input<Iter> &in) {
+  while (1) {
+    int ch = in.getc();
+    if (ch < ' ') {
+      in.ungetc();
+      return false;
+    } else if (ch == '"') {
+      return true;
+    } else if (ch == '\\') {
+      if ((ch = in.getc()) == -1) {
+        return false;
+      }
+      switch (ch) {
+#define MAP(sym, val)                                                                                                              \
+  case sym:                                                                                                                        \
+    out.push_back(val);                                                                                                            \
+    break
+        MAP('"', '\"');
+        MAP('\\', '\\');
+        MAP('/', '/');
+        MAP('b', '\b');
+        MAP('f', '\f');
+        MAP('n', '\n');
+        MAP('r', '\r');
+        MAP('t', '\t');
+#undef MAP
+      case 'u':
+        if (!_parse_codepoint(out, in)) {
+          return false;
+        }
+        break;
+      default:
+        return false;
+      }
+    } else {
+      out.push_back(static_cast<char>(ch));
+    }
+  }
+  return false;
+}
+
+template <typename Context, typename Iter> inline bool _parse_array(Context &ctx, input<Iter> &in) {
+  if (!ctx.parse_array_start()) {
+    return false;
+  }
+  size_t idx = 0;
+  if (in.expect(']')) {
+    return ctx.parse_array_stop(idx);
+  }
+  do {
+    if (!ctx.parse_array_item(in, idx)) {
+      return false;
+    }
+    idx++;
+  } while (in.expect(','));
+  return in.expect(']') && ctx.parse_array_stop(idx);
+}
+
+template <typename Context, typename Iter> inline bool _parse_object(Context &ctx, input<Iter> &in) {
+  if (!ctx.parse_object_start()) {
+    return false;
+  }
+  if (in.expect('}')) {
+    return true;
+  }
+  do {
+    std::string key;
+    if (!in.expect('"') || !_parse_string(key, in) || !in.expect(':')) {
+      return false;
+    }
+    if (!ctx.parse_object_item(in, key)) {
+      return false;
+    }
+  } while (in.expect(','));
+  return in.expect('}');
+}
+
+template <typename Iter> inline std::string _parse_number(input<Iter> &in) {
+  std::string num_str;
+  while (1) {
+    int ch = in.getc();
+    if (('0' <= ch && ch <= '9') || ch == '+' || ch == '-' || ch == 'e' || ch == 'E') {
+      num_str.push_back(static_cast<char>(ch));
+    } else if (ch == '.') {
+#if PICOJSON_USE_LOCALE
+      num_str += localeconv()->decimal_point;
+#else
+      num_str.push_back('.');
+#endif
+    } else {
+      in.ungetc();
+      break;
+    }
+  }
+  return num_str;
+}
+
+template <typename Context, typename Iter> inline bool _parse(Context &ctx, input<Iter> &in) {
+  in.skip_ws();
+  int ch = in.getc();
+  switch (ch) {
+#define IS(ch, text, op)                                                                                                           \
+  case ch:                                                                                                                         \
+    if (in.match(text) && op) {                                                                                                    \
+      return true;                                                                                                                 \
+    } else {                                                                                                                       \
+      return false;                                                                                                                \
+    }
+    IS('n', "ull", ctx.set_null());
+    IS('f', "alse", ctx.set_bool(false));
+    IS('t', "rue", ctx.set_bool(true));
+#undef IS
+  case '"':
+    return ctx.parse_string(in);
+  case '[':
+    return _parse_array(ctx, in);
+  case '{':
+    return _parse_object(ctx, in);
+  default:
+    if (('0' <= ch && ch <= '9') || ch == '-') {
+      double f;
+      char *endp;
+      in.ungetc();
+      std::string num_str(_parse_number(in));
+      if (num_str.empty()) {
+        return false;
+      }
+#ifdef PICOJSON_USE_INT64
+      {
+        errno = 0;
+        intmax_t ival = strtoimax(num_str.c_str(), &endp, 10);
+        if (errno == 0 && std::numeric_limits<int64_t>::min() <= ival && ival <= std::numeric_limits<int64_t>::max() &&
+            endp == num_str.c_str() + num_str.size()) {
+          ctx.set_int64(ival);
+          return true;
+        }
+      }
+#endif
+      f = strtod(num_str.c_str(), &endp);
+      if (endp == num_str.c_str() + num_str.size()) {
+        ctx.set_number(f);
+        return true;
+      }
+      return false;
+    }
+    break;
+  }
+  in.ungetc();
+  return false;
+}
+
+class deny_parse_context {
+public:
+  bool set_null() {
+    return false;
+  }
+  bool set_bool(bool) {
+    return false;
+  }
+#ifdef PICOJSON_USE_INT64
+  bool set_int64(int64_t) {
+    return false;
+  }
+#endif
+  bool set_number(double) {
+    return false;
+  }
+  template <typename Iter> bool parse_string(input<Iter> &) {
+    return false;
+  }
+  bool parse_array_start() {
+    return false;
+  }
+  template <typename Iter> bool parse_array_item(input<Iter> &, size_t) {
+    return false;
+  }
+  bool parse_array_stop(size_t) {
+    return false;
+  }
+  bool parse_object_start() {
+    return false;
+  }
+  template <typename Iter> bool parse_object_item(input<Iter> &, const std::string &) {
+    return false;
+  }
+};
+
+class default_parse_context {
+protected:
+  value *out_;
+
+public:
+  default_parse_context(value *out) : out_(out) {
+  }
+  bool set_null() {
+    *out_ = value();
+    return true;
+  }
+  bool set_bool(bool b) {
+    *out_ = value(b);
+    return true;
+  }
+#ifdef PICOJSON_USE_INT64
+  bool set_int64(int64_t i) {
+    *out_ = value(i);
+    return true;
+  }
+#endif
+  bool set_number(double f) {
+    *out_ = value(f);
+    return true;
+  }
+  template <typename Iter> bool parse_string(input<Iter> &in) {
+    *out_ = value(string_type, false);
+    return _parse_string(out_->get<std::string>(), in);
+  }
+  bool parse_array_start() {
+    *out_ = value(array_type, false);
+    return true;
+  }
+  template <typename Iter> bool parse_array_item(input<Iter> &in, size_t) {
+    array &a = out_->get<array>();
+    a.push_back(value());
+    default_parse_context ctx(&a.back());
+    return _parse(ctx, in);
+  }
+  bool parse_array_stop(size_t) {
+    return true;
+  }
+  bool parse_object_start() {
+    *out_ = value(object_type, false);
+    return true;
+  }
+  template <typename Iter> bool parse_object_item(input<Iter> &in, const std::string &key) {
+    object &o = out_->get<object>();
+    default_parse_context ctx(&o[key]);
+    return _parse(ctx, in);
+  }
+
+private:
+  default_parse_context(const default_parse_context &);
+  default_parse_context &operator=(const default_parse_context &);
+};
+
+class null_parse_context {
+public:
+  struct dummy_str {
+    void push_back(int) {
+    }
+  };
+
+public:
+  null_parse_context() {
+  }
+  bool set_null() {
+    return true;
+  }
+  bool set_bool(bool) {
+    return true;
+  }
+#ifdef PICOJSON_USE_INT64
+  bool set_int64(int64_t) {
+    return true;
+  }
+#endif
+  bool set_number(double) {
+    return true;
+  }
+  template <typename Iter> bool parse_string(input<Iter> &in) {
+    dummy_str s;
+    return _parse_string(s, in);
+  }
+  bool parse_array_start() {
+    return true;
+  }
+  template <typename Iter> bool parse_array_item(input<Iter> &in, size_t) {
+    return _parse(*this, in);
+  }
+  bool parse_array_stop(size_t) {
+    return true;
+  }
+  bool parse_object_start() {
+    return true;
+  }
+  template <typename Iter> bool parse_object_item(input<Iter> &in, const std::string &) {
+    return _parse(*this, in);
+  }
+
+private:
+  null_parse_context(const null_parse_context &);
+  null_parse_context &operator=(const null_parse_context &);
+};
+
+// obsolete, use the version below
+template <typename Iter> inline std::string parse(value &out, Iter &pos, const Iter &last) {
+  std::string err;
+  pos = parse(out, pos, last, &err);
+  return err;
+}
+
+template <typename Context, typename Iter> inline Iter _parse(Context &ctx, const Iter &first, const Iter &last, std::string *err) {
+  input<Iter> in(first, last);
+  if (!_parse(ctx, in) && err != NULL) {
+    char buf[64];
+    SNPRINTF(buf, sizeof(buf), "syntax error at line %d near: ", in.line());
+    *err = buf;
+    while (1) {
+      int ch = in.getc();
+      if (ch == -1 || ch == '\n') {
+        break;
+      } else if (ch >= ' ') {
+        err->push_back(static_cast<char>(ch));
+      }
+    }
+  }
+  return in.cur();
+}
+
+template <typename Iter> inline Iter parse(value &out, const Iter &first, const Iter &last, std::string *err) {
+  default_parse_context ctx(&out);
+  return _parse(ctx, first, last, err);
+}
+
+inline std::string parse(value &out, const std::string &s) {
+  std::string err;
+  parse(out, s.begin(), s.end(), &err);
+  return err;
+}
+
+inline std::string parse(value &out, std::istream &is) {
+  std::string err;
+  parse(out, std::istreambuf_iterator<char>(is.rdbuf()), std::istreambuf_iterator<char>(), &err);
+  return err;
+}
+
+template <typename T> struct last_error_t { static std::string s; };
+template <typename T> std::string last_error_t<T>::s;
+
+inline void set_last_error(const std::string &s) {
+  last_error_t<bool>::s = s;
+}
+
+inline const std::string &get_last_error() {
+  return last_error_t<bool>::s;
+}
+
+inline bool operator==(const value &x, const value &y) {
+  if (x.is<null>())
+    return y.is<null>();
+#define PICOJSON_CMP(type)                                                                                                         \
+  if (x.is<type>())                                                                                                                \
+  return y.is<type>() && x.get<type>() == y.get<type>()
+  PICOJSON_CMP(bool);
+  PICOJSON_CMP(double);
+  PICOJSON_CMP(std::string);
+  PICOJSON_CMP(array);
+  PICOJSON_CMP(object);
+#undef PICOJSON_CMP
+  PICOJSON_ASSERT(0);
+#ifdef _MSC_VER
+  __assume(0);
+#endif
+  return false;
+}
+
+inline bool operator!=(const value &x, const value &y) {
+  return !(x == y);
+}
+}
+
+#if !PICOJSON_USE_RVALUE_REFERENCE
+namespace std {
+template <> inline void swap(picojson::value &x, picojson::value &y) {
+  x.swap(y);
+}
+}
+#endif
+
+inline std::istream &operator>>(std::istream &is, picojson::value &x) {
+  picojson::set_last_error(std::string());
+  const std::string err(picojson::parse(x, is));
+  if (!err.empty()) {
+    picojson::set_last_error(err);
+    is.setstate(std::ios::failbit);
+  }
+  return is;
+}
+
+inline std::ostream &operator<<(std::ostream &os, const picojson::value &x) {
+  x.serialize(std::ostream_iterator<char>(os));
+  return os;
+}
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#endif
diff --git a/Fltk/FlGui.cpp b/Fltk/FlGui.cpp
index d83e278f299fa0e9c2af1b162fafff4f97538668..728e8b2343da3d21382ed79812c8c1357bac7968 100644
--- a/Fltk/FlGui.cpp
+++ b/Fltk/FlGui.cpp
@@ -646,6 +646,24 @@ int FlGui::testGlobalShortcuts(int event)
     elementaryContext->butt[2]->value(!elementaryContext->butt[2]->value());
     status = 1; // trick: do as if we didn't use it
   }
+  else if(Fl::test_shortcut(FL_SHIFT + 'x')) {
+    elementaryContext->butt[0]->value(0);
+    elementaryContext->butt[1]->value(1);
+    elementaryContext->butt[2]->value(1);
+    status = 1; // trick: do as if we didn't use it
+  }
+  else if(Fl::test_shortcut(FL_SHIFT + 'y')) {
+    elementaryContext->butt[0]->value(1);
+    elementaryContext->butt[1]->value(0);
+    elementaryContext->butt[2]->value(1);
+    status = 1; // trick: do as if we didn't use it
+  }
+  else if(Fl::test_shortcut(FL_SHIFT + 'z')) {
+    elementaryContext->butt[0]->value(1);
+    elementaryContext->butt[1]->value(1);
+    elementaryContext->butt[2]->value(0);
+    status = 1; // trick: do as if we didn't use it
+  }
   else if(Fl::test_shortcut(FL_Escape) ||
           Fl::test_shortcut(FL_META + FL_Escape) ||
           Fl::test_shortcut(FL_SHIFT + FL_Escape) ||
diff --git a/Fltk/contextWindow.cpp b/Fltk/contextWindow.cpp
index 03c43364e4e9354cd89b21850b035359a1327ab3..916ddb7b67965589c8c288f8c0f95e26729b2629 100644
--- a/Fltk/contextWindow.cpp
+++ b/Fltk/contextWindow.cpp
@@ -935,11 +935,11 @@ elementaryContextWindow::elementaryContextWindow(int deltaFontSize)
       value[i]->callback(elementary_snap_cb);
     }
     butt[0] = new Fl_Check_Button(width - 6 * BH, height - WB - BH, 1.2 * BH, BH, "X");
-    butt[0]->tooltip("(x)");
+    butt[0]->tooltip("Toggle (x) or exclusive unselect (Shift+x)");
     butt[1] = new Fl_Check_Button(width - 6 * BH + 1.2 * BH, height - WB - BH, 1.2*BH, BH, "Y");
-    butt[1]->tooltip("(y)");
+    butt[1]->tooltip("Toggle (y) or exclusive unselect (Shift+y)");
     butt[2] = new Fl_Check_Button(width - 6 * BH + 2.4 * BH, height - WB - BH, (6 - 2*1.2) * BH - WB, BH, "Z freeze");
-    butt[2]->tooltip("(z)");
+    butt[2]->tooltip("Toggle (z) or exclusive unselect (Shift+z)");
   }
 
   tab1->show();
diff --git a/Fltk/fileDialogs.cpp b/Fltk/fileDialogs.cpp
index 2dda82b3a82b3ef8741eb2633cb26e11307afd14..b0e7ce1e1c36e328819816fbb2a319dede208f61 100644
--- a/Fltk/fileDialogs.cpp
+++ b/Fltk/fileDialogs.cpp
@@ -688,26 +688,23 @@ static void activate_gl2ps_choices(int format, int quality, Fl_Check_Button *b[5
     b[1]->deactivate();
     b[2]->deactivate();
     b[3]->deactivate();
-    b[4]->deactivate();
     break;
   case 1: // simple sort
   case 3: // unsorted
     b[1]->activate();
-    b[2]->activate();
-    b[3]->deactivate();
-    if(format == FORMAT_PDF || format == FORMAT_SVG)
-      b[4]->deactivate();
+    b[2]->deactivate();
+    if(format == FORMAT_PS || format == FORMAT_EPS)
+      b[3]->activate();
     else
-      b[4]->activate();
+      b[3]->deactivate();
     break;
   case 2: // bsp sort
     b[1]->activate();
     b[2]->activate();
-    b[3]->activate();
-    if(format == FORMAT_PDF || format == FORMAT_SVG)
-      b[4]->deactivate();
+    if(format == FORMAT_PS || format == FORMAT_EPS)
+      b[3]->activate();
     else
-      b[4]->activate();
+      b[3]->deactivate();
     break;
   }
 }
diff --git a/Fltk/graphicWindow.cpp b/Fltk/graphicWindow.cpp
index 7689c0d2ca0cbeb1ae6c411ecc04812bb458aa64..edc64013964b43f0fd61156db2f5a71e0d7c2fe4 100644
--- a/Fltk/graphicWindow.cpp
+++ b/Fltk/graphicWindow.cpp
@@ -335,6 +335,8 @@ static int _save_ppm(const char *name){ return genericBitmapFileDialog
     (name, "PPM Options", FORMAT_PPM); }
 static int _save_svg(const char *name){ return gl2psFileDialog
     (name, "SVG Options", FORMAT_SVG); }
+static int _save_tikz(const char *name){ return gl2psFileDialog
+    (name, "TIKZ Options", FORMAT_TIKZ); }
 static int _save_yuv(const char *name){ return genericBitmapFileDialog
     (name, "YUV Options", FORMAT_YUV); }
 static int _save_view_pos(const char *name){ return posFileDialog(name); }
@@ -389,6 +391,7 @@ static int _save_auto(const char *name)
   case FORMAT_PS   : return _save_ps(name);
   case FORMAT_PPM  : return _save_ppm(name);
   case FORMAT_SVG  : return _save_svg(name);
+  case FORMAT_TIKZ : return _save_tikz(name);
   case FORMAT_YUV  : return _save_yuv(name);
   default :
     CreateOutputFile(name, FORMAT_AUTO);
@@ -455,6 +458,7 @@ static void file_export_cb(Fl_Widget *w, void *data)
     {"Image - PostScript" TT "*.ps", _save_ps},
     {"Image - PPM" TT "*.ppm", _save_ppm},
     {"Image - SVG" TT "*.svg", _save_svg},
+    {"Image - TIKZ" TT "*.tikz", _save_tikz},
     {"Image - YUV" TT "*.yuv", _save_yuv},
 #if defined(HAVE_MPEG_ENCODE)
     {"Movie - MPEG" TT "*.mpg", _save_mpeg},
diff --git a/Fltk/onelabGroup.cpp b/Fltk/onelabGroup.cpp
index bd1bd33898844b353340455d8a6b6ed3bec52f21..d0c25bcbf1da6fe635395092ca7fa15032474164 100644
--- a/Fltk/onelabGroup.cpp
+++ b/Fltk/onelabGroup.cpp
@@ -10,6 +10,8 @@
 #elif defined(HAVE_NO_INTPTR_T)
 typedef unsigned long intptr_t;
 #endif
+#include <fstream>
+#include <streambuf>
 #include <ctype.h>
 #include "GmshMessage.h"
 #include "onelab.h"
@@ -1204,6 +1206,12 @@ void onelabGroup::rebuildTree(bool deleteWidgets)
     for(unsigned int i = 0; i < delStrings.size(); i++)
       free(delStrings[i]);
   }
+
+#if 0 // test
+  std::ifstream t("test.db.json");
+  std::string str((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());
+  onelab::server::instance()->fromJSON(str);
+#endif
 }
 
 void onelabGroup::openTreeItem(const std::string &name)
diff --git a/Geo/GModel.cpp b/Geo/GModel.cpp
index e511c231bd0b19bab066073d614000607bb4e69f..743ab8e73194dddb430d4fa89d5b7172030f4a1c 100644
--- a/Geo/GModel.cpp
+++ b/Geo/GModel.cpp
@@ -571,7 +571,7 @@ bool GModel::getBoundaryTags(const std::vector<std::pair<int, int> > &inDimTags,
     }
   }
 
-  if(combined || recursive){
+  if(combined){
     // compute boundary of the combined shapes
     std::set<int, AbsIntLessThan> c[3];
     for(unsigned int i = 0; i < outDimTags.size(); i++){
diff --git a/Geo/GModelIO_BDF.cpp b/Geo/GModelIO_BDF.cpp
index 0786ce58db228b95b3985416712baeb1c9629e4b..d0689270eb88fa45576af2fa730488141431ca07 100644
--- a/Geo/GModelIO_BDF.cpp
+++ b/Geo/GModelIO_BDF.cpp
@@ -17,9 +17,19 @@
 
 static int getFormatBDF(char *buffer, int &keySize)
 {
+  for(unsigned int i = 0; i < strlen(buffer); i++){
+    if(buffer[i] == ','){ // free fields
+      if(buffer[keySize] == '*'){ // will contine on next line
+        keySize = i;
+        return -1;
+      }
+      else{
+        keySize = i;
+        return 0;
+      }
+    }
+  }
   if(buffer[keySize] == '*'){ keySize++; return 2; } // long fields
-  for(unsigned int i = 0; i < strlen(buffer); i++)
-    if(buffer[i] == ',') return 0; // free fields
   return 1; // small fields;
 }
 
@@ -54,10 +64,11 @@ static int readVertexBDF(FILE *fp, char *buffer, int keySize,
                          int *num, double *x, double *y, double *z)
 {
   char tmp[5][32];
+  int format = getFormatBDF(buffer, keySize);
   int j = keySize;
-
-  switch(getFormatBDF(buffer, keySize)){
+  switch(format){
   case 0: // free field
+  case -1: // free field with continuation
     for(int i = 0; i < 5; i++){
       tmp[i][16] = '\0';
       strncpy(tmp[i], &buffer[j + 1], 16);
@@ -65,6 +76,13 @@ static int readVertexBDF(FILE *fp, char *buffer, int keySize,
       j++;
       while(j < (int)strlen(buffer) && buffer[j] != ',') j++;
     }
+    if(format == -1){ // continued on next line
+      char buffer2[256];
+      if(!fgets(buffer2, sizeof(buffer2), fp)) return 0;
+      j = 0;
+      while(j < (int)strlen(buffer2) && buffer2[j] != ',') j++;
+      strncpy(tmp[4], &buffer2[j + 1], 16);
+    }
     break;
   case 1: // small field
     for(int i = 0; i < 5; i++) tmp[i][8] = '\0';
@@ -105,7 +123,7 @@ static void readLineBDF(char *buffer, int format, std::vector<char*> &fields)
   int cmax = (format == 2) ? 16 : 8; // max char per (center) field
   int nmax = (format == 2) ? 4 : 8; // max num of (center) fields per line
 
-  if(format == 0){ // free fields
+  if(format <= 0){ // free fields
     for(unsigned int i = 0; i < strlen(buffer); i++){
       if(buffer[i] == ',') fields.push_back(&buffer[i + 1]);
     }
diff --git a/Geo/GModelIO_OCC.cpp b/Geo/GModelIO_OCC.cpp
index 822ccf924f49b13f05f3f96c294baa7639e03bd8..e9830c3ab0bbf22d6fc55af95b770e0c031db330 100644
--- a/Geo/GModelIO_OCC.cpp
+++ b/Geo/GModelIO_OCC.cpp
@@ -151,7 +151,8 @@ void OCC_Internals::bind(TopoDS_Vertex vertex, int tag, bool recursive)
 {
   if(vertex.IsNull()) return;
   if(_vertexTag.IsBound(vertex) && _vertexTag.Find(vertex) != tag){
-    Msg::Debug("OpenCASCADE vertex %d is already bound to another tag", tag);
+    Msg::Info("Cannot bind existing OpenCASCADE vertex %d to second tag %d",
+               _vertexTag.Find(vertex), tag);
   }
   else{
     _vertexTag.Bind(vertex, tag);
@@ -166,7 +167,8 @@ void OCC_Internals::bind(TopoDS_Edge edge, int tag, bool recursive)
 {
   if(edge.IsNull()) return;
   if(_edgeTag.IsBound(edge) && _edgeTag.Find(edge) != tag){
-    Msg::Debug("OpenCASCADE edge %d is already bound to another tag", tag);
+    Msg::Info("Cannot bind existing OpenCASCADE edge %d to second tag %d",
+              _edgeTag.Find(edge), tag);
   }
   else{
     _edgeTag.Bind(edge, tag);
@@ -191,7 +193,8 @@ void OCC_Internals::bind(TopoDS_Wire wire, int tag, bool recursive)
 {
   if(wire.IsNull()) return;
   if(_wireTag.IsBound(wire) && _wireTag.Find(wire) != tag){
-    Msg::Debug("OpenCASCADE wire %d is already bound to anthor tag", tag);
+    Msg::Info("Cannot bind existing OpenCASCADE wire %d to second tag %d",
+              _wireTag.Find(wire), tag);
   }
   else{
     _wireTag.Bind(wire, tag);
@@ -215,7 +218,8 @@ void OCC_Internals::bind(TopoDS_Face face, int tag, bool recursive)
 {
   if(face.IsNull()) return;
   if(_faceTag.IsBound(face) && _faceTag.Find(face) != tag){
-    Msg::Debug("OpenCASCADE face %d is already bound to another tag", tag);
+    Msg::Info("Cannot bind existing OpenCASCADE face %d to second tag %d",
+              _faceTag.Find(face), tag);
   }
   else{
     _faceTag.Bind(face, tag);
@@ -247,7 +251,8 @@ void OCC_Internals::bind(TopoDS_Shell shell, int tag, bool recursive)
 {
   if(shell.IsNull()) return;
   if(_shellTag.IsBound(shell) && _shellTag.Find(shell) != tag){
-    Msg::Debug("OpenCASCADE shell %d is already bound to another tag", tag);
+    Msg::Info("Cannot bind existing OpenCASCADE shell %d to second tag %d",
+              _shellTag.Find(shell), tag);
   }
   else{
     _shellTag.Bind(shell, tag);
@@ -271,7 +276,8 @@ void OCC_Internals::bind(TopoDS_Solid solid, int tag, bool recursive)
 {
   if(solid.IsNull()) return;
   if(_solidTag.IsBound(solid) && _solidTag.Find(solid) != tag){
-    Msg::Debug("OpenCASCADE solid %d is already bound to another tag", tag);
+    Msg::Info("Cannot bind existing OpenCASCADE solid %d to second tag %d",
+              _solidTag.Find(solid), tag);
   }
   else{
     _solidTag.Bind(solid, tag);
@@ -322,9 +328,11 @@ void OCC_Internals::unbind(TopoDS_Vertex vertex, int tag, bool recursive)
       if(exp1.Current().IsSame(vertex)) return;
     }
   }
+  std::pair<int, int> dimTag(0, tag);
+  if(_toPreserve.find(dimTag) != _toPreserve.end()) return;
   _vertexTag.UnBind(vertex);
   _tagVertex.UnBind(tag);
-  _toRemove.insert(std::pair<int, int>(0, tag));
+  _toRemove.insert(dimTag);
   _recomputeMaxTag(0);
   _changed = true;
 }
@@ -339,9 +347,11 @@ void OCC_Internals::unbind(TopoDS_Edge edge, int tag, bool recursive)
       if(exp1.Current().IsSame(edge)) return;
     }
   }
+  std::pair<int, int> dimTag(1, tag);
+  if(_toPreserve.find(dimTag) != _toPreserve.end()) return;
   _edgeTag.UnBind(edge);
   _tagEdge.UnBind(tag);
-  _toRemove.insert(std::pair<int, int>(1, tag));
+  _toRemove.insert(dimTag);
   _recomputeMaxTag(1);
   if(recursive){
     TopExp_Explorer exp0;
@@ -366,9 +376,11 @@ void OCC_Internals::unbind(TopoDS_Wire wire, int tag, bool recursive)
       if(exp1.Current().IsSame(wire)) return;
     }
   }
+  std::pair<int, int> dimTag(-1, tag);
+  if(_toPreserve.find(dimTag) != _toPreserve.end()) return;
   _wireTag.UnBind(wire);
   _tagWire.UnBind(tag);
-  _toRemove.insert(std::pair<int, int>(-1, tag));
+  _toRemove.insert(dimTag);
   _recomputeMaxTag(-1);
   if(recursive){
     TopExp_Explorer exp0;
@@ -393,9 +405,11 @@ void OCC_Internals::unbind(TopoDS_Face face, int tag, bool recursive)
       if(exp1.Current().IsSame(face)) return;
     }
   }
+  std::pair<int, int> dimTag(2, tag);
+  if(_toPreserve.find(dimTag) != _toPreserve.end()) return;
   _faceTag.UnBind(face);
   _tagFace.UnBind(tag);
-  _toRemove.insert(std::pair<int, int>(2, tag));
+  _toRemove.insert(dimTag);
   _recomputeMaxTag(2);
   if(recursive){
     TopExp_Explorer exp0;
@@ -427,9 +441,11 @@ void OCC_Internals::unbind(TopoDS_Shell shell, int tag, bool recursive)
       if(exp1.Current().IsSame(shell)) return;
     }
   }
+  std::pair<int, int> dimTag(-2, tag);
+  if(_toPreserve.find(dimTag) != _toPreserve.end()) return;
   _shellTag.UnBind(shell);
   _tagShell.UnBind(tag);
-  _toRemove.insert(std::pair<int, int>(-2, tag));
+  _toRemove.insert(dimTag);
   _recomputeMaxTag(-2);
   if(recursive){
     TopExp_Explorer exp0;
@@ -446,9 +462,11 @@ void OCC_Internals::unbind(TopoDS_Shell shell, int tag, bool recursive)
 
 void OCC_Internals::unbind(TopoDS_Solid solid, int tag, bool recursive)
 {
+  std::pair<int, int> dimTag(3, tag);
+  if(_toPreserve.find(dimTag) != _toPreserve.end()) return;
   _solidTag.UnBind(solid);
   _tagSolid.UnBind(tag);
-  _toRemove.insert(std::pair<int, int>(3, tag));
+  _toRemove.insert(dimTag);
   _recomputeMaxTag(3);
   if(recursive){
     TopExp_Explorer exp0;
@@ -485,7 +503,7 @@ void OCC_Internals::unbind(TopoDS_Shape shape, int dim, int tag, bool recursive)
 
 void OCC_Internals::_multiBind(TopoDS_Shape shape, int tag,
                                std::vector<std::pair<int, int> > &outDimTags,
-                               bool returnHighestDimOnly, bool recursive,
+                               bool highestDimOnly, bool recursive,
                                bool returnNewOnly)
 {
   TopExp_Explorer exp0;
@@ -512,7 +530,7 @@ void OCC_Internals::_multiBind(TopoDS_Shape shape, int tag,
       outDimTags.push_back(std::pair<int, int>(3, t));
     count++;
   }
-  if(returnHighestDimOnly && count) return;
+  if(highestDimOnly && count) return;
   for(exp0.Init(shape, TopAbs_FACE); exp0.More(); exp0.Next()){
     TopoDS_Face face = TopoDS::Face(exp0.Current());
     bool exists = false;
@@ -535,7 +553,7 @@ void OCC_Internals::_multiBind(TopoDS_Shape shape, int tag,
       outDimTags.push_back(std::pair<int, int>(2, t));
     count++;
   }
-  if(returnHighestDimOnly && count) return;
+  if(highestDimOnly && count) return;
   for(exp0.Init(shape, TopAbs_EDGE); exp0.More(); exp0.Next()){
     TopoDS_Edge edge = TopoDS::Edge(exp0.Current());
     bool exists = false;
@@ -558,7 +576,7 @@ void OCC_Internals::_multiBind(TopoDS_Shape shape, int tag,
       outDimTags.push_back(std::pair<int, int>(1, t));
     count++;
   }
-  if(returnHighestDimOnly && count) return;
+  if(highestDimOnly && count) return;
   for(exp0.Init(shape, TopAbs_VERTEX); exp0.More(); exp0.Next()){
     TopoDS_Vertex vertex = TopoDS::Vertex(exp0.Current());
     bool exists = false;
@@ -566,7 +584,7 @@ void OCC_Internals::_multiBind(TopoDS_Shape shape, int tag,
     if(t <= 0){
       if(_vertexTag.IsBound(vertex)){
         t = _vertexTag.Find(vertex);
-        exists = false;
+        exists = true;
       }
       t = getMaxTag(0) + 1;
     }
@@ -1994,7 +2012,7 @@ bool OCC_Internals::fillet(const std::vector<int> &regionTags,
     }
     TopoDS_Shape shape = _find(3, regionTags[i]);
     b.Add(c, shape);
-    if(removeRegion) unbind(shape, 3, regionTags[i], true); // recursive
+    if(removeRegion) unbind(shape, 3, regionTags[i], true);
   }
   TopoDS_Shape result;
   try{
@@ -2023,14 +2041,26 @@ bool OCC_Internals::fillet(const std::vector<int> &regionTags,
   return true;
 }
 
+static void _filterTags(std::vector<std::pair<int, int> > &outDimTags, int minDim)
+{
+  std::vector<std::pair<int, int> > tmp(outDimTags);
+  outDimTags.clear();
+  for(unsigned int i = 0; i < tmp.size(); i++){
+    if(tmp[i].first >= minDim)
+      outDimTags.push_back(tmp[i]);
+  }
+}
+
 bool OCC_Internals::booleanOperator(int tag, BooleanOperator op,
                                     const std::vector<std::pair<int, int> > &objectDimTags,
                                     const std::vector<std::pair<int, int> > &toolDimTags,
                                     std::vector<std::pair<int, int> > &outDimTags,
+                                    std::vector<std::vector<std::pair<int, int> > > &outDimTagsMap,
                                     bool removeObject, bool removeTool)
 {
   double tolerance = CTX::instance()->geom.toleranceBoolean;
   bool parallel = CTX::instance()->geom.occParallel;
+  bool preserveNumbering = CTX::instance()->geom.occBooleanPreserveNumbering;
 
   if(objectDimTags.empty()) return true;
 
@@ -2039,6 +2069,7 @@ bool OCC_Internals::booleanOperator(int tag, BooleanOperator op,
     return false;
   }
 
+  int minDim = 3;
   TopTools_ListOfShape objectShapes, toolShapes;
   for(unsigned int i = 0; i < objectDimTags.size(); i++){
     int dim = objectDimTags[i].first;
@@ -2051,6 +2082,7 @@ bool OCC_Internals::booleanOperator(int tag, BooleanOperator op,
       TopoDS_Shape object = _find(dim, t);
       objectShapes.Append(object);
     }
+    minDim = std::min(minDim, dim);
   }
   for(unsigned int i = 0; i < toolDimTags.size(); i++){
     int dim = toolDimTags[i].first;
@@ -2063,9 +2095,11 @@ bool OCC_Internals::booleanOperator(int tag, BooleanOperator op,
       TopoDS_Shape tool = _find(dim, t);
       toolShapes.Append(tool);
     }
+    minDim = std::min(minDim, dim);
   }
 
   TopoDS_Shape result;
+  std::vector<TopoDS_Shape> mapOriginal;
   std::vector<TopTools_ListOfShape> mapModified, mapGenerated;
   std::vector<bool> mapDeleted;
   try{
@@ -2086,12 +2120,14 @@ bool OCC_Internals::booleanOperator(int tag, BooleanOperator op,
         result = fuse.Shape();
         TopTools_ListIteratorOfListOfShape it(objectShapes);
         for(; it.More(); it.Next()){
+          mapOriginal.push_back(it.Value());
           mapModified.push_back(fuse.Modified(it.Value()));
           mapDeleted.push_back(fuse.IsDeleted(it.Value()));
           mapGenerated.push_back(fuse.Generated(it.Value()));
         }
         TopTools_ListIteratorOfListOfShape it2(toolShapes);
         for(; it2.More(); it2.Next()){
+          mapOriginal.push_back(it2.Value());
           mapModified.push_back(fuse.Modified(it2.Value()));
           mapDeleted.push_back(fuse.IsDeleted(it2.Value()));
           mapGenerated.push_back(fuse.Generated(it2.Value()));
@@ -2114,12 +2150,14 @@ bool OCC_Internals::booleanOperator(int tag, BooleanOperator op,
         result = common.Shape();
         TopTools_ListIteratorOfListOfShape it(objectShapes);
         for(; it.More(); it.Next()){
+          mapOriginal.push_back(it.Value());
           mapModified.push_back(common.Modified(it.Value()));
           mapDeleted.push_back(common.IsDeleted(it.Value()));
           mapGenerated.push_back(common.Generated(it.Value()));
         }
         TopTools_ListIteratorOfListOfShape it2(toolShapes);
         for(; it2.More(); it2.Next()){
+          mapOriginal.push_back(it2.Value());
           mapModified.push_back(common.Modified(it2.Value()));
           mapDeleted.push_back(common.IsDeleted(it2.Value()));
           mapGenerated.push_back(common.Generated(it2.Value()));
@@ -2143,12 +2181,14 @@ bool OCC_Internals::booleanOperator(int tag, BooleanOperator op,
         result = cut.Shape();
         TopTools_ListIteratorOfListOfShape it(objectShapes);
         for(; it.More(); it.Next()){
+          mapOriginal.push_back(it.Value());
           mapModified.push_back(cut.Modified(it.Value()));
           mapDeleted.push_back(cut.IsDeleted(it.Value()));
           mapGenerated.push_back(cut.Generated(it.Value()));
         }
         TopTools_ListIteratorOfListOfShape it2(toolShapes);
         for(; it2.More(); it2.Next()){
+          mapOriginal.push_back(it2.Value());
           mapModified.push_back(cut.Modified(it2.Value()));
           mapDeleted.push_back(cut.IsDeleted(it2.Value()));
           mapGenerated.push_back(cut.Generated(it2.Value()));
@@ -2174,6 +2214,7 @@ bool OCC_Internals::booleanOperator(int tag, BooleanOperator op,
         result = fragments.Shape();
         TopTools_ListIteratorOfListOfShape it(objectShapes);
         for(; it.More(); it.Next()){
+          mapOriginal.push_back(it.Value());
           mapModified.push_back(fragments.Modified(it.Value()));
           mapDeleted.push_back(fragments.IsDeleted(it.Value()));
           mapGenerated.push_back(fragments.Generated(it.Value()));
@@ -2187,135 +2228,101 @@ bool OCC_Internals::booleanOperator(int tag, BooleanOperator op,
     return false;
   }
 
-  // don't try to preserve numbering if we specify the tag explicitly, or if
-  // there is a problem
-  bool bug1 = (objectDimTags.size() + toolDimTags.size() != mapModified.size());
-  bool bug2 = (op == OCC_Internals::Union); // steange fuse behavior in OCC 7.1
-  if(tag >= 0 || bug1 || bug2){
-    if(bug1) Msg::Error("Wrong shape count in boolean operation");
-    if(removeObject){
-      for(unsigned int i = 0; i < objectDimTags.size(); i++){
-        int d = objectDimTags[i].first;
-        int t = objectDimTags[i].second;
-        if(_isBound(d, t)) unbind(_find(d, t), d, t, true); // recursive
-      }
-    }
-    if(removeTool){
-      for(unsigned int i = 0; i < toolDimTags.size(); i++){
-        int d = toolDimTags[i].first;
-        int t = toolDimTags[i].second;
-        if(_isBound(d, t)) unbind(_find(d, t), d, t, true); // recursive
+  std::vector<std::pair<int, int> > inDimTags;
+  inDimTags.insert(inDimTags.end(), objectDimTags.begin(), objectDimTags.end());
+  inDimTags.insert(inDimTags.end(), toolDimTags.begin(), toolDimTags.end());
+  unsigned int numObjects = objectDimTags.size();
+
+  if(tag >= 0 || !preserveNumbering){
+    // if we specify the tag explicitly, or if we don't care about preserving
+    // the numering, just go ahead and bind the resulting shape (and sub-shapes)
+    for(unsigned int i = 0; i < inDimTags.size(); i++){
+      bool remove = (i < numObjects) ? removeObject : removeTool;
+      if(remove){
+        int d = inDimTags[i].first;
+        int t = inDimTags[i].second;
+        if(_isBound(d, t)) unbind(_find(d, t), d, t, true);
       }
     }
     _multiBind(result, tag, outDimTags, true, true);
-    return true;
+    _filterTags(outDimTags, minDim);
   }
-
-  // otherwise, try to preserve the numbering
-  std::vector<TopoDS_Shape> toBind;
-  for(unsigned int i = 0; i < objectDimTags.size(); i++){
-    int dim = objectDimTags[i].first;
-    int tag = objectDimTags[i].second;
-    if(mapDeleted[i] && !mapGenerated[i].Extent()){
-      // the shape has been deleted
-      if(removeObject && _isBound(dim, tag)){
-        unbind(_find(dim, tag), dim, tag, true);
-      }
-    }
-    else if(mapModified[i].Extent() == 0){
-      // the shape has not been modified
-      outDimTags.push_back(std::pair<int, int>(dim, tag));
-      // FIXME: since we currently don't guarantee that the tags of the entities
-      // on the boundary will be preserved, we must force a re-sync of the
-      // shape, by unbinding (which will add it in _toRemove) and re-binding it
-      if(removeObject && _isBound(dim, tag)){
-        TopoDS_Shape shape = _find(dim, tag);
-        unbind(shape, dim, tag, true);
-        bind(shape, dim, tag, true);
-      }
-    }
-    else if(mapModified[i].Extent() == 1){
-      if(removeObject){
-        // the shape has been replaced by a single shape, keep the same tag
-        if(_isBound(dim, tag)){
-          unbind(_find(dim, tag), dim, tag, true);
-        }
-        bind(mapModified[i].First(), dim, tag, true);
+  else{
+    // otherwise, try to preserve the numbering of the input shapes that did not
+    // change, or that were replaced by a single shape. Note that to preserve
+    // the numbering of smaller dimension entities (on boundaries) they should
+    // appear *before* higher dimensional entities in the object/tool lists.
+    _toPreserve.clear();
+    for(unsigned int i = 0; i < inDimTags.size(); i++){
+      int dim = inDimTags[i].first;
+      int tag = inDimTags[i].second;
+      bool remove = (i < numObjects) ? removeObject : removeTool;
+      if(mapDeleted[i]){ // deleted
+        if(remove) unbind(mapOriginal[i], dim, tag, true);
+        Msg::Debug("BOOL (%d,%d) deleted", dim, tag);
+      }
+      else if(mapModified[i].Extent() == 0){ // not modified
         outDimTags.push_back(std::pair<int, int>(dim, tag));
+        _toPreserve.insert(std::pair<int, int>(dim, tag));
+        Msg::Debug("BOOL (%d,%d) not modified", dim, tag);
+      }
+      else if(mapModified[i].Extent() == 1){ // replaced by single one
+        if(remove){
+          unbind(mapOriginal[i], dim, tag, true);
+          bind(mapModified[i].First(), dim, tag, false); // not recursive!
+          int t = _find(dim, mapModified[i].First());
+          if(tag != t)
+            Msg::Info("Could not preserve tag of %dD object %d (->%d)", dim, tag, t);
+          outDimTags.push_back(std::pair<int, int>(dim, t));
+          _toPreserve.insert(std::pair<int, int>(dim, t));
+        }
+        Msg::Debug("BOOL (%d,%d) replaced by 1", dim, tag);
       }
       else{
-        toBind.push_back(mapModified[i].First());
+        if(remove) unbind(mapOriginal[i], dim, tag, true);
+        Msg::Debug("BOOL (%d,%d) other", dim, tag);
       }
     }
-    else{
-      if(removeObject && _isBound(dim, tag)){
-        unbind(_find(dim, tag), dim, tag, true);
-      }
-      TopTools_ListIteratorOfListOfShape it(mapModified[i]);
-      for(; it.More(); it.Next())
-        toBind.push_back(it.Value());
-    }
-    {
-      TopTools_ListIteratorOfListOfShape it(mapGenerated[i]);
-      for(; it.More(); it.Next())
-        toBind.push_back(it.Value());
-    }
+    for(int dim = -2; dim <= 3; dim++) _recomputeMaxTag(dim);
+    // bind all remaining entities and add the new ones to the returned list
+    _multiBind(result, -1, outDimTags, false, true, true);
+    _filterTags(outDimTags, minDim);
+    _toPreserve.clear();
   }
 
-  for(unsigned int i = 0; i < toolDimTags.size(); i++){
-    int k = objectDimTags.size() + i;
-    int dim = toolDimTags[i].first;
-    int tag = toolDimTags[i].second;
-    if(mapDeleted[k] && !mapGenerated[k].Extent()){
-      // the shape has been deleted
-      if(removeTool && _isBound(dim, tag)){
-        unbind(_find(dim, tag), dim, tag, true); // recursive
-      }
-    }
-    else if(mapModified[k].Extent() == 0){
-      // the shape has not been modified
-      outDimTags.push_back(std::pair<int, int>(dim, tag));
-      // FIXME: since we currently don't guarantee that the tags of the entities
-      // on the boundary will be preserved, we must force a re-sync of the
-      // shape, by unbinding (which will add it in _toRemove) and re-binding it
-      if(removeTool && _isBound(dim, tag)){
-        TopoDS_Shape shape = _find(dim, tag);
-        unbind(shape, dim, tag, true);
-        bind(shape, dim, tag, true);
-      }
-    }
-    else if(mapModified[k].Extent() == 1){
-      if(removeTool){
-        // the shape has been replaced by a single shape, keep the same tag
-        if(_isBound(dim, tag)){
-          unbind(_find(dim, tag), dim, tag, true); // recursive
-        }
-        bind(mapModified[k].First(), dim, tag, true); // recursive
-        outDimTags.push_back(std::pair<int, int>(dim, tag));
-      }
-      else{
-        toBind.push_back(mapModified[k].First());
-      }
+  // return input/output correspondance maps
+  for(unsigned int i = 0; i < inDimTags.size(); i++){
+    int dim = inDimTags[i].first;
+    int tag = inDimTags[i].second;
+    std::pair<int, int> dimTag(dim, tag);
+    std::vector<std::pair<int, int> > dimTags;
+    if(mapDeleted[i]){ // deleted
+    }
+    else if(mapModified[i].Extent() == 0){ // not modified
+      dimTags.push_back(dimTag);
     }
     else{
-      if(removeTool && _isBound(dim, tag)){
-        unbind(_find(dim, tag), dim, tag, true); // recursive
+      TopTools_ListIteratorOfListOfShape it(mapModified[i]);
+      for(; it.More(); it.Next()){
+        if(_isBound(dim, it.Value())){
+          int t = _find(dim, it.Value());
+          dimTags.push_back(std::pair<int, int>(dim, t));
+        }
+      }
+      TopTools_ListIteratorOfListOfShape it2(mapGenerated[i]);
+      for(; it2.More(); it2.Next()){
+        if(_isBound(dim, it2.Value())){
+          int t = _find(dim, it2.Value());
+          dimTags.push_back(std::pair<int, int>(dim, t));
+        }
       }
-      TopTools_ListIteratorOfListOfShape it(mapModified[k]);
-      for(; it.More(); it.Next())
-        toBind.push_back(it.Value());
-    }
-    {
-      TopTools_ListIteratorOfListOfShape it(mapGenerated[k]);
-      for(; it.More(); it.Next())
-        toBind.push_back(it.Value());
     }
-  }
-
-  for(unsigned int i = 0; i < toBind.size(); i++){
-    // bind all remaining entities (and only return the new ones, as modified
-    // entities can appear as "Modified()" subshapes of both object and tool)
-    _multiBind(toBind[i], -1, outDimTags, true, true, true);
+    std::ostringstream sstream;
+    sstream << "BOOL in (" << dim << "," << tag << ") -> out";
+    for(unsigned int j = 0; j < dimTags.size(); j++)
+      sstream << " (" << dimTags[j].first << "," << dimTags[j].second << ")";
+    Msg::Debug("%s", sstream.str().c_str());
+    outDimTagsMap.push_back(dimTags);
   }
 
   return true;
@@ -2325,40 +2332,44 @@ bool OCC_Internals::booleanUnion(int tag,
                                  const std::vector<std::pair<int, int> > &objectDimTags,
                                  const std::vector<std::pair<int, int> > &toolDimTags,
                                  std::vector<std::pair<int, int> > &outDimTags,
+                                 std::vector<std::vector<std::pair<int, int> > > &outDimTagsMap,
                                  bool removeObject, bool removeTool)
 {
   return booleanOperator(tag, OCC_Internals::Union, objectDimTags, toolDimTags,
-                         outDimTags, removeObject, removeTool);
+                         outDimTags, outDimTagsMap, removeObject, removeTool);
 }
 
 bool OCC_Internals::booleanIntersection(int tag,
                                         const std::vector<std::pair<int, int> > &objectDimTags,
                                         const std::vector<std::pair<int, int> > &toolDimTags,
                                         std::vector<std::pair<int, int> > &outDimTags,
+                                        std::vector<std::vector<std::pair<int, int> > > &outDimTagsMap,
                                         bool removeObject, bool removeTool)
 {
   return booleanOperator(tag, OCC_Internals::Intersection, objectDimTags, toolDimTags,
-                         outDimTags, removeObject, removeTool);
+                         outDimTags, outDimTagsMap, removeObject, removeTool);
 }
 
 bool OCC_Internals::booleanDifference(int tag,
                                       const std::vector<std::pair<int, int> > &objectDimTags,
                                       const std::vector<std::pair<int, int> > &toolDimTags,
                                       std::vector<std::pair<int, int> > &outDimTags,
+                                      std::vector<std::vector<std::pair<int, int> > > &outDimTagsMap,
                                       bool removeObject, bool removeTool)
 {
   return booleanOperator(tag, OCC_Internals::Difference, objectDimTags, toolDimTags,
-                         outDimTags, removeObject, removeTool);
+                         outDimTags, outDimTagsMap, removeObject, removeTool);
 }
 
 bool OCC_Internals::booleanFragments(int tag,
                                      const std::vector<std::pair<int, int> > &objectDimTags,
                                      const std::vector<std::pair<int, int> > &toolDimTags,
                                      std::vector<std::pair<int, int> > &outDimTags,
+                                     std::vector<std::vector<std::pair<int, int> > > &outDimTagsMap,
                                      bool removeObject, bool removeTool)
 {
   return booleanOperator(tag, OCC_Internals::Fragments, objectDimTags, toolDimTags,
-                         outDimTags, removeObject, removeTool);
+                         outDimTags, outDimTagsMap, removeObject, removeTool);
 }
 
 bool OCC_Internals::_transform(const std::vector<std::pair<int, int> > &inDimTags,
@@ -2390,6 +2401,8 @@ bool OCC_Internals::_transform(const std::vector<std::pair<int, int> > &inDimTag
       }
       result = gtfo->Shape();
     }
+    // FIXME we should implement rebind(object, result, dim) which would
+    // unbind/bind all subshapes to the same tags
     unbind(object, dim, tag, true);
     bind(result, dim, tag, true);
   }
diff --git a/Geo/GModelIO_OCC.h b/Geo/GModelIO_OCC.h
index 47f40ef8e8d0d17e2bf1fe0b0ff4553709b33584..2f4c5c2b0f4297d0c50355b1d26638ce73cbbf10 100644
--- a/Geo/GModelIO_OCC.h
+++ b/Geo/GModelIO_OCC.h
@@ -62,6 +62,10 @@ class OCC_Internals {
   // remove from the model at the next synchronization
   std::set<std::pair<int, int> > _toRemove;
 
+  // cache of <dim,tag> pairs corresponding to entities that should not be
+  // unbound during boolean operations
+  std::set<std::pair<int, int> > _toPreserve;
+
   // mesh attributes
   OCCMeshAttributesRTree *_meshAttributes;
 
@@ -74,11 +78,14 @@ class OCC_Internals {
 
   // bind (potentially) mutliple entities in shape and return the tags in
   // outTags. If tag > 0 and a single entity if found, use that; if
-  // highestDimOnly is true, only return the entities of the highest dimension
+  // highestDimOnly is true, only bind the entities (and sub-entities, if
+  // recursive is set) of the highest dimension; if returnNewOnly is set, only
+  // return newly bound entities in outDimTags.
   void _multiBind(TopoDS_Shape shape, int tag,
                   std::vector<std::pair<int, int> > &outDimTags,
                   bool returnHighestDimOnly, bool recursive=false,
                   bool returnNewOnly=false);
+
   // is the entity of a given dimension and tag bound?
   bool _isBound(int dim, int tag);
 
@@ -247,26 +254,31 @@ class OCC_Internals {
                        const std::vector<std::pair<int, int> > &objectDimTags,
                        const std::vector<std::pair<int, int> > &toolDimTags,
                        std::vector<std::pair<int, int> > &outDimTags,
+                       std::vector<std::vector<std::pair<int, int> > > &outDimTagsMap,
                        bool removeObject, bool removeTool);
   bool booleanUnion(int tag,
                     const std::vector<std::pair<int, int> > &objectDimTags,
                     const std::vector<std::pair<int, int> > &toolDimTags,
                     std::vector<std::pair<int, int> > &outDimTags,
+                    std::vector<std::vector<std::pair<int, int> > > &outDimTagsMap,
                     bool removeObject, bool removeTool);
   bool booleanIntersection(int tag,
                            const std::vector<std::pair<int, int> > &objectDimTags,
                            const std::vector<std::pair<int, int> > &toolDimTags,
                            std::vector<std::pair<int, int> > &outDimTags,
+                           std::vector<std::vector<std::pair<int, int> > > &outDimTagsMap,
                            bool removeObject, bool removeTool);
   bool booleanDifference(int tag,
                          const std::vector<std::pair<int, int> > &objectDimTags,
                          const std::vector<std::pair<int, int> > &toolDimTags,
                          std::vector<std::pair<int, int> > &outDimTags,
+                         std::vector<std::vector<std::pair<int, int> > > &outDimTagsMap,
                          bool removeObject, bool removeTool);
   bool booleanFragments(int tag,
                         const std::vector<std::pair<int, int> > &objectDimTags,
                         const std::vector<std::pair<int, int> > &toolDimTags,
                         std::vector<std::pair<int, int> > &outDimTags,
+                        std::vector<std::vector<std::pair<int, int> > > &outDimTagsMap,
                         bool removeObject, bool removeTool);
 
   // apply transformations
@@ -536,6 +548,7 @@ public:
                        const std::vector<std::pair<int, int> > &objectDimTags,
                        const std::vector<std::pair<int, int> > &toolDimTags,
                        std::vector<std::pair<int, int> > &outDimTags,
+                       std::vector<std::vector<std::pair<int, int> > > &outDimTagsMap,
                        bool removeObject, bool removeTool)
   {
     return _error("apply boolean operator");
@@ -544,6 +557,7 @@ public:
                     const std::vector<std::pair<int, int> > &objectDimTags,
                     const std::vector<std::pair<int, int> > &toolDimTags,
                     std::vector<std::pair<int, int> > &outDimTags,
+                    std::vector<std::vector<std::pair<int, int> > > &outDimTagsMap,
                     bool removeObject, bool removeTool)
   {
     return _error("apply boolean union");
@@ -552,6 +566,7 @@ public:
                            const std::vector<std::pair<int, int> > &objectDimTags,
                            const std::vector<std::pair<int, int> > &toolDimTags,
                            std::vector<std::pair<int, int> > &outDimTags,
+                           std::vector<std::vector<std::pair<int, int> > > &outDimTagsMap,
                            bool removeObject, bool removeTool)
   {
     return _error("apply boolean intersection");
@@ -560,6 +575,7 @@ public:
                          const std::vector<std::pair<int, int> > &objectDimTags,
                          const std::vector<std::pair<int, int> > &toolDimTags,
                          std::vector<std::pair<int, int> > &outDimTags,
+                         std::vector<std::vector<std::pair<int, int> > > &outDimTagsMap,
                          bool removeObject, bool removeTool)
   {
     return _error("apply boolean difference");
@@ -568,6 +584,7 @@ public:
                         const std::vector<std::pair<int, int> > &objectDimTags,
                         const std::vector<std::pair<int, int> > &toolDimTags,
                         std::vector<std::pair<int, int> > &outDimTags,
+                        std::vector<std::vector<std::pair<int, int> > > &outDimTagsMap,
                         bool removeObject, bool removeTool)
   {
     return _error("apply boolean fragments");
diff --git a/Geo/Geo.cpp b/Geo/Geo.cpp
index 79d1fcf002be4cd3779c867c10e33526abbf2c9d..e60ec2cd62ae8160da0ab252ed7d68402ccfaae9 100644
--- a/Geo/Geo.cpp
+++ b/Geo/Geo.cpp
@@ -345,8 +345,9 @@ void EndCurve(Curve *c)
     }
     else if(!v[3] && fabs((R - R2) / (R + R2)) > 0.1){
       // check cocircular pts (allow 10% error)
-      Msg::Error("Control points of circle with tag %d are not cocircular (%g, %g)",
-                 c->Num, R, R2);
+      Msg::Error("Control points of circle with tag %d are not cocircular: "
+                 "R1=%g, R2=%g, n=[%g,%g,%g]",
+                 c->Num, R, R2, n[0], n[1], n[2]);
     }
     // A1 = angle first pt
     // A3 = angle last pt
diff --git a/Geo/OCCFace.cpp b/Geo/OCCFace.cpp
index 3bc647e3db577bf958ea5afd1575fabfbf6c9cd7..c9628090372666b55bac8fc90159c88105961e01 100644
--- a/Geo/OCCFace.cpp
+++ b/Geo/OCCFace.cpp
@@ -73,7 +73,7 @@ void OCCFace::setup()
 	Msg::Error("Unknown edge in face %d", tag());
       }
       else if(edge.Orientation() == TopAbs_INTERNAL){
-        Msg::Info("Adding embedded edge %d in face %d", e->tag(), tag());
+        Msg::Debug("Adding embedded edge %d in face %d", e->tag(), tag());
         embedded_edges.push_back(e);
         OCCEdge *occe = (OCCEdge*)e;
         occe->setTrimmed(this);
@@ -134,7 +134,7 @@ void OCCFace::setup()
       Msg::Error("Unknown vertex in face %d", tag());
     }
     else if(vertex.Orientation() == TopAbs_INTERNAL){
-      Msg::Info("Adding embedded vertex %d in face %d", v->tag(), tag());
+      Msg::Debug("Adding embedded vertex %d in face %d", v->tag(), tag());
       embedded_vertices.push_back(v);
     }
   }
diff --git a/Geo/OCCRegion.cpp b/Geo/OCCRegion.cpp
index 388d4fe9e66da9e1577e5559a976634057898e33..2ebcb241cd18db8584244079ef44f35c4366c043 100644
--- a/Geo/OCCRegion.cpp
+++ b/Geo/OCCRegion.cpp
@@ -51,7 +51,7 @@ void OCCRegion::setup()
         Msg::Error("Unknown face in region %d", tag());
       }
       else if (face.Orientation() == TopAbs_INTERNAL){
-        Msg::Info("Adding embedded face %d in region %d", f->tag(), tag());
+        Msg::Debug("Adding embedded face %d in region %d", f->tag(), tag());
         embedded_faces.push_back(f);
       }
       else{
@@ -70,7 +70,7 @@ void OCCRegion::setup()
       Msg::Error("Unknown edge in region %d", tag());
     }
     else if (edge.Orientation() == TopAbs_INTERNAL){
-      Msg::Info("Adding embedded edge %d in region %d", e->tag(), tag());
+      Msg::Debug("Adding embedded edge %d in region %d", e->tag(), tag());
       embedded_edges.push_back(e);
       //OCCEdge *occe = (OCCEdge*)e;
       //occe->setTrimmed(this);
@@ -86,7 +86,7 @@ void OCCRegion::setup()
       Msg::Error("Unknown vertex in region %d", tag());
     }
     else if (vertex.Orientation() == TopAbs_INTERNAL){
-      Msg::Info("Adding embedded vertex %d in region %d", v->tag(), tag());
+      Msg::Debug("Adding embedded vertex %d in region %d", v->tag(), tag());
       embedded_vertices.push_back(v);
     }
   }
diff --git a/Graphics/drawGlyph.cpp b/Graphics/drawGlyph.cpp
index 5834e8934baa4cbc95abc34aac76b76a58407d5d..557e59f4e99f432377f18b94fbbf33de873996d1 100644
--- a/Graphics/drawGlyph.cpp
+++ b/Graphics/drawGlyph.cpp
@@ -86,7 +86,8 @@ void drawContext::drawString(const std::string &s, double x, double y, double z,
             (CTX::instance()->print.fileFormat == FORMAT_PS ||
              CTX::instance()->print.fileFormat == FORMAT_EPS ||
              CTX::instance()->print.fileFormat == FORMAT_PDF ||
-             CTX::instance()->print.fileFormat == FORMAT_SVG)){
+             CTX::instance()->print.fileFormat == FORMAT_SVG ||
+             CTX::instance()->print.fileFormat == FORMAT_TIKZ)){
       gl2psText(s.c_str(), font_name.c_str(), font_size);
     }
     else{
diff --git a/Mesh/meshGFaceBDS.cpp b/Mesh/meshGFaceBDS.cpp
index 7b697dc3ca3f4a9d78b2468c275694462f575550..65a703375ecd701d4a8cdb125a6647cded00f790 100644
--- a/Mesh/meshGFaceBDS.cpp
+++ b/Mesh/meshGFaceBDS.cpp
@@ -318,7 +318,7 @@ bool edgeSwapTestDelaunay(BDS_Edge *e, GFace *gf)
   fourthPoint(p1x, p2x, op1x, fourth);
   double result = robustPredicates::insphere(p1x, p2x, op1x, fourth, op2x) *
     robustPredicates::orient3d(p1x, p2x, op1x, fourth);
-  return result > 0.;
+  return result > 1e-12;
 }
 
 bool edgeSwapTestHighOrder(BDS_Edge *e,GFace *gf)
diff --git a/Parser/Gmsh.tab.cpp b/Parser/Gmsh.tab.cpp
index 4baaad7aaff4adab9b0b53bc7159b369e33227b1..b9b9d48e1c19c1efae5003611ad83bc81d053e3e 100644
--- a/Parser/Gmsh.tab.cpp
+++ b/Parser/Gmsh.tab.cpp
@@ -1444,34 +1444,34 @@ static const yytype_uint16 yyrline[] =
     3643,  3662,  3661,  3686,  3685,  3712,  3711,  3736,  3735,  3758,
     3774,  3791,  3808,  3831,  3834,  3840,  3852,  3872,  3876,  3880,
     3884,  3888,  3892,  3896,  3900,  3909,  3922,  3923,  3924,  3925,
-    3926,  3930,  3931,  3932,  3933,  3934,  3937,  3960,  3979,  4001,
-    4004,  4020,  4023,  4040,  4043,  4049,  4052,  4059,  4062,  4069,
-    4086,  4127,  4171,  4210,  4235,  4244,  4274,  4300,  4326,  4358,
-    4385,  4411,  4437,  4463,  4489,  4511,  4522,  4570,  4619,  4631,
-    4642,  4646,  4656,  4667,  4668,  4669,  4673,  4679,  4691,  4709,
-    4737,  4738,  4739,  4740,  4741,  4742,  4743,  4744,  4745,  4752,
-    4753,  4754,  4755,  4756,  4757,  4758,  4759,  4760,  4761,  4762,
-    4763,  4764,  4765,  4766,  4767,  4768,  4769,  4770,  4771,  4772,
-    4773,  4774,  4775,  4776,  4777,  4778,  4779,  4780,  4781,  4782,
-    4783,  4784,  4785,  4786,  4787,  4788,  4789,  4798,  4799,  4800,
-    4801,  4802,  4803,  4804,  4805,  4806,  4807,  4808,  4813,  4812,
-    4820,  4822,  4827,  4832,  4836,  4841,  4846,  4850,  4854,  4858,
-    4862,  4866,  4870,  4876,  4892,  4897,  4903,  4909,  4928,  4949,
-    4982,  4986,  4991,  4995,  4999,  5003,  5008,  5013,  5023,  5033,
-    5038,  5049,  5058,  5063,  5068,  5096,  5097,  5103,  5104,  5110,
-    5109,  5132,  5134,  5139,  5148,  5150,  5156,  5157,  5162,  5166,
-    5170,  5174,  5178,  5185,  5189,  5193,  5197,  5204,  5209,  5216,
-    5221,  5225,  5230,  5234,  5242,  5253,  5257,  5269,  5277,  5285,
-    5292,  5302,  5325,  5331,  5342,  5348,  5358,  5368,  5378,  5390,
-    5394,  5399,  5411,  5415,  5419,  5423,  5441,  5449,  5457,  5486,
-    5496,  5512,  5523,  5528,  5532,  5536,  5548,  5552,  5564,  5581,
-    5591,  5595,  5610,  5615,  5622,  5626,  5631,  5645,  5661,  5665,
-    5669,  5673,  5677,  5685,  5691,  5697,  5703,  5712,  5716,  5720,
-    5728,  5734,  5740,  5744,  5752,  5760,  5767,  5776,  5780,  5784,
-    5799,  5813,  5827,  5839,  5855,  5864,  5873,  5883,  5894,  5902,
-    5910,  5914,  5933,  5940,  5946,  5953,  5961,  5960,  5970,  5994,
-    5996,  6002,  6007,  6009,  6014,  6019,  6024,  6026,  6030,  6042,
-    6056,  6060,  6067,  6075,  6083,  6094,  6096,  6099
+    3926,  3930,  3931,  3932,  3933,  3934,  3937,  3961,  3980,  4003,
+    4006,  4022,  4025,  4042,  4045,  4051,  4054,  4061,  4064,  4071,
+    4088,  4129,  4173,  4212,  4237,  4246,  4276,  4302,  4328,  4360,
+    4387,  4413,  4439,  4465,  4491,  4513,  4524,  4572,  4621,  4633,
+    4644,  4648,  4658,  4669,  4670,  4671,  4675,  4681,  4693,  4711,
+    4739,  4740,  4741,  4742,  4743,  4744,  4745,  4746,  4747,  4754,
+    4755,  4756,  4757,  4758,  4759,  4760,  4761,  4762,  4763,  4764,
+    4765,  4766,  4767,  4768,  4769,  4770,  4771,  4772,  4773,  4774,
+    4775,  4776,  4777,  4778,  4779,  4780,  4781,  4782,  4783,  4784,
+    4785,  4786,  4787,  4788,  4789,  4790,  4791,  4800,  4801,  4802,
+    4803,  4804,  4805,  4806,  4807,  4808,  4809,  4810,  4815,  4814,
+    4822,  4824,  4829,  4834,  4838,  4843,  4848,  4852,  4856,  4860,
+    4864,  4868,  4872,  4878,  4894,  4899,  4905,  4911,  4930,  4951,
+    4984,  4988,  4993,  4997,  5001,  5005,  5010,  5015,  5025,  5035,
+    5040,  5051,  5060,  5065,  5070,  5098,  5099,  5105,  5106,  5112,
+    5111,  5134,  5136,  5141,  5150,  5152,  5158,  5159,  5164,  5168,
+    5172,  5176,  5180,  5187,  5191,  5195,  5199,  5206,  5211,  5218,
+    5223,  5227,  5232,  5236,  5244,  5255,  5259,  5271,  5279,  5287,
+    5294,  5304,  5327,  5333,  5344,  5350,  5360,  5370,  5380,  5392,
+    5396,  5401,  5413,  5417,  5421,  5425,  5443,  5451,  5459,  5488,
+    5498,  5514,  5525,  5530,  5534,  5538,  5550,  5554,  5566,  5583,
+    5593,  5597,  5612,  5617,  5624,  5628,  5633,  5647,  5663,  5667,
+    5671,  5675,  5679,  5687,  5693,  5699,  5705,  5714,  5718,  5722,
+    5730,  5736,  5742,  5746,  5754,  5762,  5769,  5778,  5782,  5786,
+    5801,  5815,  5829,  5841,  5857,  5866,  5875,  5885,  5896,  5904,
+    5912,  5916,  5935,  5942,  5948,  5955,  5963,  5962,  5972,  5996,
+    5998,  6004,  6009,  6011,  6016,  6021,  6026,  6028,  6032,  6044,
+    6058,  6062,  6069,  6077,  6085,  6096,  6098,  6101
 };
 #endif
 
@@ -10478,13 +10478,14 @@ yyreduce:
       bool r = true;
       if(gmsh_yyfactory == "OpenCASCADE" && GModel::current()->getOCCInternals()){
         std::vector<std::pair<int, int > > object, tool, out;
+        std::vector<std::vector<std::pair<int, int > > > outMap;
         ListOfShapes2VectorOfPairs((yyvsp[(3) - (9)].l), object);
         ListOfShapes2VectorOfPairs((yyvsp[(7) - (9)].l), tool);
         // currently we don't distinguish between Delete and Recursive Delete:
         // we always delete recursively. Let us know if you have examples where
         // having the choice would be interesting
         r = GModel::current()->getOCCInternals()->booleanOperator
-          (-1, (OCC_Internals::BooleanOperator)(yyvsp[(1) - (9)].i), object, tool, out, (yyvsp[(4) - (9)].i), (yyvsp[(8) - (9)].i));
+          (-1, (OCC_Internals::BooleanOperator)(yyvsp[(1) - (9)].i), object, tool, out, outMap, (yyvsp[(4) - (9)].i), (yyvsp[(8) - (9)].i));
         VectorOfPairs2ListOfShapes(out, (yyval.l));
       }
       else{
@@ -10497,7 +10498,7 @@ yyreduce:
     break;
 
   case 317:
-#line 3961 "Gmsh.y"
+#line 3962 "Gmsh.y"
     {
       (yyval.l) = List_Create(2, 1, sizeof(Shape));
       bool r = true;
@@ -10516,18 +10517,19 @@ yyreduce:
     break;
 
   case 318:
-#line 3981 "Gmsh.y"
+#line 3982 "Gmsh.y"
     {
       bool r = true;
       if(gmsh_yyfactory == "OpenCASCADE" && GModel::current()->getOCCInternals()){
         std::vector<std::pair<int, int> > object, tool, out;
+        std::vector<std::vector<std::pair<int, int > > > outMap;
         ListOfShapes2VectorOfPairs((yyvsp[(7) - (14)].l), object);
         ListOfShapes2VectorOfPairs((yyvsp[(11) - (14)].l), tool);
         // currently we don't distinguish between Delete and Recursive Delete:
         // we always delete recursively. Let us know if you have examples where
         // having the choice would be interesting
         r = GModel::current()->getOCCInternals()->booleanOperator
-          ((int)(yyvsp[(3) - (14)].d), (OCC_Internals::BooleanOperator)(yyvsp[(1) - (14)].i), object, tool, out, (yyvsp[(8) - (14)].i), (yyvsp[(12) - (14)].i));
+          ((int)(yyvsp[(3) - (14)].d), (OCC_Internals::BooleanOperator)(yyvsp[(1) - (14)].i), object, tool, out, outMap, (yyvsp[(8) - (14)].i), (yyvsp[(12) - (14)].i));
       }
       if(!r) yymsg(0, "Could not apply boolean operator");
       List_Delete((yyvsp[(7) - (14)].l));
@@ -10536,14 +10538,14 @@ yyreduce:
     break;
 
   case 319:
-#line 4001 "Gmsh.y"
+#line 4003 "Gmsh.y"
     {
       (yyval.v)[0] = (yyval.v)[1] = 1.;
     ;}
     break;
 
   case 320:
-#line 4005 "Gmsh.y"
+#line 4007 "Gmsh.y"
     {
       if(!strcmp((yyvsp[(2) - (3)].c), "Progression") || !strcmp((yyvsp[(2) - (3)].c), "Power"))
         (yyval.v)[0] = 1.;
@@ -10559,14 +10561,14 @@ yyreduce:
     break;
 
   case 321:
-#line 4020 "Gmsh.y"
+#line 4022 "Gmsh.y"
     {
       (yyval.i) = -1; // left
     ;}
     break;
 
   case 322:
-#line 4024 "Gmsh.y"
+#line 4026 "Gmsh.y"
     {
       if(!strcmp((yyvsp[(1) - (1)].c), "Right"))
         (yyval.i) = 1;
@@ -10583,49 +10585,49 @@ yyreduce:
     break;
 
   case 323:
-#line 4040 "Gmsh.y"
+#line 4042 "Gmsh.y"
     {
      (yyval.l) = List_Create(1, 1, sizeof(double));
    ;}
     break;
 
   case 324:
-#line 4044 "Gmsh.y"
+#line 4046 "Gmsh.y"
     {
      (yyval.l) = (yyvsp[(2) - (2)].l);
    ;}
     break;
 
   case 325:
-#line 4049 "Gmsh.y"
+#line 4051 "Gmsh.y"
     {
       (yyval.i) = 45;
     ;}
     break;
 
   case 326:
-#line 4053 "Gmsh.y"
+#line 4055 "Gmsh.y"
     {
       (yyval.i) = (int)(yyvsp[(2) - (2)].d);
     ;}
     break;
 
   case 327:
-#line 4059 "Gmsh.y"
+#line 4061 "Gmsh.y"
     {
       (yyval.l) = List_Create(1, 1, sizeof(double));
     ;}
     break;
 
   case 328:
-#line 4063 "Gmsh.y"
+#line 4065 "Gmsh.y"
     {
       (yyval.l) = (yyvsp[(2) - (2)].l);
     ;}
     break;
 
   case 329:
-#line 4070 "Gmsh.y"
+#line 4072 "Gmsh.y"
     {
       // mesh sizes at vertices are stored in internal CAD data, as they can be
       // specified during vertex creation and copied around during CAD
@@ -10645,7 +10647,7 @@ yyreduce:
     break;
 
   case 330:
-#line 4087 "Gmsh.y"
+#line 4089 "Gmsh.y"
     {
       // transfinite constraints are stored in GEO internals in addition to
       // GModel, as they can be copied around during GEO operations
@@ -10689,7 +10691,7 @@ yyreduce:
     break;
 
   case 331:
-#line 4128 "Gmsh.y"
+#line 4130 "Gmsh.y"
     {
       // transfinite constraints are stored in GEO internals in addition to
       // GModel, as they can be copied around during GEO operations
@@ -10736,7 +10738,7 @@ yyreduce:
     break;
 
   case 332:
-#line 4172 "Gmsh.y"
+#line 4174 "Gmsh.y"
     {
       // transfinite constraints are stored in GEO internals in addition to
       // GModel, as they can be copied around during GEO operations
@@ -10778,7 +10780,7 @@ yyreduce:
     break;
 
   case 333:
-#line 4211 "Gmsh.y"
+#line 4213 "Gmsh.y"
     {
       // transfinite constraints are stored in GEO internals in addition to
       // GModel, as they can be copied around during GEO operations
@@ -10806,7 +10808,7 @@ yyreduce:
     break;
 
   case 334:
-#line 4236 "Gmsh.y"
+#line 4238 "Gmsh.y"
     {
       for(int i = 0; i < List_Nbr((yyvsp[(4) - (8)].l)); i++){
 	double d;
@@ -10818,7 +10820,7 @@ yyreduce:
     break;
 
   case 335:
-#line 4245 "Gmsh.y"
+#line 4247 "Gmsh.y"
     {
       // recombine constraints are stored in GEO internals in addition to
       // GModel, as they can be copied around during GEO operations
@@ -10851,7 +10853,7 @@ yyreduce:
     break;
 
   case 336:
-#line 4275 "Gmsh.y"
+#line 4277 "Gmsh.y"
     {
       // recombine constraints are stored in GEO internals in addition to
       // GModel, as they can be copied around during GEO operations
@@ -10880,7 +10882,7 @@ yyreduce:
     break;
 
   case 337:
-#line 4301 "Gmsh.y"
+#line 4303 "Gmsh.y"
     {
       // smoothing constraints are stored in GEO internals in addition to
       // GModel, as they can be copied around during GEO operations
@@ -10909,7 +10911,7 @@ yyreduce:
     break;
 
   case 338:
-#line 4328 "Gmsh.y"
+#line 4330 "Gmsh.y"
     {
       if (List_Nbr((yyvsp[(4) - (11)].l)) != List_Nbr((yyvsp[(8) - (11)].l))){
         yymsg(0, "Number of master lines (%d) different from number of "
@@ -10943,7 +10945,7 @@ yyreduce:
     break;
 
   case 339:
-#line 4360 "Gmsh.y"
+#line 4362 "Gmsh.y"
     {
       if (List_Nbr((yyvsp[(4) - (11)].l)) != List_Nbr((yyvsp[(8) - (11)].l))){
         yymsg(0, "Number of master faces (%d) different from number of "
@@ -10972,7 +10974,7 @@ yyreduce:
     break;
 
   case 340:
-#line 4387 "Gmsh.y"
+#line 4389 "Gmsh.y"
     {
       if (List_Nbr((yyvsp[(4) - (18)].l)) != List_Nbr((yyvsp[(8) - (18)].l))){
         yymsg(0, "Number of master edges (%d) different from number of "
@@ -11000,7 +11002,7 @@ yyreduce:
     break;
 
   case 341:
-#line 4413 "Gmsh.y"
+#line 4415 "Gmsh.y"
     {
       if (List_Nbr((yyvsp[(4) - (18)].l)) != List_Nbr((yyvsp[(8) - (18)].l))){
         yymsg(0, "Number of master faces (%d) different from number of "
@@ -11028,7 +11030,7 @@ yyreduce:
     break;
 
   case 342:
-#line 4439 "Gmsh.y"
+#line 4441 "Gmsh.y"
     {
       if (List_Nbr((yyvsp[(4) - (12)].l)) != List_Nbr((yyvsp[(8) - (12)].l))){
         yymsg(0, "Number of master edges (%d) different from number of "
@@ -11056,7 +11058,7 @@ yyreduce:
     break;
 
   case 343:
-#line 4465 "Gmsh.y"
+#line 4467 "Gmsh.y"
     {
       if (List_Nbr((yyvsp[(4) - (12)].l)) != List_Nbr((yyvsp[(8) - (12)].l))){
         yymsg(0, "Number of master faces (%d) different from number of "
@@ -11084,7 +11086,7 @@ yyreduce:
     break;
 
   case 344:
-#line 4491 "Gmsh.y"
+#line 4493 "Gmsh.y"
     {
       if (List_Nbr((yyvsp[(5) - (12)].l)) != List_Nbr((yyvsp[(10) - (12)].l))){
         yymsg(0, "Number of master surface edges (%d) different from number of "
@@ -11108,7 +11110,7 @@ yyreduce:
     break;
 
   case 345:
-#line 4512 "Gmsh.y"
+#line 4514 "Gmsh.y"
     {
       if (((yyvsp[(6) - (10)].i)==2 || (yyvsp[(6) - (10)].i)==3) && (yyvsp[(1) - (10)].i)<(yyvsp[(6) - (10)].i) ) {
         std::vector<int> tags; ListOfDouble2Vector((yyvsp[(3) - (10)].l), tags);
@@ -11122,7 +11124,7 @@ yyreduce:
     break;
 
   case 346:
-#line 4523 "Gmsh.y"
+#line 4525 "Gmsh.y"
     {
       // reverse mesh constraints are stored in GEO internals in addition to
       // GModel, as they can be copied around during GEO operations
@@ -11173,7 +11175,7 @@ yyreduce:
     break;
 
   case 347:
-#line 4571 "Gmsh.y"
+#line 4573 "Gmsh.y"
     {
       if(!(yyvsp[(3) - (4)].l)){
         switch ((yyvsp[(2) - (4)].i)) {
@@ -11225,7 +11227,7 @@ yyreduce:
     break;
 
   case 348:
-#line 4620 "Gmsh.y"
+#line 4622 "Gmsh.y"
     {
       for(int i = 0; i < List_Nbr((yyvsp[(3) - (4)].l)); i++){
 	double dnum;
@@ -11240,7 +11242,7 @@ yyreduce:
     break;
 
   case 349:
-#line 4632 "Gmsh.y"
+#line 4634 "Gmsh.y"
     {
       std::vector<int> tags; ListOfDouble2Vector((yyvsp[(3) - (4)].l), tags);
       GModel::current()->getGEOInternals()->setCompoundMesh((yyvsp[(2) - (4)].i), tags);
@@ -11249,14 +11251,14 @@ yyreduce:
     break;
 
   case 350:
-#line 4643 "Gmsh.y"
+#line 4645 "Gmsh.y"
     {
       GModel::current()->getGEOInternals()->removeAllDuplicates();
     ;}
     break;
 
   case 351:
-#line 4647 "Gmsh.y"
+#line 4649 "Gmsh.y"
     {
       if(!strcmp((yyvsp[(2) - (3)].c), "Geometry"))
         GModel::current()->getGEOInternals()->removeAllDuplicates();
@@ -11269,7 +11271,7 @@ yyreduce:
     break;
 
   case 352:
-#line 4657 "Gmsh.y"
+#line 4659 "Gmsh.y"
     {
       std::vector<int> tags; ListOfDouble2Vector((yyvsp[(4) - (6)].l), tags);
       GModel::current()->getGEOInternals()->mergeVertices(tags);
@@ -11278,22 +11280,22 @@ yyreduce:
     break;
 
   case 353:
-#line 4667 "Gmsh.y"
+#line 4669 "Gmsh.y"
     { (yyval.c) = (char*)"Homology"; ;}
     break;
 
   case 354:
-#line 4668 "Gmsh.y"
+#line 4670 "Gmsh.y"
     { (yyval.c) = (char*)"Cohomology"; ;}
     break;
 
   case 355:
-#line 4669 "Gmsh.y"
+#line 4671 "Gmsh.y"
     { (yyval.c) = (char*)"Betti"; ;}
     break;
 
   case 356:
-#line 4674 "Gmsh.y"
+#line 4676 "Gmsh.y"
     {
       std::vector<int> domain, subdomain, dim;
       for(int i = 0; i < 4; i++) dim.push_back(i);
@@ -11302,7 +11304,7 @@ yyreduce:
     break;
 
   case 357:
-#line 4680 "Gmsh.y"
+#line 4682 "Gmsh.y"
     {
       std::vector<int> domain, subdomain, dim;
       for(int i = 0; i < List_Nbr((yyvsp[(3) - (5)].l)); i++){
@@ -11317,7 +11319,7 @@ yyreduce:
     break;
 
   case 358:
-#line 4692 "Gmsh.y"
+#line 4694 "Gmsh.y"
     {
       std::vector<int> domain, subdomain, dim;
       for(int i = 0; i < List_Nbr((yyvsp[(3) - (7)].l)); i++){
@@ -11338,7 +11340,7 @@ yyreduce:
     break;
 
   case 359:
-#line 4710 "Gmsh.y"
+#line 4712 "Gmsh.y"
     {
       std::vector<int> domain, subdomain, dim;
       for(int i = 0; i < List_Nbr((yyvsp[(6) - (10)].l)); i++){
@@ -11364,47 +11366,47 @@ yyreduce:
     break;
 
   case 360:
-#line 4737 "Gmsh.y"
+#line 4739 "Gmsh.y"
     { (yyval.d) = (yyvsp[(1) - (1)].d);           ;}
     break;
 
   case 361:
-#line 4738 "Gmsh.y"
+#line 4740 "Gmsh.y"
     { (yyval.d) = (yyvsp[(2) - (3)].d);           ;}
     break;
 
   case 362:
-#line 4739 "Gmsh.y"
+#line 4741 "Gmsh.y"
     { (yyval.d) = -(yyvsp[(2) - (2)].d);          ;}
     break;
 
   case 363:
-#line 4740 "Gmsh.y"
+#line 4742 "Gmsh.y"
     { (yyval.d) = (yyvsp[(2) - (2)].d);           ;}
     break;
 
   case 364:
-#line 4741 "Gmsh.y"
+#line 4743 "Gmsh.y"
     { (yyval.d) = !(yyvsp[(2) - (2)].d);          ;}
     break;
 
   case 365:
-#line 4742 "Gmsh.y"
+#line 4744 "Gmsh.y"
     { (yyval.d) = (yyvsp[(1) - (3)].d) - (yyvsp[(3) - (3)].d);      ;}
     break;
 
   case 366:
-#line 4743 "Gmsh.y"
+#line 4745 "Gmsh.y"
     { (yyval.d) = (yyvsp[(1) - (3)].d) + (yyvsp[(3) - (3)].d);      ;}
     break;
 
   case 367:
-#line 4744 "Gmsh.y"
+#line 4746 "Gmsh.y"
     { (yyval.d) = (yyvsp[(1) - (3)].d) * (yyvsp[(3) - (3)].d);      ;}
     break;
 
   case 368:
-#line 4746 "Gmsh.y"
+#line 4748 "Gmsh.y"
     {
       if(!(yyvsp[(3) - (3)].d))
 	yymsg(0, "Division by zero in '%g / %g'", (yyvsp[(1) - (3)].d), (yyvsp[(3) - (3)].d));
@@ -11414,257 +11416,257 @@ yyreduce:
     break;
 
   case 369:
-#line 4752 "Gmsh.y"
+#line 4754 "Gmsh.y"
     { (yyval.d) = (int)(yyvsp[(1) - (3)].d) | (int)(yyvsp[(3) - (3)].d); ;}
     break;
 
   case 370:
-#line 4753 "Gmsh.y"
+#line 4755 "Gmsh.y"
     { (yyval.d) = (int)(yyvsp[(1) - (3)].d) & (int)(yyvsp[(3) - (3)].d); ;}
     break;
 
   case 371:
-#line 4754 "Gmsh.y"
+#line 4756 "Gmsh.y"
     { (yyval.d) = (int)(yyvsp[(1) - (3)].d) % (int)(yyvsp[(3) - (3)].d); ;}
     break;
 
   case 372:
-#line 4755 "Gmsh.y"
+#line 4757 "Gmsh.y"
     { (yyval.d) = pow((yyvsp[(1) - (3)].d), (yyvsp[(3) - (3)].d));  ;}
     break;
 
   case 373:
-#line 4756 "Gmsh.y"
+#line 4758 "Gmsh.y"
     { (yyval.d) = (yyvsp[(1) - (3)].d) < (yyvsp[(3) - (3)].d);      ;}
     break;
 
   case 374:
-#line 4757 "Gmsh.y"
+#line 4759 "Gmsh.y"
     { (yyval.d) = (yyvsp[(1) - (3)].d) > (yyvsp[(3) - (3)].d);      ;}
     break;
 
   case 375:
-#line 4758 "Gmsh.y"
+#line 4760 "Gmsh.y"
     { (yyval.d) = (yyvsp[(1) - (3)].d) <= (yyvsp[(3) - (3)].d);     ;}
     break;
 
   case 376:
-#line 4759 "Gmsh.y"
+#line 4761 "Gmsh.y"
     { (yyval.d) = (yyvsp[(1) - (3)].d) >= (yyvsp[(3) - (3)].d);     ;}
     break;
 
   case 377:
-#line 4760 "Gmsh.y"
+#line 4762 "Gmsh.y"
     { (yyval.d) = (yyvsp[(1) - (3)].d) == (yyvsp[(3) - (3)].d);     ;}
     break;
 
   case 378:
-#line 4761 "Gmsh.y"
+#line 4763 "Gmsh.y"
     { (yyval.d) = (yyvsp[(1) - (3)].d) != (yyvsp[(3) - (3)].d);     ;}
     break;
 
   case 379:
-#line 4762 "Gmsh.y"
+#line 4764 "Gmsh.y"
     { (yyval.d) = (yyvsp[(1) - (3)].d) && (yyvsp[(3) - (3)].d);     ;}
     break;
 
   case 380:
-#line 4763 "Gmsh.y"
+#line 4765 "Gmsh.y"
     { (yyval.d) = (yyvsp[(1) - (3)].d) || (yyvsp[(3) - (3)].d);     ;}
     break;
 
   case 381:
-#line 4764 "Gmsh.y"
+#line 4766 "Gmsh.y"
     { (yyval.d) = ((int)(yyvsp[(1) - (3)].d) >> (int)(yyvsp[(3) - (3)].d)); ;}
     break;
 
   case 382:
-#line 4765 "Gmsh.y"
+#line 4767 "Gmsh.y"
     { (yyval.d) = ((int)(yyvsp[(1) - (3)].d) << (int)(yyvsp[(3) - (3)].d)); ;}
     break;
 
   case 383:
-#line 4766 "Gmsh.y"
+#line 4768 "Gmsh.y"
     { (yyval.d) = (yyvsp[(1) - (5)].d) ? (yyvsp[(3) - (5)].d) : (yyvsp[(5) - (5)].d); ;}
     break;
 
   case 384:
-#line 4767 "Gmsh.y"
+#line 4769 "Gmsh.y"
     { (yyval.d) = exp((yyvsp[(3) - (4)].d));      ;}
     break;
 
   case 385:
-#line 4768 "Gmsh.y"
+#line 4770 "Gmsh.y"
     { (yyval.d) = log((yyvsp[(3) - (4)].d));      ;}
     break;
 
   case 386:
-#line 4769 "Gmsh.y"
+#line 4771 "Gmsh.y"
     { (yyval.d) = log10((yyvsp[(3) - (4)].d));    ;}
     break;
 
   case 387:
-#line 4770 "Gmsh.y"
+#line 4772 "Gmsh.y"
     { (yyval.d) = sqrt((yyvsp[(3) - (4)].d));     ;}
     break;
 
   case 388:
-#line 4771 "Gmsh.y"
+#line 4773 "Gmsh.y"
     { (yyval.d) = sin((yyvsp[(3) - (4)].d));      ;}
     break;
 
   case 389:
-#line 4772 "Gmsh.y"
+#line 4774 "Gmsh.y"
     { (yyval.d) = asin((yyvsp[(3) - (4)].d));     ;}
     break;
 
   case 390:
-#line 4773 "Gmsh.y"
+#line 4775 "Gmsh.y"
     { (yyval.d) = cos((yyvsp[(3) - (4)].d));      ;}
     break;
 
   case 391:
-#line 4774 "Gmsh.y"
+#line 4776 "Gmsh.y"
     { (yyval.d) = acos((yyvsp[(3) - (4)].d));     ;}
     break;
 
   case 392:
-#line 4775 "Gmsh.y"
+#line 4777 "Gmsh.y"
     { (yyval.d) = tan((yyvsp[(3) - (4)].d));      ;}
     break;
 
   case 393:
-#line 4776 "Gmsh.y"
+#line 4778 "Gmsh.y"
     { (yyval.d) = atan((yyvsp[(3) - (4)].d));     ;}
     break;
 
   case 394:
-#line 4777 "Gmsh.y"
+#line 4779 "Gmsh.y"
     { (yyval.d) = atan2((yyvsp[(3) - (6)].d), (yyvsp[(5) - (6)].d));;}
     break;
 
   case 395:
-#line 4778 "Gmsh.y"
+#line 4780 "Gmsh.y"
     { (yyval.d) = sinh((yyvsp[(3) - (4)].d));     ;}
     break;
 
   case 396:
-#line 4779 "Gmsh.y"
+#line 4781 "Gmsh.y"
     { (yyval.d) = cosh((yyvsp[(3) - (4)].d));     ;}
     break;
 
   case 397:
-#line 4780 "Gmsh.y"
+#line 4782 "Gmsh.y"
     { (yyval.d) = tanh((yyvsp[(3) - (4)].d));     ;}
     break;
 
   case 398:
-#line 4781 "Gmsh.y"
+#line 4783 "Gmsh.y"
     { (yyval.d) = fabs((yyvsp[(3) - (4)].d));     ;}
     break;
 
   case 399:
-#line 4782 "Gmsh.y"
+#line 4784 "Gmsh.y"
     { (yyval.d) = std::abs((yyvsp[(3) - (4)].d)); ;}
     break;
 
   case 400:
-#line 4783 "Gmsh.y"
+#line 4785 "Gmsh.y"
     { (yyval.d) = floor((yyvsp[(3) - (4)].d));    ;}
     break;
 
   case 401:
-#line 4784 "Gmsh.y"
+#line 4786 "Gmsh.y"
     { (yyval.d) = ceil((yyvsp[(3) - (4)].d));     ;}
     break;
 
   case 402:
-#line 4785 "Gmsh.y"
+#line 4787 "Gmsh.y"
     { (yyval.d) = floor((yyvsp[(3) - (4)].d) + 0.5); ;}
     break;
 
   case 403:
-#line 4786 "Gmsh.y"
+#line 4788 "Gmsh.y"
     { (yyval.d) = fmod((yyvsp[(3) - (6)].d), (yyvsp[(5) - (6)].d)); ;}
     break;
 
   case 404:
-#line 4787 "Gmsh.y"
+#line 4789 "Gmsh.y"
     { (yyval.d) = fmod((yyvsp[(3) - (6)].d), (yyvsp[(5) - (6)].d)); ;}
     break;
 
   case 405:
-#line 4788 "Gmsh.y"
+#line 4790 "Gmsh.y"
     { (yyval.d) = sqrt((yyvsp[(3) - (6)].d) * (yyvsp[(3) - (6)].d) + (yyvsp[(5) - (6)].d) * (yyvsp[(5) - (6)].d)); ;}
     break;
 
   case 406:
-#line 4789 "Gmsh.y"
+#line 4791 "Gmsh.y"
     { (yyval.d) = (yyvsp[(3) - (4)].d) * (double)rand() / (double)RAND_MAX; ;}
     break;
 
   case 407:
-#line 4798 "Gmsh.y"
+#line 4800 "Gmsh.y"
     { (yyval.d) = (yyvsp[(1) - (1)].d); ;}
     break;
 
   case 408:
-#line 4799 "Gmsh.y"
+#line 4801 "Gmsh.y"
     { (yyval.d) = 3.141592653589793; ;}
     break;
 
   case 409:
-#line 4800 "Gmsh.y"
+#line 4802 "Gmsh.y"
     { (yyval.d) = (double)ImbricatedTest; ;}
     break;
 
   case 410:
-#line 4801 "Gmsh.y"
+#line 4803 "Gmsh.y"
     { (yyval.d) = Msg::GetCommRank(); ;}
     break;
 
   case 411:
-#line 4802 "Gmsh.y"
+#line 4804 "Gmsh.y"
     { (yyval.d) = Msg::GetCommSize(); ;}
     break;
 
   case 412:
-#line 4803 "Gmsh.y"
+#line 4805 "Gmsh.y"
     { (yyval.d) = GetGmshMajorVersion(); ;}
     break;
 
   case 413:
-#line 4804 "Gmsh.y"
+#line 4806 "Gmsh.y"
     { (yyval.d) = GetGmshMinorVersion(); ;}
     break;
 
   case 414:
-#line 4805 "Gmsh.y"
+#line 4807 "Gmsh.y"
     { (yyval.d) = GetGmshPatchVersion(); ;}
     break;
 
   case 415:
-#line 4806 "Gmsh.y"
+#line 4808 "Gmsh.y"
     { (yyval.d) = Cpu(); ;}
     break;
 
   case 416:
-#line 4807 "Gmsh.y"
+#line 4809 "Gmsh.y"
     { (yyval.d) = GetMemoryUsage()/1024./1024.; ;}
     break;
 
   case 417:
-#line 4808 "Gmsh.y"
+#line 4810 "Gmsh.y"
     { (yyval.d) = TotalRam(); ;}
     break;
 
   case 418:
-#line 4813 "Gmsh.y"
+#line 4815 "Gmsh.y"
     { init_options(); ;}
     break;
 
   case 419:
-#line 4815 "Gmsh.y"
+#line 4817 "Gmsh.y"
     {
       std::vector<double> val(1, (yyvsp[(3) - (6)].d));
       Msg::ExchangeOnelabParameter("", val, floatOptions, charOptions);
@@ -11673,12 +11675,12 @@ yyreduce:
     break;
 
   case 420:
-#line 4821 "Gmsh.y"
+#line 4823 "Gmsh.y"
     { (yyval.d) = (yyvsp[(1) - (1)].d); ;}
     break;
 
   case 421:
-#line 4823 "Gmsh.y"
+#line 4825 "Gmsh.y"
     {
       (yyval.d) = Msg::GetOnelabNumber((yyvsp[(3) - (4)].c));
       Free((yyvsp[(3) - (4)].c));
@@ -11686,7 +11688,7 @@ yyreduce:
     break;
 
   case 422:
-#line 4828 "Gmsh.y"
+#line 4830 "Gmsh.y"
     {
       (yyval.d) = Msg::GetOnelabNumber((yyvsp[(3) - (6)].c), (yyvsp[(5) - (6)].d));
       Free((yyvsp[(3) - (6)].c));
@@ -11694,70 +11696,70 @@ yyreduce:
     break;
 
   case 423:
-#line 4833 "Gmsh.y"
+#line 4835 "Gmsh.y"
     {
       (yyval.d) = treat_Struct_FullName_Float((yyvsp[(1) - (1)].c2).char1, (yyvsp[(1) - (1)].c2).char2);
     ;}
     break;
 
   case 424:
-#line 4838 "Gmsh.y"
+#line 4840 "Gmsh.y"
     {
       (yyval.d) = treat_Struct_FullName_Float(NULL, (yyvsp[(1) - (4)].c), 2, (int)(yyvsp[(3) - (4)].d));
     ;}
     break;
 
   case 425:
-#line 4843 "Gmsh.y"
+#line 4845 "Gmsh.y"
     {
       (yyval.d) = treat_Struct_FullName_Float(NULL, (yyvsp[(1) - (4)].c), 2, (int)(yyvsp[(3) - (4)].d));
     ;}
     break;
 
   case 426:
-#line 4847 "Gmsh.y"
+#line 4849 "Gmsh.y"
     {
       (yyval.d) = treat_Struct_FullName_Float((yyvsp[(3) - (4)].c2).char1, (yyvsp[(3) - (4)].c2).char2, 1, 0, 0., 1);
     ;}
     break;
 
   case 427:
-#line 4851 "Gmsh.y"
+#line 4853 "Gmsh.y"
     {
       (yyval.d) = treat_Struct_FullName_dot_tSTRING_Float((yyvsp[(3) - (6)].c2).char1, (yyvsp[(3) - (6)].c2).char2, (yyvsp[(5) - (6)].c), 0, 0., 1);
     ;}
     break;
 
   case 428:
-#line 4855 "Gmsh.y"
+#line 4857 "Gmsh.y"
     {
       (yyval.d) = treat_Struct_FullName_Float((yyvsp[(3) - (5)].c2).char1, (yyvsp[(3) - (5)].c2).char2, 1, 0, (yyvsp[(4) - (5)].d), 2);
     ;}
     break;
 
   case 429:
-#line 4859 "Gmsh.y"
+#line 4861 "Gmsh.y"
     {
       (yyval.d) = treat_Struct_FullName_dot_tSTRING_Float((yyvsp[(3) - (7)].c2).char1, (yyvsp[(3) - (7)].c2).char2, (yyvsp[(5) - (7)].c), 0, (yyvsp[(6) - (7)].d), 2);
     ;}
     break;
 
   case 430:
-#line 4863 "Gmsh.y"
+#line 4865 "Gmsh.y"
     {
       (yyval.d) = treat_Struct_FullName_Float((yyvsp[(3) - (8)].c2).char1, (yyvsp[(3) - (8)].c2).char2, 2, (int)(yyvsp[(5) - (8)].d), (yyvsp[(7) - (8)].d), 2);
     ;}
     break;
 
   case 431:
-#line 4867 "Gmsh.y"
+#line 4869 "Gmsh.y"
     {
       (yyval.d) = treat_Struct_FullName_dot_tSTRING_Float((yyvsp[(3) - (10)].c2).char1, (yyvsp[(3) - (10)].c2).char2, (yyvsp[(5) - (10)].c), (int)(yyvsp[(7) - (10)].d), (yyvsp[(9) - (10)].d), 2);
     ;}
     break;
 
   case 432:
-#line 4871 "Gmsh.y"
+#line 4873 "Gmsh.y"
     {
       std::string tmp = FixRelativePath(gmsh_yyname, (yyvsp[(3) - (4)].c));
       (yyval.d) = !StatFile(tmp);
@@ -11766,7 +11768,7 @@ yyreduce:
     break;
 
   case 433:
-#line 4877 "Gmsh.y"
+#line 4879 "Gmsh.y"
     {
       if(gmsh_yysymbols.count((yyvsp[(2) - (4)].c))){
         gmsh_yysymbol &s(gmsh_yysymbols[(yyvsp[(2) - (4)].c)]);
@@ -11784,14 +11786,14 @@ yyreduce:
     break;
 
   case 434:
-#line 4893 "Gmsh.y"
+#line 4895 "Gmsh.y"
     {
       (yyval.d) = treat_Struct_FullName_dot_tSTRING_Float_getDim((yyvsp[(2) - (6)].c2).char1, (yyvsp[(2) - (6)].c2).char2, (yyvsp[(4) - (6)].c));
     ;}
     break;
 
   case 435:
-#line 4898 "Gmsh.y"
+#line 4900 "Gmsh.y"
     {
       std::string struct_namespace((yyvsp[(3) - (4)].c));
       (yyval.d) = (double)gmsh_yynamespaces[struct_namespace].size();
@@ -11800,7 +11802,7 @@ yyreduce:
     break;
 
   case 436:
-#line 4904 "Gmsh.y"
+#line 4906 "Gmsh.y"
     {
       std::string struct_namespace(std::string(""));
       (yyval.d) = (double)gmsh_yynamespaces[struct_namespace].size();
@@ -11808,7 +11810,7 @@ yyreduce:
     break;
 
   case 437:
-#line 4910 "Gmsh.y"
+#line 4912 "Gmsh.y"
     {
       if(!gmsh_yysymbols.count((yyvsp[(1) - (2)].c))){
 	yymsg(0, "Unknown variable '%s'", (yyvsp[(1) - (2)].c));
@@ -11830,7 +11832,7 @@ yyreduce:
     break;
 
   case 438:
-#line 4929 "Gmsh.y"
+#line 4931 "Gmsh.y"
     {
       int index = (int)(yyvsp[(3) - (5)].d);
       if(!gmsh_yysymbols.count((yyvsp[(1) - (5)].c))){
@@ -11853,7 +11855,7 @@ yyreduce:
     break;
 
   case 439:
-#line 4950 "Gmsh.y"
+#line 4952 "Gmsh.y"
     {
       int index = (int)(yyvsp[(3) - (5)].d);
       if(!gmsh_yysymbols.count((yyvsp[(1) - (5)].c))){
@@ -11876,49 +11878,49 @@ yyreduce:
     break;
 
   case 440:
-#line 4983 "Gmsh.y"
+#line 4985 "Gmsh.y"
     {
       (yyval.d) = treat_Struct_FullName_dot_tSTRING_Float(NULL, (yyvsp[(1) - (3)].c), (yyvsp[(3) - (3)].c));
     ;}
     break;
 
   case 441:
-#line 4987 "Gmsh.y"
+#line 4989 "Gmsh.y"
     {
       (yyval.d) = treat_Struct_FullName_dot_tSTRING_Float((yyvsp[(1) - (5)].c), (yyvsp[(3) - (5)].c), (yyvsp[(5) - (5)].c));
     ;}
     break;
 
   case 442:
-#line 4992 "Gmsh.y"
+#line 4994 "Gmsh.y"
     {
       (yyval.d) = treat_Struct_FullName_dot_tSTRING_Float(NULL, (yyvsp[(1) - (6)].c), (yyvsp[(3) - (6)].c), (int)(yyvsp[(5) - (6)].d));
     ;}
     break;
 
   case 443:
-#line 4996 "Gmsh.y"
+#line 4998 "Gmsh.y"
     {
       (yyval.d) = treat_Struct_FullName_dot_tSTRING_Float((yyvsp[(1) - (8)].c), (yyvsp[(3) - (8)].c), (yyvsp[(5) - (8)].c), (int)(yyvsp[(7) - (8)].d));
     ;}
     break;
 
   case 444:
-#line 5000 "Gmsh.y"
+#line 5002 "Gmsh.y"
     {
       (yyval.d) = treat_Struct_FullName_dot_tSTRING_Float(NULL, (yyvsp[(1) - (6)].c), (yyvsp[(3) - (6)].c), (int)(yyvsp[(5) - (6)].d));
     ;}
     break;
 
   case 445:
-#line 5004 "Gmsh.y"
+#line 5006 "Gmsh.y"
     {
       (yyval.d) = treat_Struct_FullName_dot_tSTRING_Float((yyvsp[(1) - (8)].c), (yyvsp[(3) - (8)].c), (yyvsp[(5) - (8)].c), (int)(yyvsp[(7) - (8)].d));
     ;}
     break;
 
   case 446:
-#line 5009 "Gmsh.y"
+#line 5011 "Gmsh.y"
     {
       NumberOption(GMSH_GET, (yyvsp[(1) - (6)].c), (int)(yyvsp[(3) - (6)].d), (yyvsp[(6) - (6)].c), (yyval.d));
       Free((yyvsp[(1) - (6)].c)); Free((yyvsp[(6) - (6)].c));
@@ -11926,7 +11928,7 @@ yyreduce:
     break;
 
   case 447:
-#line 5014 "Gmsh.y"
+#line 5016 "Gmsh.y"
     {
       double d = 0.;
       if(NumberOption(GMSH_GET, (yyvsp[(1) - (4)].c), 0, (yyvsp[(3) - (4)].c), d)){
@@ -11939,7 +11941,7 @@ yyreduce:
     break;
 
   case 448:
-#line 5024 "Gmsh.y"
+#line 5026 "Gmsh.y"
     {
       double d = 0.;
       if(NumberOption(GMSH_GET, (yyvsp[(1) - (7)].c), (int)(yyvsp[(3) - (7)].d), (yyvsp[(6) - (7)].c), d)){
@@ -11952,7 +11954,7 @@ yyreduce:
     break;
 
   case 449:
-#line 5034 "Gmsh.y"
+#line 5036 "Gmsh.y"
     {
       (yyval.d) = Msg::GetValue((yyvsp[(3) - (6)].c), (yyvsp[(5) - (6)].d));
       Free((yyvsp[(3) - (6)].c));
@@ -11960,7 +11962,7 @@ yyreduce:
     break;
 
   case 450:
-#line 5039 "Gmsh.y"
+#line 5041 "Gmsh.y"
     {
       int matches = 0;
       for(int i = 0; i < List_Nbr((yyvsp[(3) - (6)].l)); i++){
@@ -11974,7 +11976,7 @@ yyreduce:
     break;
 
   case 451:
-#line 5050 "Gmsh.y"
+#line 5052 "Gmsh.y"
     {
       std::string s((yyvsp[(3) - (6)].c)), substr((yyvsp[(5) - (6)].c));
       if(s.find(substr) != std::string::npos)
@@ -11986,7 +11988,7 @@ yyreduce:
     break;
 
   case 452:
-#line 5059 "Gmsh.y"
+#line 5061 "Gmsh.y"
     {
       (yyval.d) = strlen((yyvsp[(3) - (4)].c));
       Free((yyvsp[(3) - (4)].c));
@@ -11994,7 +11996,7 @@ yyreduce:
     break;
 
   case 453:
-#line 5064 "Gmsh.y"
+#line 5066 "Gmsh.y"
     {
       (yyval.d) = strcmp((yyvsp[(3) - (6)].c), (yyvsp[(5) - (6)].c));
       Free((yyvsp[(3) - (6)].c)); Free((yyvsp[(5) - (6)].c));
@@ -12002,7 +12004,7 @@ yyreduce:
     break;
 
   case 454:
-#line 5069 "Gmsh.y"
+#line 5071 "Gmsh.y"
     {
       int align = 0, font = 0, fontsize = CTX::instance()->glFontSize;
       if(List_Nbr((yyvsp[(3) - (4)].l)) % 2){
@@ -12029,27 +12031,27 @@ yyreduce:
     break;
 
   case 455:
-#line 5096 "Gmsh.y"
+#line 5098 "Gmsh.y"
     { (yyval.d) = 0.; ;}
     break;
 
   case 456:
-#line 5098 "Gmsh.y"
+#line 5100 "Gmsh.y"
     { (yyval.d) = (yyvsp[(2) - (2)].d);;}
     break;
 
   case 457:
-#line 5103 "Gmsh.y"
+#line 5105 "Gmsh.y"
     { (yyval.c) = NULL; ;}
     break;
 
   case 458:
-#line 5105 "Gmsh.y"
+#line 5107 "Gmsh.y"
     { (yyval.c) = (yyvsp[(2) - (2)].c);;}
     break;
 
   case 459:
-#line 5110 "Gmsh.y"
+#line 5112 "Gmsh.y"
     {
       std::string struct_namespace((yyvsp[(2) - (3)].c2).char1? (yyvsp[(2) - (3)].c2).char1 : std::string("")),
         struct_name((yyvsp[(2) - (3)].c2).char2);
@@ -12059,7 +12061,7 @@ yyreduce:
     break;
 
   case 460:
-#line 5117 "Gmsh.y"
+#line 5119 "Gmsh.y"
     {
       std::string struct_namespace((yyvsp[(2) - (7)].c2).char1? (yyvsp[(2) - (7)].c2).char1 : std::string("")),
         struct_name((yyvsp[(2) - (7)].c2).char2);
@@ -12075,105 +12077,105 @@ yyreduce:
     break;
 
   case 461:
-#line 5133 "Gmsh.y"
+#line 5135 "Gmsh.y"
     { (yyval.c2).char1 = NULL; (yyval.c2).char2 = (yyvsp[(1) - (1)].c); ;}
     break;
 
   case 462:
-#line 5135 "Gmsh.y"
+#line 5137 "Gmsh.y"
     { (yyval.c2).char1 = (yyvsp[(1) - (3)].c); (yyval.c2).char2 = (yyvsp[(3) - (3)].c); ;}
     break;
 
   case 463:
-#line 5140 "Gmsh.y"
+#line 5142 "Gmsh.y"
     { (yyval.c) = (yyvsp[(1) - (1)].c); flag_tSTRING_alloc = 1; ;}
     break;
 
   case 464:
-#line 5149 "Gmsh.y"
+#line 5151 "Gmsh.y"
     { (yyval.i) = 99; ;}
     break;
 
   case 465:
-#line 5151 "Gmsh.y"
+#line 5153 "Gmsh.y"
     { (yyval.i) = (int)(yyvsp[(2) - (2)].d); ;}
     break;
 
   case 466:
-#line 5156 "Gmsh.y"
+#line 5158 "Gmsh.y"
     { (yyval.i) = 0; ;}
     break;
 
   case 467:
-#line 5158 "Gmsh.y"
+#line 5160 "Gmsh.y"
     { (yyval.i) = (yyvsp[(2) - (3)].i); ;}
     break;
 
   case 468:
-#line 5163 "Gmsh.y"
+#line 5165 "Gmsh.y"
     {
       memcpy((yyval.v), (yyvsp[(1) - (1)].v), 5*sizeof(double));
     ;}
     break;
 
   case 469:
-#line 5167 "Gmsh.y"
+#line 5169 "Gmsh.y"
     {
       for(int i = 0; i < 5; i++) (yyval.v)[i] = -(yyvsp[(2) - (2)].v)[i];
     ;}
     break;
 
   case 470:
-#line 5171 "Gmsh.y"
+#line 5173 "Gmsh.y"
     {
       for(int i = 0; i < 5; i++) (yyval.v)[i] = (yyvsp[(2) - (2)].v)[i];
     ;}
     break;
 
   case 471:
-#line 5175 "Gmsh.y"
+#line 5177 "Gmsh.y"
     {
       for(int i = 0; i < 5; i++) (yyval.v)[i] = (yyvsp[(1) - (3)].v)[i] - (yyvsp[(3) - (3)].v)[i];
     ;}
     break;
 
   case 472:
-#line 5179 "Gmsh.y"
+#line 5181 "Gmsh.y"
     {
       for(int i = 0; i < 5; i++) (yyval.v)[i] = (yyvsp[(1) - (3)].v)[i] + (yyvsp[(3) - (3)].v)[i];
     ;}
     break;
 
   case 473:
-#line 5186 "Gmsh.y"
+#line 5188 "Gmsh.y"
     {
       (yyval.v)[0] = (yyvsp[(2) - (11)].d);  (yyval.v)[1] = (yyvsp[(4) - (11)].d);  (yyval.v)[2] = (yyvsp[(6) - (11)].d);  (yyval.v)[3] = (yyvsp[(8) - (11)].d); (yyval.v)[4] = (yyvsp[(10) - (11)].d);
     ;}
     break;
 
   case 474:
-#line 5190 "Gmsh.y"
+#line 5192 "Gmsh.y"
     {
       (yyval.v)[0] = (yyvsp[(2) - (9)].d);  (yyval.v)[1] = (yyvsp[(4) - (9)].d);  (yyval.v)[2] = (yyvsp[(6) - (9)].d);  (yyval.v)[3] = (yyvsp[(8) - (9)].d); (yyval.v)[4] = 1.0;
     ;}
     break;
 
   case 475:
-#line 5194 "Gmsh.y"
+#line 5196 "Gmsh.y"
     {
       (yyval.v)[0] = (yyvsp[(2) - (7)].d);  (yyval.v)[1] = (yyvsp[(4) - (7)].d);  (yyval.v)[2] = (yyvsp[(6) - (7)].d);  (yyval.v)[3] = 0.0; (yyval.v)[4] = 1.0;
     ;}
     break;
 
   case 476:
-#line 5198 "Gmsh.y"
+#line 5200 "Gmsh.y"
     {
       (yyval.v)[0] = (yyvsp[(2) - (7)].d);  (yyval.v)[1] = (yyvsp[(4) - (7)].d);  (yyval.v)[2] = (yyvsp[(6) - (7)].d);  (yyval.v)[3] = 0.0; (yyval.v)[4] = 1.0;
     ;}
     break;
 
   case 477:
-#line 5205 "Gmsh.y"
+#line 5207 "Gmsh.y"
     {
       (yyval.l) = List_Create(2, 1, sizeof(List_T*));
       List_Add((yyval.l), &((yyvsp[(1) - (1)].l)));
@@ -12181,14 +12183,14 @@ yyreduce:
     break;
 
   case 478:
-#line 5210 "Gmsh.y"
+#line 5212 "Gmsh.y"
     {
       List_Add((yyval.l), &((yyvsp[(3) - (3)].l)));
     ;}
     break;
 
   case 479:
-#line 5217 "Gmsh.y"
+#line 5219 "Gmsh.y"
     {
       (yyval.l) = List_Create(2, 1, sizeof(double));
       List_Add((yyval.l), &((yyvsp[(1) - (1)].d)));
@@ -12196,14 +12198,14 @@ yyreduce:
     break;
 
   case 480:
-#line 5222 "Gmsh.y"
+#line 5224 "Gmsh.y"
     {
       (yyval.l) = (yyvsp[(1) - (1)].l);
     ;}
     break;
 
   case 481:
-#line 5226 "Gmsh.y"
+#line 5228 "Gmsh.y"
     {
       // creates an empty list
       (yyval.l) = List_Create(2, 1, sizeof(double));
@@ -12211,14 +12213,14 @@ yyreduce:
     break;
 
   case 482:
-#line 5231 "Gmsh.y"
+#line 5233 "Gmsh.y"
     {
       (yyval.l) = (yyvsp[(2) - (3)].l);
     ;}
     break;
 
   case 483:
-#line 5235 "Gmsh.y"
+#line 5237 "Gmsh.y"
     {
       (yyval.l) = (yyvsp[(3) - (4)].l);
       for(int i = 0; i < List_Nbr((yyval.l)); i++){
@@ -12229,7 +12231,7 @@ yyreduce:
     break;
 
   case 484:
-#line 5243 "Gmsh.y"
+#line 5245 "Gmsh.y"
     {
       (yyval.l) = (yyvsp[(4) - (5)].l);
       for(int i = 0; i < List_Nbr((yyval.l)); i++){
@@ -12240,14 +12242,14 @@ yyreduce:
     break;
 
   case 485:
-#line 5254 "Gmsh.y"
+#line 5256 "Gmsh.y"
     {
       (yyval.l) = (yyvsp[(1) - (1)].l);
     ;}
     break;
 
   case 486:
-#line 5258 "Gmsh.y"
+#line 5260 "Gmsh.y"
     {
       if(!strcmp((yyvsp[(1) - (1)].c), "*") || !strcmp((yyvsp[(1) - (1)].c), "all"))
         (yyval.l) = 0;
@@ -12259,7 +12261,7 @@ yyreduce:
     break;
 
   case 487:
-#line 5270 "Gmsh.y"
+#line 5272 "Gmsh.y"
     {
       (yyval.l) = (yyvsp[(2) - (2)].l);
       for(int i = 0; i < List_Nbr((yyval.l)); i++){
@@ -12270,7 +12272,7 @@ yyreduce:
     break;
 
   case 488:
-#line 5278 "Gmsh.y"
+#line 5280 "Gmsh.y"
     {
       (yyval.l) = (yyvsp[(3) - (3)].l);
       for(int i = 0; i < List_Nbr((yyval.l)); i++){
@@ -12281,7 +12283,7 @@ yyreduce:
     break;
 
   case 489:
-#line 5286 "Gmsh.y"
+#line 5288 "Gmsh.y"
     {
       (yyval.l) = List_Create(2, 1, sizeof(double));
       for(double d = (yyvsp[(1) - (3)].d); ((yyvsp[(1) - (3)].d) < (yyvsp[(3) - (3)].d)) ? (d <= (yyvsp[(3) - (3)].d)) : (d >= (yyvsp[(3) - (3)].d));
@@ -12291,7 +12293,7 @@ yyreduce:
     break;
 
   case 490:
-#line 5293 "Gmsh.y"
+#line 5295 "Gmsh.y"
     {
       (yyval.l) = List_Create(2, 1, sizeof(double));
       if(!(yyvsp[(5) - (5)].d)){  //|| ($1 < $3 && $5 < 0) || ($1 > $3 && $5 > 0)
@@ -12304,7 +12306,7 @@ yyreduce:
     break;
 
   case 491:
-#line 5303 "Gmsh.y"
+#line 5305 "Gmsh.y"
     {
       (yyval.l) = List_Create(3, 1, sizeof(double));
       int tag = (int)(yyvsp[(3) - (4)].d);
@@ -12330,7 +12332,7 @@ yyreduce:
     break;
 
   case 492:
-#line 5326 "Gmsh.y"
+#line 5328 "Gmsh.y"
     {
       (yyval.l) = List_Create(10, 10, sizeof(double));
       getAllElementaryTags((yyvsp[(1) - (2)].i), (yyval.l));
@@ -12339,7 +12341,7 @@ yyreduce:
     break;
 
   case 493:
-#line 5332 "Gmsh.y"
+#line 5334 "Gmsh.y"
     {
       (yyval.l) = List_Create(10, 10, sizeof(double));
       if(!(yyvsp[(3) - (3)].l)){
@@ -12353,7 +12355,7 @@ yyreduce:
     break;
 
   case 494:
-#line 5344 "Gmsh.y"
+#line 5346 "Gmsh.y"
     {
       (yyval.l) = List_Create(10, 10, sizeof(double));
       getElementaryTagsInBoundingBox((yyvsp[(1) - (16)].i), (yyvsp[(5) - (16)].d), (yyvsp[(7) - (16)].d), (yyvsp[(9) - (16)].d), (yyvsp[(11) - (16)].d), (yyvsp[(13) - (16)].d), (yyvsp[(15) - (16)].d), (yyval.l));
@@ -12361,7 +12363,7 @@ yyreduce:
     break;
 
   case 495:
-#line 5349 "Gmsh.y"
+#line 5351 "Gmsh.y"
     {
       (yyval.l) = List_Create(List_Nbr((yyvsp[(1) - (1)].l)), 1, sizeof(double));
       for(int i = 0; i < List_Nbr((yyvsp[(1) - (1)].l)); i++){
@@ -12374,7 +12376,7 @@ yyreduce:
     break;
 
   case 496:
-#line 5359 "Gmsh.y"
+#line 5361 "Gmsh.y"
     {
       (yyval.l) = List_Create(List_Nbr((yyvsp[(1) - (1)].l)), 1, sizeof(double));
       for(int i = 0; i < List_Nbr((yyvsp[(1) - (1)].l)); i++){
@@ -12387,7 +12389,7 @@ yyreduce:
     break;
 
   case 497:
-#line 5369 "Gmsh.y"
+#line 5371 "Gmsh.y"
     {
       (yyval.l) = List_Create(List_Nbr((yyvsp[(1) - (1)].l)), 1, sizeof(double));
       for(int i = 0; i < List_Nbr((yyvsp[(1) - (1)].l)); i++){
@@ -12400,7 +12402,7 @@ yyreduce:
     break;
 
   case 498:
-#line 5379 "Gmsh.y"
+#line 5381 "Gmsh.y"
     {
       (yyval.l) = List_Create(20, 20, sizeof(double));
       if(!gmsh_yysymbols.count((yyvsp[(1) - (3)].c)))
@@ -12415,21 +12417,21 @@ yyreduce:
     break;
 
   case 499:
-#line 5391 "Gmsh.y"
+#line 5393 "Gmsh.y"
     {
       (yyval.l) = treat_Struct_FullName_dot_tSTRING_ListOfFloat(NULL, (yyvsp[(1) - (5)].c), (yyvsp[(3) - (5)].c));
     ;}
     break;
 
   case 500:
-#line 5395 "Gmsh.y"
+#line 5397 "Gmsh.y"
     {
       (yyval.l) = treat_Struct_FullName_dot_tSTRING_ListOfFloat((yyvsp[(1) - (7)].c), (yyvsp[(3) - (7)].c), (yyvsp[(5) - (7)].c));
     ;}
     break;
 
   case 501:
-#line 5400 "Gmsh.y"
+#line 5402 "Gmsh.y"
     {
       (yyval.l) = List_Create(2, 1, sizeof(double));
       if(!gmsh_yysymbols.count((yyvsp[(3) - (4)].c)))
@@ -12444,28 +12446,28 @@ yyreduce:
     break;
 
   case 502:
-#line 5412 "Gmsh.y"
+#line 5414 "Gmsh.y"
     {
       (yyval.l) = (yyvsp[(3) - (4)].l);
     ;}
     break;
 
   case 503:
-#line 5416 "Gmsh.y"
+#line 5418 "Gmsh.y"
     {
       (yyval.l) = (yyvsp[(3) - (4)].l);
     ;}
     break;
 
   case 504:
-#line 5420 "Gmsh.y"
+#line 5422 "Gmsh.y"
     {
       (yyval.l) = (yyvsp[(4) - (6)].l);
     ;}
     break;
 
   case 505:
-#line 5424 "Gmsh.y"
+#line 5426 "Gmsh.y"
     {
       (yyval.l) = List_Create(2, 1, sizeof(double));
       if(!gmsh_yysymbols.count((yyvsp[(1) - (6)].c)))
@@ -12486,7 +12488,7 @@ yyreduce:
     break;
 
   case 506:
-#line 5442 "Gmsh.y"
+#line 5444 "Gmsh.y"
     {
       (yyval.l) = List_Create(20,20,sizeof(double));
       for(int i = 0; i < (int)(yyvsp[(7) - (8)].d); i++) {
@@ -12497,7 +12499,7 @@ yyreduce:
     break;
 
   case 507:
-#line 5450 "Gmsh.y"
+#line 5452 "Gmsh.y"
     {
       (yyval.l) = List_Create(20,20,sizeof(double));
       for(int i = 0; i < (int)(yyvsp[(7) - (8)].d); i++) {
@@ -12508,7 +12510,7 @@ yyreduce:
     break;
 
   case 508:
-#line 5458 "Gmsh.y"
+#line 5460 "Gmsh.y"
     {
       Msg::Barrier();
       FILE *File;
@@ -12540,7 +12542,7 @@ yyreduce:
     break;
 
   case 509:
-#line 5487 "Gmsh.y"
+#line 5489 "Gmsh.y"
     {
       double x0 = (yyvsp[(3) - (14)].d), x1 = (yyvsp[(5) - (14)].d), y0 = (yyvsp[(7) - (14)].d), y1 = (yyvsp[(9) - (14)].d), ys = (yyvsp[(11) - (14)].d);
       int N = (int)(yyvsp[(13) - (14)].d);
@@ -12553,7 +12555,7 @@ yyreduce:
     break;
 
   case 510:
-#line 5497 "Gmsh.y"
+#line 5499 "Gmsh.y"
     {
       std::vector<double> tmp;
       for(int i = 0; i < List_Nbr((yyvsp[(3) - (4)].l)); i++){
@@ -12572,7 +12574,7 @@ yyreduce:
     break;
 
   case 511:
-#line 5513 "Gmsh.y"
+#line 5515 "Gmsh.y"
     {
       for(int i = 0; i < List_Nbr((yyvsp[(3) - (4)].l)); i++){
         double *d = (double*)List_Pointer((yyvsp[(3) - (4)].l), i);
@@ -12583,7 +12585,7 @@ yyreduce:
     break;
 
   case 512:
-#line 5524 "Gmsh.y"
+#line 5526 "Gmsh.y"
     {
       (yyval.l) = List_Create(2, 1, sizeof(double));
       List_Add((yyval.l), &((yyvsp[(1) - (1)].d)));
@@ -12591,21 +12593,21 @@ yyreduce:
     break;
 
   case 513:
-#line 5529 "Gmsh.y"
+#line 5531 "Gmsh.y"
     {
       (yyval.l) = (yyvsp[(1) - (1)].l);
     ;}
     break;
 
   case 514:
-#line 5533 "Gmsh.y"
+#line 5535 "Gmsh.y"
     {
       List_Add((yyval.l), &((yyvsp[(3) - (3)].d)));
     ;}
     break;
 
   case 515:
-#line 5537 "Gmsh.y"
+#line 5539 "Gmsh.y"
     {
       for(int i = 0; i < List_Nbr((yyvsp[(3) - (3)].l)); i++){
 	double d;
@@ -12617,21 +12619,21 @@ yyreduce:
     break;
 
   case 516:
-#line 5549 "Gmsh.y"
+#line 5551 "Gmsh.y"
     {
       (yyval.u) = CTX::instance()->packColor((int)(yyvsp[(2) - (9)].d), (int)(yyvsp[(4) - (9)].d), (int)(yyvsp[(6) - (9)].d), (int)(yyvsp[(8) - (9)].d));
     ;}
     break;
 
   case 517:
-#line 5553 "Gmsh.y"
+#line 5555 "Gmsh.y"
     {
       (yyval.u) = CTX::instance()->packColor((int)(yyvsp[(2) - (7)].d), (int)(yyvsp[(4) - (7)].d), (int)(yyvsp[(6) - (7)].d), 255);
     ;}
     break;
 
   case 518:
-#line 5565 "Gmsh.y"
+#line 5567 "Gmsh.y"
     {
       int flag = 0;
       if(gmsh_yystringsymbols.count((yyvsp[(1) - (1)].c))){
@@ -12651,7 +12653,7 @@ yyreduce:
     break;
 
   case 519:
-#line 5582 "Gmsh.y"
+#line 5584 "Gmsh.y"
     {
       unsigned int val = 0;
       ColorOption(GMSH_GET, (yyvsp[(1) - (5)].c), 0, (yyvsp[(5) - (5)].c), val);
@@ -12661,14 +12663,14 @@ yyreduce:
     break;
 
   case 520:
-#line 5592 "Gmsh.y"
+#line 5594 "Gmsh.y"
     {
       (yyval.l) = (yyvsp[(2) - (3)].l);
     ;}
     break;
 
   case 521:
-#line 5596 "Gmsh.y"
+#line 5598 "Gmsh.y"
     {
       (yyval.l) = List_Create(256, 10, sizeof(unsigned int));
       GmshColorTable *ct = GetColorTable((int)(yyvsp[(3) - (6)].d));
@@ -12683,7 +12685,7 @@ yyreduce:
     break;
 
   case 522:
-#line 5611 "Gmsh.y"
+#line 5613 "Gmsh.y"
     {
       (yyval.l) = List_Create(256, 10, sizeof(unsigned int));
       List_Add((yyval.l), &((yyvsp[(1) - (1)].u)));
@@ -12691,21 +12693,21 @@ yyreduce:
     break;
 
   case 523:
-#line 5616 "Gmsh.y"
+#line 5618 "Gmsh.y"
     {
       List_Add((yyval.l), &((yyvsp[(3) - (3)].u)));
     ;}
     break;
 
   case 524:
-#line 5623 "Gmsh.y"
+#line 5625 "Gmsh.y"
     {
       (yyval.c) = (yyvsp[(1) - (1)].c);
     ;}
     break;
 
   case 525:
-#line 5627 "Gmsh.y"
+#line 5629 "Gmsh.y"
     {
       // No need to extend to Struct_FullName (a Tag is not a String)
       (yyval.c) = treat_Struct_FullName_String(NULL, (yyvsp[(1) - (1)].c));
@@ -12713,7 +12715,7 @@ yyreduce:
     break;
 
   case 526:
-#line 5632 "Gmsh.y"
+#line 5634 "Gmsh.y"
     {
       std::string val;
       int j = (int)(yyvsp[(3) - (4)].d);
@@ -12730,7 +12732,7 @@ yyreduce:
     break;
 
   case 527:
-#line 5646 "Gmsh.y"
+#line 5648 "Gmsh.y"
     {
       std::string val;
       int j = (int)(yyvsp[(3) - (4)].d);
@@ -12747,35 +12749,35 @@ yyreduce:
     break;
 
   case 528:
-#line 5662 "Gmsh.y"
+#line 5664 "Gmsh.y"
     {
       (yyval.c) = treat_Struct_FullName_dot_tSTRING_String(NULL, (yyvsp[(1) - (3)].c), (yyvsp[(3) - (3)].c));
     ;}
     break;
 
   case 529:
-#line 5666 "Gmsh.y"
+#line 5668 "Gmsh.y"
     {
       (yyval.c) = treat_Struct_FullName_dot_tSTRING_String((yyvsp[(1) - (5)].c), (yyvsp[(3) - (5)].c), (yyvsp[(5) - (5)].c));
     ;}
     break;
 
   case 530:
-#line 5670 "Gmsh.y"
+#line 5672 "Gmsh.y"
     {
       (yyval.c) = treat_Struct_FullName_dot_tSTRING_String(NULL, (yyvsp[(1) - (6)].c), (yyvsp[(3) - (6)].c), (int)(yyvsp[(5) - (6)].d));
     ;}
     break;
 
   case 531:
-#line 5674 "Gmsh.y"
+#line 5676 "Gmsh.y"
     {
       (yyval.c) = treat_Struct_FullName_dot_tSTRING_String((yyvsp[(1) - (8)].c), (yyvsp[(3) - (8)].c), (yyvsp[(5) - (8)].c), (int)(yyvsp[(7) - (8)].d));
     ;}
     break;
 
   case 532:
-#line 5678 "Gmsh.y"
+#line 5680 "Gmsh.y"
     {
       std::string out;
       StringOption(GMSH_GET, (yyvsp[(1) - (6)].c), (int)(yyvsp[(3) - (6)].d), (yyvsp[(6) - (6)].c), out);
@@ -12786,7 +12788,7 @@ yyreduce:
     break;
 
   case 533:
-#line 5686 "Gmsh.y"
+#line 5688 "Gmsh.y"
     {
       std::string name = GModel::current()->getPhysicalName(0, (int)(yyvsp[(4) - (5)].d));
       (yyval.c) = (char*)Malloc((name.size() + 1) * sizeof(char));
@@ -12795,7 +12797,7 @@ yyreduce:
     break;
 
   case 534:
-#line 5692 "Gmsh.y"
+#line 5694 "Gmsh.y"
     {
       std::string name = GModel::current()->getPhysicalName(1, (int)(yyvsp[(4) - (5)].d));
       (yyval.c) = (char*)Malloc((name.size() + 1) * sizeof(char));
@@ -12804,7 +12806,7 @@ yyreduce:
     break;
 
   case 535:
-#line 5698 "Gmsh.y"
+#line 5700 "Gmsh.y"
     {
       std::string name = GModel::current()->getPhysicalName(2, (int)(yyvsp[(4) - (5)].d));
       (yyval.c) = (char*)Malloc((name.size() + 1) * sizeof(char));
@@ -12813,7 +12815,7 @@ yyreduce:
     break;
 
   case 536:
-#line 5704 "Gmsh.y"
+#line 5706 "Gmsh.y"
     {
       std::string name = GModel::current()->getPhysicalName(3, (int)(yyvsp[(4) - (5)].d));
       (yyval.c) = (char*)Malloc((name.size() + 1) * sizeof(char));
@@ -12822,21 +12824,21 @@ yyreduce:
     break;
 
   case 537:
-#line 5713 "Gmsh.y"
+#line 5715 "Gmsh.y"
     {
       (yyval.c) = (yyvsp[(1) - (1)].c);
     ;}
     break;
 
   case 538:
-#line 5717 "Gmsh.y"
+#line 5719 "Gmsh.y"
     {
       (yyval.c) = (yyvsp[(3) - (4)].c);
     ;}
     break;
 
   case 539:
-#line 5721 "Gmsh.y"
+#line 5723 "Gmsh.y"
     {
       (yyval.c) = (char *)Malloc(32 * sizeof(char));
       time_t now;
@@ -12847,7 +12849,7 @@ yyreduce:
     break;
 
   case 540:
-#line 5729 "Gmsh.y"
+#line 5731 "Gmsh.y"
     {
       std::string exe = Msg::GetExecutableName();
       (yyval.c) = (char *)Malloc(exe.size() + 1);
@@ -12856,7 +12858,7 @@ yyreduce:
     break;
 
   case 541:
-#line 5735 "Gmsh.y"
+#line 5737 "Gmsh.y"
     {
       std::string action = Msg::GetOnelabAction();
       (yyval.c) = (char *)Malloc(action.size() + 1);
@@ -12865,14 +12867,14 @@ yyreduce:
     break;
 
   case 542:
-#line 5741 "Gmsh.y"
+#line 5743 "Gmsh.y"
     {
       (yyval.c) = strsave((char*)"Gmsh");
     ;}
     break;
 
   case 543:
-#line 5745 "Gmsh.y"
+#line 5747 "Gmsh.y"
     {
       const char *env = GetEnvironmentVar((yyvsp[(3) - (4)].c));
       if(!env) env = "";
@@ -12883,7 +12885,7 @@ yyreduce:
     break;
 
   case 544:
-#line 5753 "Gmsh.y"
+#line 5755 "Gmsh.y"
     {
       std::string s = Msg::GetString((yyvsp[(3) - (6)].c), (yyvsp[(5) - (6)].c));
       (yyval.c) = (char *)Malloc((s.size() + 1) * sizeof(char));
@@ -12894,7 +12896,7 @@ yyreduce:
     break;
 
   case 545:
-#line 5761 "Gmsh.y"
+#line 5763 "Gmsh.y"
     {
       std::string s = Msg::GetOnelabString((yyvsp[(3) - (4)].c));
       (yyval.c) = (char *)Malloc((s.size() + 1) * sizeof(char));
@@ -12904,7 +12906,7 @@ yyreduce:
     break;
 
   case 546:
-#line 5768 "Gmsh.y"
+#line 5770 "Gmsh.y"
     {
       std::string s = Msg::GetOnelabString((yyvsp[(3) - (6)].c), (yyvsp[(5) - (6)].c));
       (yyval.c) = (char *)Malloc((s.size() + 1) * sizeof(char));
@@ -12915,21 +12917,21 @@ yyreduce:
     break;
 
   case 547:
-#line 5777 "Gmsh.y"
+#line 5779 "Gmsh.y"
     {
       (yyval.c) = treat_Struct_FullName_String(NULL, (yyvsp[(3) - (5)].c2).char2, 1, 0, (yyvsp[(4) - (5)].c), 2);
     ;}
     break;
 
   case 548:
-#line 5781 "Gmsh.y"
+#line 5783 "Gmsh.y"
     {
       (yyval.c) = treat_Struct_FullName_dot_tSTRING_String((yyvsp[(3) - (7)].c2).char1, (yyvsp[(3) - (7)].c2).char2, (yyvsp[(5) - (7)].c), 0, (yyvsp[(6) - (7)].c), 2);
     ;}
     break;
 
   case 549:
-#line 5785 "Gmsh.y"
+#line 5787 "Gmsh.y"
     {
       int size = 1;
       for(int i = 0; i < List_Nbr((yyvsp[(3) - (4)].l)); i++)
@@ -12947,7 +12949,7 @@ yyreduce:
     break;
 
   case 550:
-#line 5800 "Gmsh.y"
+#line 5802 "Gmsh.y"
     {
       (yyval.c) = (char *)Malloc((strlen((yyvsp[(3) - (4)].c)) + 1) * sizeof(char));
       int i;
@@ -12964,7 +12966,7 @@ yyreduce:
     break;
 
   case 551:
-#line 5814 "Gmsh.y"
+#line 5816 "Gmsh.y"
     {
       (yyval.c) = (char *)Malloc((strlen((yyvsp[(3) - (4)].c)) + 1) * sizeof(char));
       int i;
@@ -12981,7 +12983,7 @@ yyreduce:
     break;
 
   case 552:
-#line 5828 "Gmsh.y"
+#line 5830 "Gmsh.y"
     {
       std::string input = (yyvsp[(3) - (8)].c);
       std::string substr_old = (yyvsp[(5) - (8)].c);
@@ -12996,7 +12998,7 @@ yyreduce:
     break;
 
   case 553:
-#line 5840 "Gmsh.y"
+#line 5842 "Gmsh.y"
     {
       int size = 1;
       for(int i = 0; i < List_Nbr((yyvsp[(3) - (4)].l)); i++)
@@ -13015,7 +13017,7 @@ yyreduce:
     break;
 
   case 554:
-#line 5856 "Gmsh.y"
+#line 5858 "Gmsh.y"
     {
       int i = 0;
       while ((yyvsp[(3) - (4)].c)[i]) {
@@ -13027,7 +13029,7 @@ yyreduce:
     break;
 
   case 555:
-#line 5865 "Gmsh.y"
+#line 5867 "Gmsh.y"
     {
       int i = 0;
       while ((yyvsp[(3) - (4)].c)[i]) {
@@ -13039,7 +13041,7 @@ yyreduce:
     break;
 
   case 556:
-#line 5874 "Gmsh.y"
+#line 5876 "Gmsh.y"
     {
       int i = 0;
       while ((yyvsp[(3) - (4)].c)[i]) {
@@ -13052,7 +13054,7 @@ yyreduce:
     break;
 
   case 557:
-#line 5884 "Gmsh.y"
+#line 5886 "Gmsh.y"
     {
       if((yyvsp[(3) - (8)].d)){
         (yyval.c) = (yyvsp[(5) - (8)].c);
@@ -13066,7 +13068,7 @@ yyreduce:
     break;
 
   case 558:
-#line 5895 "Gmsh.y"
+#line 5897 "Gmsh.y"
     {
       std::string in = (yyvsp[(3) - (8)].c);
       std::string out = in.substr((int)(yyvsp[(5) - (8)].d), (int)(yyvsp[(7) - (8)].d));
@@ -13077,7 +13079,7 @@ yyreduce:
     break;
 
   case 559:
-#line 5903 "Gmsh.y"
+#line 5905 "Gmsh.y"
     {
       std::string in = (yyvsp[(3) - (6)].c);
       std::string out = in.substr((int)(yyvsp[(5) - (6)].d), std::string::npos);
@@ -13088,14 +13090,14 @@ yyreduce:
     break;
 
   case 560:
-#line 5911 "Gmsh.y"
+#line 5913 "Gmsh.y"
     {
       (yyval.c) = (yyvsp[(3) - (4)].c);
     ;}
     break;
 
   case 561:
-#line 5915 "Gmsh.y"
+#line 5917 "Gmsh.y"
     {
       char tmpstring[5000];
       int i = printListOfDouble((yyvsp[(3) - (6)].c), (yyvsp[(5) - (6)].l), tmpstring);
@@ -13117,7 +13119,7 @@ yyreduce:
     break;
 
   case 562:
-#line 5934 "Gmsh.y"
+#line 5936 "Gmsh.y"
     {
       std::string tmp = FixRelativePath(gmsh_yyname, (yyvsp[(3) - (4)].c));
       (yyval.c) = (char*)Malloc((tmp.size() + 1) * sizeof(char));
@@ -13127,7 +13129,7 @@ yyreduce:
     break;
 
   case 563:
-#line 5941 "Gmsh.y"
+#line 5943 "Gmsh.y"
     {
       std::string tmp = SplitFileName(GetAbsolutePath(gmsh_yyname))[0];
       (yyval.c) = (char*)Malloc((tmp.size() + 1) * sizeof(char));
@@ -13136,7 +13138,7 @@ yyreduce:
     break;
 
   case 564:
-#line 5947 "Gmsh.y"
+#line 5949 "Gmsh.y"
     {
       std::string tmp = SplitFileName((yyvsp[(3) - (4)].c))[0];
       (yyval.c) = (char*)Malloc((tmp.size() + 1) * sizeof(char));
@@ -13146,7 +13148,7 @@ yyreduce:
     break;
 
   case 565:
-#line 5954 "Gmsh.y"
+#line 5956 "Gmsh.y"
     {
       std::string tmp = GetAbsolutePath((yyvsp[(3) - (4)].c));
       (yyval.c) = (char*)Malloc((tmp.size() + 1) * sizeof(char));
@@ -13156,12 +13158,12 @@ yyreduce:
     break;
 
   case 566:
-#line 5961 "Gmsh.y"
+#line 5963 "Gmsh.y"
     { init_options(); ;}
     break;
 
   case 567:
-#line 5963 "Gmsh.y"
+#line 5965 "Gmsh.y"
     {
       std::string val((yyvsp[(3) - (6)].c));
       Msg::ExchangeOnelabParameter("", val, floatOptions, charOptions);
@@ -13172,7 +13174,7 @@ yyreduce:
     break;
 
   case 568:
-#line 5971 "Gmsh.y"
+#line 5973 "Gmsh.y"
     {
       std::string out;
       const std::string * key_struct = NULL;
@@ -13196,37 +13198,37 @@ yyreduce:
     break;
 
   case 569:
-#line 5995 "Gmsh.y"
+#line 5997 "Gmsh.y"
     { struct_namespace = std::string(""); (yyval.d) = (yyvsp[(2) - (2)].d); ;}
     break;
 
   case 570:
-#line 5997 "Gmsh.y"
+#line 5999 "Gmsh.y"
     { struct_namespace = (yyvsp[(1) - (4)].c); Free((yyvsp[(1) - (4)].c)); (yyval.d) = (yyvsp[(4) - (4)].d); ;}
     break;
 
   case 571:
-#line 6003 "Gmsh.y"
+#line 6005 "Gmsh.y"
     { (yyval.l) = (yyvsp[(3) - (4)].l); ;}
     break;
 
   case 572:
-#line 6008 "Gmsh.y"
+#line 6010 "Gmsh.y"
     { (yyval.l) = (yyvsp[(1) - (1)].l); ;}
     break;
 
   case 573:
-#line 6010 "Gmsh.y"
+#line 6012 "Gmsh.y"
     { (yyval.l) = (yyvsp[(1) - (1)].l); ;}
     break;
 
   case 574:
-#line 6015 "Gmsh.y"
+#line 6017 "Gmsh.y"
     { (yyval.l) = (yyvsp[(2) - (3)].l); ;}
     break;
 
   case 575:
-#line 6020 "Gmsh.y"
+#line 6022 "Gmsh.y"
     {
       (yyval.l) = List_Create(20,20,sizeof(char*));
       List_Add((yyval.l), &((yyvsp[(1) - (1)].c)));
@@ -13234,19 +13236,19 @@ yyreduce:
     break;
 
   case 576:
-#line 6025 "Gmsh.y"
+#line 6027 "Gmsh.y"
     { (yyval.l) = (yyvsp[(1) - (1)].l); ;}
     break;
 
   case 577:
-#line 6027 "Gmsh.y"
+#line 6029 "Gmsh.y"
     {
       List_Add((yyval.l), &((yyvsp[(3) - (3)].c)));
     ;}
     break;
 
   case 578:
-#line 6031 "Gmsh.y"
+#line 6033 "Gmsh.y"
     {
       for(int i = 0; i < List_Nbr((yyvsp[(3) - (3)].l)); i++){
 	char* c;
@@ -13258,7 +13260,7 @@ yyreduce:
     break;
 
   case 579:
-#line 6043 "Gmsh.y"
+#line 6045 "Gmsh.y"
     {
       (yyval.l) = List_Create(20, 20, sizeof(char *));
       if(!gmsh_yystringsymbols.count((yyvsp[(1) - (3)].c)))
@@ -13275,21 +13277,21 @@ yyreduce:
     break;
 
   case 580:
-#line 6057 "Gmsh.y"
+#line 6059 "Gmsh.y"
     {
       (yyval.l) = treat_Struct_FullName_dot_tSTRING_ListOfString(NULL, (yyvsp[(1) - (5)].c), (yyvsp[(3) - (5)].c));
     ;}
     break;
 
   case 581:
-#line 6061 "Gmsh.y"
+#line 6063 "Gmsh.y"
     {
       (yyval.l) = treat_Struct_FullName_dot_tSTRING_ListOfString((yyvsp[(1) - (7)].c), (yyvsp[(3) - (7)].c), (yyvsp[(5) - (7)].c));
     ;}
     break;
 
   case 582:
-#line 6068 "Gmsh.y"
+#line 6070 "Gmsh.y"
     {
       char tmpstr[256];
       sprintf(tmpstr, "_%d", (int)(yyvsp[(4) - (5)].d));
@@ -13300,7 +13302,7 @@ yyreduce:
     break;
 
   case 583:
-#line 6076 "Gmsh.y"
+#line 6078 "Gmsh.y"
     {
       char tmpstr[256];
       sprintf(tmpstr, "_%d", (int)(yyvsp[(4) - (5)].d));
@@ -13311,7 +13313,7 @@ yyreduce:
     break;
 
   case 584:
-#line 6084 "Gmsh.y"
+#line 6086 "Gmsh.y"
     {
       char tmpstr[256];
       sprintf(tmpstr, "_%d", (int)(yyvsp[(7) - (8)].d));
@@ -13322,23 +13324,23 @@ yyreduce:
     break;
 
   case 585:
-#line 6095 "Gmsh.y"
+#line 6097 "Gmsh.y"
     { (yyval.c) = (yyvsp[(1) - (1)].c); ;}
     break;
 
   case 586:
-#line 6097 "Gmsh.y"
+#line 6099 "Gmsh.y"
     { (yyval.c) = (yyvsp[(1) - (1)].c); ;}
     break;
 
   case 587:
-#line 6100 "Gmsh.y"
+#line 6102 "Gmsh.y"
     { (yyval.c) = (yyvsp[(3) - (4)].c); ;}
     break;
 
 
 /* Line 1267 of yacc.c.  */
-#line 13342 "Gmsh.tab.cpp"
+#line 13344 "Gmsh.tab.cpp"
       default: break;
     }
   YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
@@ -13552,7 +13554,7 @@ yyreturn:
 }
 
 
-#line 6103 "Gmsh.y"
+#line 6105 "Gmsh.y"
 
 
 void assignVariable(const std::string &name, int index, int assignType,
diff --git a/Parser/Gmsh.y b/Parser/Gmsh.y
index 4c50dcd5faffad144709786a41ee914cd7df41cc..f733ad39e9a82a6640c034be1e7e041da9abd479 100644
--- a/Parser/Gmsh.y
+++ b/Parser/Gmsh.y
@@ -3941,13 +3941,14 @@ Boolean :
       bool r = true;
       if(gmsh_yyfactory == "OpenCASCADE" && GModel::current()->getOCCInternals()){
         std::vector<std::pair<int, int > > object, tool, out;
+        std::vector<std::vector<std::pair<int, int > > > outMap;
         ListOfShapes2VectorOfPairs($3, object);
         ListOfShapes2VectorOfPairs($7, tool);
         // currently we don't distinguish between Delete and Recursive Delete:
         // we always delete recursively. Let us know if you have examples where
         // having the choice would be interesting
         r = GModel::current()->getOCCInternals()->booleanOperator
-          (-1, (OCC_Internals::BooleanOperator)$1, object, tool, out, $4, $8);
+          (-1, (OCC_Internals::BooleanOperator)$1, object, tool, out, outMap, $4, $8);
         VectorOfPairs2ListOfShapes(out, $$);
       }
       else{
@@ -3982,13 +3983,14 @@ BooleanShape :
       bool r = true;
       if(gmsh_yyfactory == "OpenCASCADE" && GModel::current()->getOCCInternals()){
         std::vector<std::pair<int, int> > object, tool, out;
+        std::vector<std::vector<std::pair<int, int > > > outMap;
         ListOfShapes2VectorOfPairs($7, object);
         ListOfShapes2VectorOfPairs($11, tool);
         // currently we don't distinguish between Delete and Recursive Delete:
         // we always delete recursively. Let us know if you have examples where
         // having the choice would be interesting
         r = GModel::current()->getOCCInternals()->booleanOperator
-          ((int)$3, (OCC_Internals::BooleanOperator)$1, object, tool, out, $8, $12);
+          ((int)$3, (OCC_Internals::BooleanOperator)$1, object, tool, out, outMap, $8, $12);
       }
       if(!r) yymsg(0, "Could not apply boolean operator");
       List_Delete($7);
diff --git a/Post/PViewVertexArrays.cpp b/Post/PViewVertexArrays.cpp
index e6a9b56609d011f4dcc7b22c57f493220480e562..bcd22f7a74ed61dcf2a2101a9aa623a5a551c050 100644
--- a/Post/PViewVertexArrays.cpp
+++ b/Post/PViewVertexArrays.cpp
@@ -806,8 +806,7 @@ static void addScalarPrism(PView *p, double **xyz, double **val, bool pre)
   PViewOptions *opt = p->getOptions();
   const int iq[3][4] = {{0, 1, 4, 3}, {0, 3, 5, 2}, {1, 2, 5, 4}};
   const int it[2][3] = {{0, 2, 1}, {3, 4, 5}};
-  // const int is[3][4] = {{0, 1, 2, 3}, {3, 4, 5, 2}, {1, 2, 4, 3}};
-  const int is[3][4] = {{0,1,2,4}, {0,2,4,5}, {0,3,4,5}};
+  const int is[3][4] = {{0, 1, 2, 4}, {0, 4, 2, 5}, {0, 3, 4, 5}};
 
   if(opt->boundary > 0){
     opt->boundary--;
@@ -839,8 +838,7 @@ static void addScalarPyramid(PView *p, double **xyz,
   PViewOptions *opt = p->getOptions();
 
   const int it[4][3] = {{0, 1, 4}, {3, 0, 4}, {1, 2, 4}, {2, 3, 4}};
-  //const int is[2][4] = {{0, 1, 2, 4}, {2, 3, 0, 4}};
-  const int is[2][4] = {{0,1,3,4}, {1,2,3,4}};
+  const int is[2][4] = {{0, 1, 3, 4}, {1, 2, 3, 4}};
 
   if(opt->boundary > 0){
     opt->boundary--;
diff --git a/benchmarks/3d/Cube-05.geo b/benchmarks/3d/Cube-05.geo
index 12e4015f450909bc919860a9191fd9346cc00ce0..720725b92804e7648635f4a553a7bb5f6237e472 100644
--- a/benchmarks/3d/Cube-05.geo
+++ b/benchmarks/3d/Cube-05.geo
@@ -1,5 +1,5 @@
-lv = .1;
-lc = .1;
+lv = .15;
+lc = .12;
 Point(1) = {0.0,0.0,0.0,lv};
 Point(2) = {1,0.0,0.0,lv};
 Point(3) = {1,1,0.0,lv};
diff --git a/demos/boolean/baffles.geo b/demos/boolean/baffles.geo
index 17399104a333a5d81d7226130705a7df32f4e4d6..64d43c444214f5fce9ee51a2d9a3f04ec38bae71 100644
--- a/demos/boolean/baffles.geo
+++ b/demos/boolean/baffles.geo
@@ -1,12 +1,16 @@
 SetFactory("OpenCASCADE");
+
 Block(1) = {-0, 0, 0, 1, 1, 1};
 
-// intersecting:
+p() = PointsOf{ Volume{1}; };
+Characteristic Length{p()} = 0.2;
+
 Rectangle(7) = {0.4, 0.7, 0.2, 0.3, 0.3, 0};
 Rectangle(8) = {-0.2, 0.3, 0.5, 0.3, 0.3, 0};
 Rectangle(9) = {0.8, -0.2, 0.5, 0.3, 0.3, 0};
-BooleanFragments{ Volume{1}; Delete; }{ Surface{7:9}; Delete; }
+Rectangle(10) = {0.3, 0.3, 0.5, 0.3, 0.3, 0};
+
+b() = BooleanFragments{ Volume{1}; Delete; }{ Surface{7:10}; Delete; };
 
-// fully inside
-Rectangle(30) = {0.3, 0.3, 0.5, 0.3, 0.3, 0};
-Surface {30} In Volume{1};
+p() = PointsOf{ Surface{b({1:6})}; };
+Characteristic Length{p()} = 0.04;
diff --git a/demos/boolean/fragment_numbering.geo b/demos/boolean/fragment_numbering.geo
index 40c6f09ac990aa74f1b8f8c291899254c8d5539a..e627c69ea6d4af220c61c09c2b61a478e6d50dad 100644
--- a/demos/boolean/fragment_numbering.geo
+++ b/demos/boolean/fragment_numbering.geo
@@ -1,5 +1,6 @@
 SetFactory("OpenCASCADE");
 
+//Geometry.LineNumbers = 1;
 Geometry.SurfaceNumbers = 1;
 
 DefineConstant[
@@ -17,6 +18,10 @@ Translate {2*w, 0, 0} { Duplicata { Surface{4}; } }
 
 Rectangle(6) = {-6*w, 0, 0, 12*w, 5*w, 0};
 
+b() = Boundary{ Surface{6}; };
+t = b(2);
+Printf("top line tag: ", t);
+
 b() = {};
 For i In {1:N}
   s = news; b() += s; Disk(s) = {-w-R-spacing, (2*R+spacing)*i, 0, R};
@@ -24,6 +29,6 @@ EndFor
 
 Printf("disk tags: ", b());
 
-c() = BooleanFragments{ Surface{1:6}; Delete; }{ Surface{b()}; Delete; };
+c() = BooleanFragments{ Line{t}; Surface{1:6}; Delete; }{ Surface{b()}; Delete; };
 
-Printf("all tags (disk tags should be unchanged!): ", c());
+Printf("all tags (disk and top line tags should be unchanged!): ", c());
diff --git a/doc/gmsh.html b/doc/gmsh.html
index 4a20454de8c0e9f49d697dc1258e0d6fb1e729cb..2277b40593030c0b700ce1f97301265a49fac80b 100644
--- a/doc/gmsh.html
+++ b/doc/gmsh.html
@@ -6,7 +6,7 @@
 <title>Gmsh: a three-dimensional finite element mesh generator with
 built-in pre- and post-processing facilities</title>
 
-<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> 
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 
 <meta name="keywords" content="free, finite elements, mesh generator, fem,
 unstructured grids, delaunay, discontinuous galerkin, triangulation,
 automatic, maillages, mailleur 3D, mesh adaptation, postprocessing,
@@ -51,7 +51,7 @@ CAO, OpenGL, GL2PS, Gmesh">
 <h1>A three-dimensional finite element mesh generator
   with built-in pre- and post-processing facilities</h1>
 
-<h4>Christophe Geuzaine and Jean-Fran�ois Remacle</h4>
+<h4>Christophe Geuzaine and Jean-François Remacle</h4>
 
 <div id="toc">
   <a href="#Download">Download</a>
@@ -279,7 +279,7 @@ directly</a> for more information.
   </ul>
   Cross-patch and STL meshing (Compounds)
   <ul class="small">
-    <li>J.-F. Remacle, C. Geuzaine, G. Comp�re and
+    <li>J.-F. Remacle, C. Geuzaine, G. Compère and
       E. Marchandise. <em><a href="doc/preprints/gmsh_stl_preprint.pdf">High-quality
       surface remeshing using harmonic maps</a></em>.  International Journal for
       Numerical Methods in Engineering 83(4), pp. 403-425, 2010.
@@ -301,7 +301,7 @@ directly</a> for more information.
       a non-uniform quadrilateral mesh generator using a minimum cost perfect
       matching algorithm</a></em>.  International Journal for Numerical Methods
       in Engineering 89, pp. 1102-1119, 2012.
-    <li>J.-F. Remacle, F. Henrotte, T. Carrier-Baudouin, E. B�chet,
+    <li>J.-F. Remacle, F. Henrotte, T. Carrier-Baudouin, E. Béchet,
       E. Marchandise, C. Geuzaine and
       T. Mouton. <em><a href="doc/preprints/gmsh_quad2_preprint.pdf">A frontal
       Delaunay quad mesh generator using the L&infin; norm</a></em>.
@@ -347,7 +347,7 @@ directly</a> for more information.
   </ul>
   Biomedical meshing
   <ul class="small">
-    <li>E. Marchandise, G. Comp�re, M. Willemet, G. Bricteux, C. Geuzaine and
+    <li>E. Marchandise, G. Compère, M. Willemet, G. Bricteux, C. Geuzaine and
       J-F Remacle. <em><a href="doc/preprints/gmsh_bio_preprint.pdf">Quality
       meshing based on STL triangulations for biomedical simulations</a></em>.
       International Journal for Numerical Methods in Biomedical Engineering
diff --git a/doc/texinfo/commandline.texi b/doc/texinfo/commandline.texi
index 7c23905418157d3f30e0b7a8c97359006a5d0023..a9ec3cd3e33cf708ceb9e121a61ac2bab1b4b820 100644
--- a/doc/texinfo/commandline.texi
+++ b/doc/texinfo/commandline.texi
@@ -30,10 +30,12 @@ Perform uniform mesh refinement, then exit
 Partition after batch mesh generation
 @item -partWeight tri|quad|tet|prism|hex int
 Weight of a triangle/quad/etc. during partitioning
-@item -saveall
+@item -save_all
 Save all elements (discard physical group definitions)
-@item -parametric
+@item -save_parametric
 Save vertices with their parametric coordinates
+@item -save_topology
+Save model topology
 @item -algo string
 Select mesh algorithm (meshadapt, del2d, front2d, delquad, del3d, front3d, mmg3d, pack)
 @item -smooth int
diff --git a/doc/texinfo/opt_fields.texi b/doc/texinfo/opt_fields.texi
index ad388855f644a1872f4b4a6adac4610cf4ed18d8..6fc5ba35f336fedb0282f7bdda6c10f75f4d28fc 100644
--- a/doc/texinfo/opt_fields.texi
+++ b/doc/texinfo/opt_fields.texi
@@ -578,7 +578,7 @@ Options:@*
 @item InField
 Id of the field to use as x coordinate.@*
 type: integer@*
-default value: @code{0}
+default value: @code{1752631850}
 @end table
 
 @item Param
diff --git a/doc/texinfo/opt_geometry.texi b/doc/texinfo/opt_geometry.texi
index 9e3bdbc5ef516c1ef0ce75804c0c8b5fc6a6f950..4b9c983a425d66a68c0cd650d9e959d3daa1e807 100644
--- a/doc/texinfo/opt_geometry.texi
+++ b/doc/texinfo/opt_geometry.texi
@@ -149,6 +149,11 @@ Use multi-threaded OCC boolean operators@*
 Default value: @code{0}@*
 Saved in: @code{General.OptionsFileName}
 
+@item Geometry.OCCBooleanPreserveNumbering
+Try to preserve numbering of entities through OCC boolean operations@*
+Default value: @code{1}@*
+Saved in: @code{General.OptionsFileName}
+
 @item Geometry.OCCScaling
 Scale STEP, IGES and BRep model by given factor@*
 Default value: @code{1}@*
diff --git a/doc/texinfo/opt_mesh.texi b/doc/texinfo/opt_mesh.texi
index fee3bb323fc8486ef48a3b578da7a1610aec9773..6e43a4da4b5659bac09688708090ea2c02af2838 100644
--- a/doc/texinfo/opt_mesh.texi
+++ b/doc/texinfo/opt_mesh.texi
@@ -599,6 +599,11 @@ Type of the element tag saved in mesh formats that don't support saving physical
 Default value: @code{1}@*
 Saved in: @code{General.OptionsFileName}
 
+@item Mesh.SaveTopology
+Save model topology in MSH2 output files (this is always saved in MSH3)@*
+Default value: @code{0}@*
+Saved in: @code{General.OptionsFileName}
+
 @item Mesh.SaveParametric
 Save parametric coordinates of nodes@*
 Default value: @code{0}@*
diff --git a/doc/texinfo/shortcuts.texi b/doc/texinfo/shortcuts.texi
index 151e945e4ebb13a393d12ea2bebbd4baaef1c163..f016ecc8b924dca56db01cedc0661eea1f62da07 100644
--- a/doc/texinfo/shortcuts.texi
+++ b/doc/texinfo/shortcuts.texi
@@ -37,11 +37,11 @@ Abort selection in geometry creation mode
 @item s
 Go to solver module
 @item x
-Freeze x coordinate in geometry creation mode
+Toogle x coordinate freeze in geometry creation mode
 @item y
-Freeze y coordinate in geometry creation mode
+Toogle y coordinate freeze in geometry creation mode
 @item z
-Freeze z coordinate in geometry creation mode
+Toogle z coordinate freeze in geometry creation mode
 @item Shift+a
 Bring all windows to front
 @item Shift+g
@@ -58,6 +58,12 @@ Show solver options
 Show post-processing view plugins
 @item Shift+w
 Show post-processing view options
+@item Shift+x
+Move only along x coordinate in geometry creation mode
+@item Shift+y
+Move only along y coordinate in geometry creation mode
+@item Shift+z
+Move only along z coordinate in geometry creation mode
 @item Shift+Escape
 Enable full mouse selection
 @item Ctrl+d