diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index a26597f1fe55fa1f05d27baf0971dee1f39a94d5..24402434a34b328e475dbd72324ae2d9009a599b 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -3,7 +3,7 @@ stages:
   - build
 
 before_script:
-  - yum -y install cmake glibc-devel sqlite which
+  - yum -y install cmake glibc-devel sqlite sqlite-devel which
   - set +e && source CI/setup_lcg.sh; set -e
 
 variables:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c9d2319160112daf8b32bf3b55d275000eaa84d9..af09319827bfc0f675634faef2416edc1ed7ae88 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -25,10 +25,11 @@ include( PrintBuildInfo )
 
 # Project's dependencies.
 find_package( GeoModelCore 3.2.0 REQUIRED )
-find_package( Qt5 COMPONENTS Core Sql REQUIRED )
+# find_package( Qt5 COMPONENTS Core Sql REQUIRED )
+find_package( SQLite3 REQUIRED )
 
 # Set sub-packages to build.
-add_subdirectory( GeoModelErrorHandler )
+# add_subdirectory( GeoModelErrorHandler ) // unused at the moment.
 add_subdirectory( GeoModelDBManager )
 add_subdirectory( TFPersistification )
 add_subdirectory( GeoModelRead )
diff --git a/GeoModelDBManager/CMakeLists.txt b/GeoModelDBManager/CMakeLists.txt
index bafb9d8d12c6540682f8b91951ed3f64a942d683..ddc72a3cfca5a56d87457e14b6201bcca0023b6e 100644
--- a/GeoModelDBManager/CMakeLists.txt
+++ b/GeoModelDBManager/CMakeLists.txt
@@ -6,12 +6,16 @@
 ################################################################################
 
 # Find the header and source files.
-file( GLOB SOURCES src/*.cpp )
-file( GLOB HEADERS GeoModelDBManager/*.h )
+file( GLOB SOURCES src/*.cpp  )
+file( GLOB HEADERS GeoModelDBManager/*.h fmt/*.h )
 
 # Set up the library.
 add_library( GeoModelDBManager SHARED ${HEADERS} ${SOURCES} )
+
 target_link_libraries( GeoModelDBManager PUBLIC Qt5::Sql )
+target_link_libraries( GeoModelDBManager LINK_PUBLIC sqlite3 )
+# target_link_libraries( GeoModelDBManager PUBLIC Qt5::Sql )
+
 target_include_directories( GeoModelDBManager PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
    $<INSTALL_INTERFACE:include> )
diff --git a/GeoModelDBManager/GeoModelDBManager/GMDBManager.h b/GeoModelDBManager/GeoModelDBManager/GMDBManager.h
index d5a49139dd4a16f48dd37f566f914ec313da964e..8743447184056a06d40e10912e8bd56af10ced45 100644
--- a/GeoModelDBManager/GeoModelDBManager/GMDBManager.h
+++ b/GeoModelDBManager/GeoModelDBManager/GMDBManager.h
@@ -14,9 +14,14 @@
 #include <QString>
 #include <QMap>
 
+#include <sqlite3.h>
+
+
 // include C++
 #include <iostream>
 #include <unordered_map>
+#include <vector>
+#include <string>
 
 /**
  * \class GMDBManager
@@ -35,7 +40,8 @@ public:
 	 * Constructor sets up connection with db and opens it
 	 * @param path - absolute path to db file
 	 */
-	GMDBManager(const QString& path);
+  GMDBManager(const std::string &path);
+//  GMDBManagerStd(const std::string &path);
 
 	/**
 	 * @brief Destructor
@@ -43,12 +49,18 @@ public:
 	 * Close the db connection
 	 */
 	~GMDBManager();
+  void DestructorGMDBManagerStd();
+
+  bool initDB();
+
+  int loadGeoNodeTypesAndBuildCache();
 
 
 	/**
 	 * @brief Returns true if the DB is correctly open
 	 */
-	bool isOpen() const;
+//  bool isOpen() const;
+  bool checkIsDBOpen() const;
 
 	/**
 	 * @brief Print names of all GeoPhysVol objects in db
@@ -131,107 +143,101 @@ public:
 	 */
 	void printDBVersion() const;
 
-	// ADD methods
-	QVariant addShape(const QString &type, const QString &parameters);
-	QVariant addMaterial(const QString &name, const QString &density, const QString &elements);
-	QVariant addElement(const QString &name, const QString &symbol, const QString &elZ, const QString &elA);
-	QVariant addLogVol(const QString &name, const QVariant &shapeId, const QVariant &materialId);
-	QVariant addPhysVol(const QVariant &logVolId, const QVariant &parentPhysVolId, bool isRootVolume = false);
-	QVariant addFullPhysVol(const QVariant &logVolId, const QVariant &parentPhysVolId, bool isRootVolume = false);
-	QVariant addSerialDenominator(const QString &baseName);
-	QVariant addFunction(const QString expression);
-	QVariant addSerialTransformer(const QVariant &funcId, const QVariant &physvolId, const QString volType, const unsigned int &copies);
-	QVariant addTransform(QVector<double> parameters);
-	QVariant addAlignableTransform(QVector<double> parameters);
-	QVariant addNameTag(const QString &name);
-
-
-	bool addListOfRecords(const QString geoType, const std::vector<QStringList> records);
-	bool addListOfRecordsToTable(const QString tableName, const std::vector<QStringList> records);
-	bool addListOfRecordsToTableOld(const QString tableName, const std::vector<QStringList> records);
-	bool addListOfChildrenPositions(const std::vector<QStringList> records);
+  
+  void printAllDBTables();
+  void getAllDBTables();
+  int execQuery(std::string queryStr);
 
-	bool addRootVolume(const QStringList);
-	void addChildPosition(const QVariant parentId, const QString parentType, const QVariant childId, const unsigned int parentCopyNumber, const unsigned int childPos, const QString childType, const unsigned int childCopyN);
 
-	void addDBversion(const QString);
+  bool addListOfRecords(const std::string geoType, const std::vector<std::vector<std::string>> records);
+  
+  bool addListOfRecordsToTable(const std::string tableName, const std::vector<std::vector<std::string>> records);
+  
+//  bool addListOfRecordsToTableOld(const QString tableName, const std::vector<QStringList> records); // for the old SQlite only
+  bool addListOfChildrenPositions(const std::vector<std::vector<std::string>> &records);
 
+	bool addRootVolume(const std::vector<std::string> &values);
+  
 	// GET methods
 
-	QString getDBFilePath();
+  std::string getDBFilePath();
 
-	QStringList getRootPhysVol();
-  std::vector<std::string> getRootPhysVolStd();
+  std::vector<std::string> getRootPhysVol();
 
-	QStringList getItem(QString geoType, unsigned int id);
-	QStringList getItem(unsigned int tableId, unsigned int id);
 	
-  QStringList getItemFromTableName(QString tableName, unsigned int id);
-  std::vector<std::string> getItemFromTableNameStd(QString tableName, unsigned int id);
+  std::vector<std::string> getItemFromTableName(std::string tableName, unsigned int id);
   
-	QStringList getItemAndType(unsigned int tableId, unsigned int id);
-  std::vector<std::string> getItemAndTypeStd(unsigned int tableId, unsigned int id);
+  std::vector<std::string> getItemAndType(unsigned int tableId, unsigned int id);
 
-	QString getNodeTypeFromTableId(unsigned int id);
-  std::string getNodeTypeFromTableIdStd(unsigned int id);
+  std::string getNodeTypeFromTableId(unsigned int id);
 
-	QMap<unsigned int, QStringList> getVPhysVolChildren(const unsigned int id, const QString nodeType, const unsigned int copyN);
 
 	/// methods to dump the DB
-	QHash<QString, QMap<unsigned int, QStringList>> getChildrenTable();
-  std::vector<std::vector<std::string>> getChildrenTableStd();
+  std::vector<std::vector<std::string>> getChildrenTable();
 
-	QHash<unsigned int, QStringList> getTableFromNodeType(QString nodeType);
-  std::vector<std::vector<std::string>> getTableFromNodeTypeStd(std::string nodeType);
+  std::vector<std::vector<std::string>> getTableFromNodeType(std::string nodeType);
   
-	QHash<unsigned int, QString> getAll_TableIDsNodeTypes();
-	QHash<QString, unsigned int> getAll_NodeTypesTableIDs();
-  std::unordered_map<unsigned int, std::string> getAll_TableIDsNodeTypesStd();
-  std::unordered_map<std::string, unsigned int> getAll_NodeTypesTableIDsStd();
+  std::unordered_map<unsigned int, std::string> getAll_TableIDsNodeTypes();
+  std::unordered_map<std::string, unsigned int> getAll_NodeTypesTableIDs();
   
+  std::vector<std::vector<std::string>> getTableRecords(std::string tableName) const; // TODO: should be private?
 
 
 private:
 
-	bool initDB();
 	bool createTables();
+  
 
-	void loadTestData(); // for debug only
+  void addDBversion(std::string version);
+  
 
-	void loadTableNamesFromDB();
-	QStringList getTableColNamesFromDB(QString tableName) const;
 
-	QString getTableNameFromTableId(unsigned int tabId);
-  std::string getTableNameFromTableIdStd(unsigned int tabId);
-  
-	QVariant getTableIdFromNodeType(QString nodeType);
-	void storeNodeType(QString nodeType, QString tableName);
-	QString getTableNameFromNodeType(QString nodeType);
+//  void loadTestData(); // for debug only
 
-	QSqlQuery selectAllFromTable(QString tableName) const;
-  QSqlQuery selectAllFromTableSortBy(QString tableName, std::string sortColumn="") const;
+  std::string getTableNameFromTableId(unsigned int tabId);
+  
+  unsigned int getTableIdFromNodeType(const std::string &nodeType);
+  void storeNodeType(std::string nodeType, std::string tableName);
+	
+  std::string getTableNameFromNodeType(const std::string &nodeType);
 
+  sqlite3_stmt* selectAllFromTable(std::string tableName) const;
+  sqlite3_stmt* selectAllFromTableSortBy(std::string tableName, std::string sortColumn="") const;
+  sqlite3_stmt* selectAllFromTableChildrenPositions() const;
 
-	void storeTableColumnNames(QStringList input);
-	QStringList getTableColumnNames(QString tableName);
-	void printTableColNamesFromDB(QString tableName) const;
+  void storeTableColumnNames(std::vector<std::string> input);
+  
+  std::vector<std::string> getTableColumnNames(const std::string &tableName);
 
-	void printAllRecords(QString tableName) const;
+  void printAllRecords(const std::string &tableName) const;
 
-	int getTableColIndex(QString tableName, QString colName);
+  int getTableColIndex(const std::string &tableName, const std::string &colName);
 
-	bool storeRootVolume(QVariant id, QString nodeType);
+  bool storeRootVolume(const unsigned int &id, const std::string &nodeType);
 
 	void showError(const QSqlError &err) const;
 
-  QString m_dbpath;
-	QSqlDatabase m_db;
+  std::string m_dbpath;
+	
+//  QSqlDatabase m_db;
+  /// Pointer to SQLite connection
+  sqlite3* m_dbSqlite;
+  /// Variable to store error messages from SQLite
+  char *m_SQLiteErrMsg;
+  
+  
 	bool m_dbIsOK;
 
-	bool m_deepDebug;
+	bool m_debug;
 
-	QMap<QString, QStringList> m_tableNames;
-	QMap<QString, QString> m_childType_tableName;
+  std::unordered_map<std::string, std::vector<std::string>> m_tableNames; /// stores the column names for each table
+  std::unordered_map<std::string, std::string> m_childType_tableName;
+  
+  std::vector<std::string> m_cache_tables; /// cache for the list of tables in the DB
+  std::unordered_map<unsigned int, std::string> m_cache_tableId_tableName; /// cache for tableID-->tableName
+  std::unordered_map<unsigned int, std::string> m_cache_tableId_nodeType; /// cache for tableID-->nodeType
+  std::unordered_map<std::string, std::string> m_cache_nodeType_tableName; /// cache for nodeType-->tableName
+  std::unordered_map<std::string, unsigned int> m_cache_nodeType_tableID; /// cache for nodeType-->tableID
 };
 
 #endif // GMDBManager_H
diff --git a/GeoModelDBManager/fmt/core.h b/GeoModelDBManager/fmt/core.h
new file mode 100644
index 0000000000000000000000000000000000000000..0e0824f5fe79891b840180228123d2d2d65e9fe0
--- /dev/null
+++ b/GeoModelDBManager/fmt/core.h
@@ -0,0 +1,1796 @@
+// Formatting library for C++ - the core API
+//
+// Copyright (c) 2012 - present, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_CORE_H_
+#define FMT_CORE_H_
+
+#include <cstdio>  // std::FILE
+#include <cstring>
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+// The fmt library version in the form major * 10000 + minor * 100 + patch.
+#define FMT_VERSION 60201
+
+#ifdef __has_feature
+#  define FMT_HAS_FEATURE(x) __has_feature(x)
+#else
+#  define FMT_HAS_FEATURE(x) 0
+#endif
+
+#if defined(__has_include) && !defined(__INTELLISENSE__) && \
+    !(defined(__INTEL_COMPILER) && __INTEL_COMPILER < 1600)
+#  define FMT_HAS_INCLUDE(x) __has_include(x)
+#else
+#  define FMT_HAS_INCLUDE(x) 0
+#endif
+
+#ifdef __has_cpp_attribute
+#  define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
+#else
+#  define FMT_HAS_CPP_ATTRIBUTE(x) 0
+#endif
+
+#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \
+  (__cplusplus >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute))
+
+#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \
+  (__cplusplus >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute))
+
+#ifdef __clang__
+#  define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
+#else
+#  define FMT_CLANG_VERSION 0
+#endif
+
+#if defined(__GNUC__) && !defined(__clang__)
+#  define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
+#else
+#  define FMT_GCC_VERSION 0
+#endif
+
+#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
+#  define FMT_HAS_GXX_CXX11 FMT_GCC_VERSION
+#else
+#  define FMT_HAS_GXX_CXX11 0
+#endif
+
+#ifdef __NVCC__
+#  define FMT_NVCC __NVCC__
+#else
+#  define FMT_NVCC 0
+#endif
+
+#ifdef _MSC_VER
+#  define FMT_MSC_VER _MSC_VER
+#else
+#  define FMT_MSC_VER 0
+#endif
+
+// Check if relaxed C++14 constexpr is supported.
+// GCC doesn't allow throw in constexpr until version 6 (bug 67371).
+#ifndef FMT_USE_CONSTEXPR
+#  define FMT_USE_CONSTEXPR                                           \
+    (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1910 || \
+     (FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) &&           \
+        !FMT_NVCC
+#endif
+#if FMT_USE_CONSTEXPR
+#  define FMT_CONSTEXPR constexpr
+#  define FMT_CONSTEXPR_DECL constexpr
+#else
+#  define FMT_CONSTEXPR inline
+#  define FMT_CONSTEXPR_DECL
+#endif
+
+#ifndef FMT_OVERRIDE
+#  if FMT_HAS_FEATURE(cxx_override) || \
+      (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900
+#    define FMT_OVERRIDE override
+#  else
+#    define FMT_OVERRIDE
+#  endif
+#endif
+
+// Check if exceptions are disabled.
+#ifndef FMT_EXCEPTIONS
+#  if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \
+      FMT_MSC_VER && !_HAS_EXCEPTIONS
+#    define FMT_EXCEPTIONS 0
+#  else
+#    define FMT_EXCEPTIONS 1
+#  endif
+#endif
+
+// Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature).
+#ifndef FMT_USE_NOEXCEPT
+#  define FMT_USE_NOEXCEPT 0
+#endif
+
+#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \
+    (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900
+#  define FMT_DETECTED_NOEXCEPT noexcept
+#  define FMT_HAS_CXX11_NOEXCEPT 1
+#else
+#  define FMT_DETECTED_NOEXCEPT throw()
+#  define FMT_HAS_CXX11_NOEXCEPT 0
+#endif
+
+#ifndef FMT_NOEXCEPT
+#  if FMT_EXCEPTIONS || FMT_HAS_CXX11_NOEXCEPT
+#    define FMT_NOEXCEPT FMT_DETECTED_NOEXCEPT
+#  else
+#    define FMT_NOEXCEPT
+#  endif
+#endif
+
+// [[noreturn]] is disabled on MSVC and NVCC because of bogus unreachable code
+// warnings.
+#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VER && \
+    !FMT_NVCC
+#  define FMT_NORETURN [[noreturn]]
+#else
+#  define FMT_NORETURN
+#endif
+
+#ifndef FMT_MAYBE_UNUSED
+#  if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused)
+#    define FMT_MAYBE_UNUSED [[maybe_unused]]
+#  else
+#    define FMT_MAYBE_UNUSED
+#  endif
+#endif
+
+#ifndef FMT_DEPRECATED
+#  if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900
+#    define FMT_DEPRECATED [[deprecated]]
+#  else
+#    if defined(__GNUC__) || defined(__clang__)
+#      define FMT_DEPRECATED __attribute__((deprecated))
+#    elif FMT_MSC_VER
+#      define FMT_DEPRECATED __declspec(deprecated)
+#    else
+#      define FMT_DEPRECATED /* deprecated */
+#    endif
+#  endif
+#endif
+
+// Workaround broken [[deprecated]] in the Intel, PGI and NVCC compilers.
+#if defined(__INTEL_COMPILER) || defined(__PGI) || FMT_NVCC
+#  define FMT_DEPRECATED_ALIAS
+#else
+#  define FMT_DEPRECATED_ALIAS FMT_DEPRECATED
+#endif
+
+#ifndef FMT_BEGIN_NAMESPACE
+#  if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \
+      FMT_MSC_VER >= 1900
+#    define FMT_INLINE_NAMESPACE inline namespace
+#    define FMT_END_NAMESPACE \
+      }                       \
+      }
+#  else
+#    define FMT_INLINE_NAMESPACE namespace
+#    define FMT_END_NAMESPACE \
+      }                       \
+      using namespace v6;     \
+      }
+#  endif
+#  define FMT_BEGIN_NAMESPACE \
+    namespace fmt {           \
+    FMT_INLINE_NAMESPACE v6 {
+#endif
+
+#if !defined(FMT_HEADER_ONLY) && defined(_WIN32)
+#  if FMT_MSC_VER
+#    define FMT_NO_W4275 __pragma(warning(suppress : 4275))
+#  else
+#    define FMT_NO_W4275
+#  endif
+#  define FMT_CLASS_API FMT_NO_W4275
+#  ifdef FMT_EXPORT
+#    define FMT_API __declspec(dllexport)
+#  elif defined(FMT_SHARED)
+#    define FMT_API __declspec(dllimport)
+#    define FMT_EXTERN_TEMPLATE_API FMT_API
+#  endif
+#endif
+#ifndef FMT_CLASS_API
+#  define FMT_CLASS_API
+#endif
+#ifndef FMT_API
+#  if FMT_GCC_VERSION || FMT_CLANG_VERSION
+#    define FMT_API __attribute__((visibility("default")))
+#    define FMT_EXTERN_TEMPLATE_API FMT_API
+#    define FMT_INSTANTIATION_DEF_API
+#  else
+#    define FMT_API
+#  endif
+#endif
+#ifndef FMT_EXTERN_TEMPLATE_API
+#  define FMT_EXTERN_TEMPLATE_API
+#endif
+#ifndef FMT_INSTANTIATION_DEF_API
+#  define FMT_INSTANTIATION_DEF_API FMT_API
+#endif
+
+#ifndef FMT_HEADER_ONLY
+#  define FMT_EXTERN extern
+#else
+#  define FMT_EXTERN
+#endif
+
+// libc++ supports string_view in pre-c++17.
+#if (FMT_HAS_INCLUDE(<string_view>) &&                       \
+     (__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \
+    (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910)
+#  include <string_view>
+#  define FMT_USE_STRING_VIEW
+#elif FMT_HAS_INCLUDE("experimental/string_view") && __cplusplus >= 201402L
+#  include <experimental/string_view>
+#  define FMT_USE_EXPERIMENTAL_STRING_VIEW
+#endif
+
+#ifndef FMT_UNICODE
+#  define FMT_UNICODE !FMT_MSC_VER
+#endif
+#if FMT_UNICODE && FMT_MSC_VER
+#  pragma execution_character_set("utf-8")
+#endif
+
+FMT_BEGIN_NAMESPACE
+
+// Implementations of enable_if_t and other metafunctions for older systems.
+template <bool B, class T = void>
+using enable_if_t = typename std::enable_if<B, T>::type;
+template <bool B, class T, class F>
+using conditional_t = typename std::conditional<B, T, F>::type;
+template <bool B> using bool_constant = std::integral_constant<bool, B>;
+template <typename T>
+using remove_reference_t = typename std::remove_reference<T>::type;
+template <typename T>
+using remove_const_t = typename std::remove_const<T>::type;
+template <typename T>
+using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type;
+template <typename T> struct type_identity { using type = T; };
+template <typename T> using type_identity_t = typename type_identity<T>::type;
+
+struct monostate {};
+
+// An enable_if helper to be used in template parameters which results in much
+// shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed
+// to workaround a bug in MSVC 2019 (see #1140 and #1186).
+#define FMT_ENABLE_IF(...) enable_if_t<(__VA_ARGS__), int> = 0
+
+namespace internal {
+
+// A helper function to suppress bogus "conditional expression is constant"
+// warnings.
+template <typename T> FMT_CONSTEXPR T const_check(T value) { return value; }
+
+// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
+template <typename... Ts> struct void_t_impl { using type = void; };
+
+FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
+                                      const char* message);
+
+#ifndef FMT_ASSERT
+#  ifdef NDEBUG
+// FMT_ASSERT is not empty to avoid -Werror=empty-body.
+#    define FMT_ASSERT(condition, message) ((void)0)
+#  else
+#    define FMT_ASSERT(condition, message)                                    \
+      ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \
+           ? (void)0                                                          \
+           : ::fmt::internal::assert_fail(__FILE__, __LINE__, (message)))
+#  endif
+#endif
+
+#if defined(FMT_USE_STRING_VIEW)
+template <typename Char> using std_string_view = std::basic_string_view<Char>;
+#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW)
+template <typename Char>
+using std_string_view = std::experimental::basic_string_view<Char>;
+#else
+template <typename T> struct std_string_view {};
+#endif
+
+#ifdef FMT_USE_INT128
+// Do nothing.
+#elif defined(__SIZEOF_INT128__) && !FMT_NVCC
+#  define FMT_USE_INT128 1
+using int128_t = __int128_t;
+using uint128_t = __uint128_t;
+#else
+#  define FMT_USE_INT128 0
+#endif
+#if !FMT_USE_INT128
+struct int128_t {};
+struct uint128_t {};
+#endif
+
+// Casts a nonnegative integer to unsigned.
+template <typename Int>
+FMT_CONSTEXPR typename std::make_unsigned<Int>::type to_unsigned(Int value) {
+  FMT_ASSERT(value >= 0, "negative value");
+  return static_cast<typename std::make_unsigned<Int>::type>(value);
+}
+
+constexpr unsigned char micro[] = "\u00B5";
+
+template <typename Char> constexpr bool is_unicode() {
+  return FMT_UNICODE || sizeof(Char) != 1 ||
+         (sizeof(micro) == 3 && micro[0] == 0xC2 && micro[1] == 0xB5);
+}
+
+#ifdef __cpp_char8_t
+using char8_type = char8_t;
+#else
+enum char8_type : unsigned char {};
+#endif
+}  // namespace internal
+
+template <typename... Ts>
+using void_t = typename internal::void_t_impl<Ts...>::type;
+
+/**
+  An implementation of ``std::basic_string_view`` for pre-C++17. It provides a
+  subset of the API. ``fmt::basic_string_view`` is used for format strings even
+  if ``std::string_view`` is available to prevent issues when a library is
+  compiled with a different ``-std`` option than the client code (which is not
+  recommended).
+ */
+template <typename Char> class basic_string_view {
+ private:
+  const Char* data_;
+  size_t size_;
+
+ public:
+  using char_type FMT_DEPRECATED_ALIAS = Char;
+  using value_type = Char;
+  using iterator = const Char*;
+
+  FMT_CONSTEXPR basic_string_view() FMT_NOEXCEPT : data_(nullptr), size_(0) {}
+
+  /** Constructs a string reference object from a C string and a size. */
+  FMT_CONSTEXPR basic_string_view(const Char* s, size_t count) FMT_NOEXCEPT
+      : data_(s),
+        size_(count) {}
+
+  /**
+    \rst
+    Constructs a string reference object from a C string computing
+    the size with ``std::char_traits<Char>::length``.
+    \endrst
+   */
+#if __cplusplus >= 201703L  // C++17's char_traits::length() is constexpr.
+  FMT_CONSTEXPR
+#endif
+  basic_string_view(const Char* s)
+      : data_(s), size_(std::char_traits<Char>::length(s)) {}
+
+  /** Constructs a string reference from a ``std::basic_string`` object. */
+  template <typename Traits, typename Alloc>
+  FMT_CONSTEXPR basic_string_view(
+      const std::basic_string<Char, Traits, Alloc>& s) FMT_NOEXCEPT
+      : data_(s.data()),
+        size_(s.size()) {}
+
+  template <
+      typename S,
+      FMT_ENABLE_IF(std::is_same<S, internal::std_string_view<Char>>::value)>
+  FMT_CONSTEXPR basic_string_view(S s) FMT_NOEXCEPT : data_(s.data()),
+                                                      size_(s.size()) {}
+
+  /** Returns a pointer to the string data. */
+  FMT_CONSTEXPR const Char* data() const { return data_; }
+
+  /** Returns the string size. */
+  FMT_CONSTEXPR size_t size() const { return size_; }
+
+  FMT_CONSTEXPR iterator begin() const { return data_; }
+  FMT_CONSTEXPR iterator end() const { return data_ + size_; }
+
+  FMT_CONSTEXPR const Char& operator[](size_t pos) const { return data_[pos]; }
+
+  FMT_CONSTEXPR void remove_prefix(size_t n) {
+    data_ += n;
+    size_ -= n;
+  }
+
+  // Lexicographically compare this string reference to other.
+  int compare(basic_string_view other) const {
+    size_t str_size = size_ < other.size_ ? size_ : other.size_;
+    int result = std::char_traits<Char>::compare(data_, other.data_, str_size);
+    if (result == 0)
+      result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1);
+    return result;
+  }
+
+  friend bool operator==(basic_string_view lhs, basic_string_view rhs) {
+    return lhs.compare(rhs) == 0;
+  }
+  friend bool operator!=(basic_string_view lhs, basic_string_view rhs) {
+    return lhs.compare(rhs) != 0;
+  }
+  friend bool operator<(basic_string_view lhs, basic_string_view rhs) {
+    return lhs.compare(rhs) < 0;
+  }
+  friend bool operator<=(basic_string_view lhs, basic_string_view rhs) {
+    return lhs.compare(rhs) <= 0;
+  }
+  friend bool operator>(basic_string_view lhs, basic_string_view rhs) {
+    return lhs.compare(rhs) > 0;
+  }
+  friend bool operator>=(basic_string_view lhs, basic_string_view rhs) {
+    return lhs.compare(rhs) >= 0;
+  }
+};
+
+using string_view = basic_string_view<char>;
+using wstring_view = basic_string_view<wchar_t>;
+
+#ifndef __cpp_char8_t
+// char8_t is deprecated; use char instead.
+using char8_t FMT_DEPRECATED_ALIAS = internal::char8_type;
+#endif
+
+/** Specifies if ``T`` is a character type. Can be specialized by users. */
+template <typename T> struct is_char : std::false_type {};
+template <> struct is_char<char> : std::true_type {};
+template <> struct is_char<wchar_t> : std::true_type {};
+template <> struct is_char<internal::char8_type> : std::true_type {};
+template <> struct is_char<char16_t> : std::true_type {};
+template <> struct is_char<char32_t> : std::true_type {};
+
+/**
+  \rst
+  Returns a string view of `s`. In order to add custom string type support to
+  {fmt} provide an overload of `to_string_view` for it in the same namespace as
+  the type for the argument-dependent lookup to work.
+
+  **Example**::
+
+    namespace my_ns {
+    inline string_view to_string_view(const my_string& s) {
+      return {s.data(), s.length()};
+    }
+    }
+    std::string message = fmt::format(my_string("The answer is {}"), 42);
+  \endrst
+ */
+template <typename Char, FMT_ENABLE_IF(is_char<Char>::value)>
+inline basic_string_view<Char> to_string_view(const Char* s) {
+  return s;
+}
+
+template <typename Char, typename Traits, typename Alloc>
+inline basic_string_view<Char> to_string_view(
+    const std::basic_string<Char, Traits, Alloc>& s) {
+  return s;
+}
+
+template <typename Char>
+inline basic_string_view<Char> to_string_view(basic_string_view<Char> s) {
+  return s;
+}
+
+template <typename Char,
+          FMT_ENABLE_IF(!std::is_empty<internal::std_string_view<Char>>::value)>
+inline basic_string_view<Char> to_string_view(
+    internal::std_string_view<Char> s) {
+  return s;
+}
+
+// A base class for compile-time strings. It is defined in the fmt namespace to
+// make formatting functions visible via ADL, e.g. format(fmt("{}"), 42).
+struct compile_string {};
+
+template <typename S>
+struct is_compile_string : std::is_base_of<compile_string, S> {};
+
+template <typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
+constexpr basic_string_view<typename S::char_type> to_string_view(const S& s) {
+  return s;
+}
+
+namespace internal {
+void to_string_view(...);
+using fmt::v6::to_string_view;
+
+// Specifies whether S is a string type convertible to fmt::basic_string_view.
+// It should be a constexpr function but MSVC 2017 fails to compile it in
+// enable_if and MSVC 2015 fails to compile it as an alias template.
+template <typename S>
+struct is_string : std::is_class<decltype(to_string_view(std::declval<S>()))> {
+};
+
+template <typename S, typename = void> struct char_t_impl {};
+template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
+  using result = decltype(to_string_view(std::declval<S>()));
+  using type = typename result::value_type;
+};
+
+struct error_handler {
+  FMT_CONSTEXPR error_handler() = default;
+  FMT_CONSTEXPR error_handler(const error_handler&) = default;
+
+  // This function is intentionally not constexpr to give a compile-time error.
+  FMT_NORETURN FMT_API void on_error(const char* message);
+};
+}  // namespace internal
+
+/** String's character type. */
+template <typename S> using char_t = typename internal::char_t_impl<S>::type;
+
+/**
+  \rst
+  Parsing context consisting of a format string range being parsed and an
+  argument counter for automatic indexing.
+
+  You can use one of the following type aliases for common character types:
+
+  +-----------------------+-------------------------------------+
+  | Type                  | Definition                          |
+  +=======================+=====================================+
+  | format_parse_context  | basic_format_parse_context<char>    |
+  +-----------------------+-------------------------------------+
+  | wformat_parse_context | basic_format_parse_context<wchar_t> |
+  +-----------------------+-------------------------------------+
+  \endrst
+ */
+template <typename Char, typename ErrorHandler = internal::error_handler>
+class basic_format_parse_context : private ErrorHandler {
+ private:
+  basic_string_view<Char> format_str_;
+  int next_arg_id_;
+
+ public:
+  using char_type = Char;
+  using iterator = typename basic_string_view<Char>::iterator;
+
+  explicit FMT_CONSTEXPR basic_format_parse_context(
+      basic_string_view<Char> format_str, ErrorHandler eh = ErrorHandler())
+      : ErrorHandler(eh), format_str_(format_str), next_arg_id_(0) {}
+
+  /**
+    Returns an iterator to the beginning of the format string range being
+    parsed.
+   */
+  FMT_CONSTEXPR iterator begin() const FMT_NOEXCEPT {
+    return format_str_.begin();
+  }
+
+  /**
+    Returns an iterator past the end of the format string range being parsed.
+   */
+  FMT_CONSTEXPR iterator end() const FMT_NOEXCEPT { return format_str_.end(); }
+
+  /** Advances the begin iterator to ``it``. */
+  FMT_CONSTEXPR void advance_to(iterator it) {
+    format_str_.remove_prefix(internal::to_unsigned(it - begin()));
+  }
+
+  /**
+    Reports an error if using the manual argument indexing; otherwise returns
+    the next argument index and switches to the automatic indexing.
+   */
+  FMT_CONSTEXPR int next_arg_id() {
+    if (next_arg_id_ >= 0) return next_arg_id_++;
+    on_error("cannot switch from manual to automatic argument indexing");
+    return 0;
+  }
+
+  /**
+    Reports an error if using the automatic argument indexing; otherwise
+    switches to the manual indexing.
+   */
+  FMT_CONSTEXPR void check_arg_id(int) {
+    if (next_arg_id_ > 0)
+      on_error("cannot switch from automatic to manual argument indexing");
+    else
+      next_arg_id_ = -1;
+  }
+
+  FMT_CONSTEXPR void check_arg_id(basic_string_view<Char>) {}
+
+  FMT_CONSTEXPR void on_error(const char* message) {
+    ErrorHandler::on_error(message);
+  }
+
+  FMT_CONSTEXPR ErrorHandler error_handler() const { return *this; }
+};
+
+using format_parse_context = basic_format_parse_context<char>;
+using wformat_parse_context = basic_format_parse_context<wchar_t>;
+
+template <typename Char, typename ErrorHandler = internal::error_handler>
+using basic_parse_context FMT_DEPRECATED_ALIAS =
+    basic_format_parse_context<Char, ErrorHandler>;
+using parse_context FMT_DEPRECATED_ALIAS = basic_format_parse_context<char>;
+using wparse_context FMT_DEPRECATED_ALIAS = basic_format_parse_context<wchar_t>;
+
+template <typename Context> class basic_format_arg;
+template <typename Context> class basic_format_args;
+
+// A formatter for objects of type T.
+template <typename T, typename Char = char, typename Enable = void>
+struct formatter {
+  // A deleted default constructor indicates a disabled formatter.
+  formatter() = delete;
+};
+
+template <typename T, typename Char, typename Enable = void>
+struct FMT_DEPRECATED convert_to_int
+    : bool_constant<!std::is_arithmetic<T>::value &&
+                    std::is_convertible<T, int>::value> {};
+
+// Specifies if T has an enabled formatter specialization. A type can be
+// formattable even if it doesn't have a formatter e.g. via a conversion.
+template <typename T, typename Context>
+using has_formatter =
+    std::is_constructible<typename Context::template formatter_type<T>>;
+
+namespace internal {
+
+/** A contiguous memory buffer with an optional growing ability. */
+template <typename T> class buffer {
+ private:
+  T* ptr_;
+  std::size_t size_;
+  std::size_t capacity_;
+
+ protected:
+  // Don't initialize ptr_ since it is not accessed to save a few cycles.
+  buffer(std::size_t sz) FMT_NOEXCEPT : size_(sz), capacity_(sz) {}
+
+  buffer(T* p = nullptr, std::size_t sz = 0, std::size_t cap = 0) FMT_NOEXCEPT
+      : ptr_(p),
+        size_(sz),
+        capacity_(cap) {}
+
+  /** Sets the buffer data and capacity. */
+  void set(T* buf_data, std::size_t buf_capacity) FMT_NOEXCEPT {
+    ptr_ = buf_data;
+    capacity_ = buf_capacity;
+  }
+
+  /** Increases the buffer capacity to hold at least *capacity* elements. */
+  virtual void grow(std::size_t capacity) = 0;
+
+ public:
+  using value_type = T;
+  using const_reference = const T&;
+
+  buffer(const buffer&) = delete;
+  void operator=(const buffer&) = delete;
+  virtual ~buffer() = default;
+
+  T* begin() FMT_NOEXCEPT { return ptr_; }
+  T* end() FMT_NOEXCEPT { return ptr_ + size_; }
+
+  const T* begin() const FMT_NOEXCEPT { return ptr_; }
+  const T* end() const FMT_NOEXCEPT { return ptr_ + size_; }
+
+  /** Returns the size of this buffer. */
+  std::size_t size() const FMT_NOEXCEPT { return size_; }
+
+  /** Returns the capacity of this buffer. */
+  std::size_t capacity() const FMT_NOEXCEPT { return capacity_; }
+
+  /** Returns a pointer to the buffer data. */
+  T* data() FMT_NOEXCEPT { return ptr_; }
+
+  /** Returns a pointer to the buffer data. */
+  const T* data() const FMT_NOEXCEPT { return ptr_; }
+
+  /**
+    Resizes the buffer. If T is a POD type new elements may not be initialized.
+   */
+  void resize(std::size_t new_size) {
+    reserve(new_size);
+    size_ = new_size;
+  }
+
+  /** Clears this buffer. */
+  void clear() { size_ = 0; }
+
+  /** Reserves space to store at least *capacity* elements. */
+  void reserve(std::size_t new_capacity) {
+    if (new_capacity > capacity_) grow(new_capacity);
+  }
+
+  void push_back(const T& value) {
+    reserve(size_ + 1);
+    ptr_[size_++] = value;
+  }
+
+  /** Appends data to the end of the buffer. */
+  template <typename U> void append(const U* begin, const U* end);
+
+  template <typename I> T& operator[](I index) { return ptr_[index]; }
+  template <typename I> const T& operator[](I index) const {
+    return ptr_[index];
+  }
+};
+
+// A container-backed buffer.
+template <typename Container>
+class container_buffer : public buffer<typename Container::value_type> {
+ private:
+  Container& container_;
+
+ protected:
+  void grow(std::size_t capacity) FMT_OVERRIDE {
+    container_.resize(capacity);
+    this->set(&container_[0], capacity);
+  }
+
+ public:
+  explicit container_buffer(Container& c)
+      : buffer<typename Container::value_type>(c.size()), container_(c) {}
+};
+
+// Extracts a reference to the container from back_insert_iterator.
+template <typename Container>
+inline Container& get_container(std::back_insert_iterator<Container> it) {
+  using bi_iterator = std::back_insert_iterator<Container>;
+  struct accessor : bi_iterator {
+    accessor(bi_iterator iter) : bi_iterator(iter) {}
+    using bi_iterator::container;
+  };
+  return *accessor(it).container;
+}
+
+template <typename T, typename Char = char, typename Enable = void>
+struct fallback_formatter {
+  fallback_formatter() = delete;
+};
+
+// Specifies if T has an enabled fallback_formatter specialization.
+template <typename T, typename Context>
+using has_fallback_formatter =
+    std::is_constructible<fallback_formatter<T, typename Context::char_type>>;
+
+template <typename Char> struct named_arg_base;
+template <typename T, typename Char> struct named_arg;
+
+enum class type {
+  none_type,
+  named_arg_type,
+  // Integer types should go first,
+  int_type,
+  uint_type,
+  long_long_type,
+  ulong_long_type,
+  int128_type,
+  uint128_type,
+  bool_type,
+  char_type,
+  last_integer_type = char_type,
+  // followed by floating-point types.
+  float_type,
+  double_type,
+  long_double_type,
+  last_numeric_type = long_double_type,
+  cstring_type,
+  string_type,
+  pointer_type,
+  custom_type
+};
+
+// Maps core type T to the corresponding type enum constant.
+template <typename T, typename Char>
+struct type_constant : std::integral_constant<type, type::custom_type> {};
+
+#define FMT_TYPE_CONSTANT(Type, constant) \
+  template <typename Char>                \
+  struct type_constant<Type, Char>        \
+      : std::integral_constant<type, type::constant> {}
+
+FMT_TYPE_CONSTANT(const named_arg_base<Char>&, named_arg_type);
+FMT_TYPE_CONSTANT(int, int_type);
+FMT_TYPE_CONSTANT(unsigned, uint_type);
+FMT_TYPE_CONSTANT(long long, long_long_type);
+FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type);
+FMT_TYPE_CONSTANT(int128_t, int128_type);
+FMT_TYPE_CONSTANT(uint128_t, uint128_type);
+FMT_TYPE_CONSTANT(bool, bool_type);
+FMT_TYPE_CONSTANT(Char, char_type);
+FMT_TYPE_CONSTANT(float, float_type);
+FMT_TYPE_CONSTANT(double, double_type);
+FMT_TYPE_CONSTANT(long double, long_double_type);
+FMT_TYPE_CONSTANT(const Char*, cstring_type);
+FMT_TYPE_CONSTANT(basic_string_view<Char>, string_type);
+FMT_TYPE_CONSTANT(const void*, pointer_type);
+
+FMT_CONSTEXPR bool is_integral_type(type t) {
+  FMT_ASSERT(t != type::named_arg_type, "invalid argument type");
+  return t > type::none_type && t <= type::last_integer_type;
+}
+
+FMT_CONSTEXPR bool is_arithmetic_type(type t) {
+  FMT_ASSERT(t != type::named_arg_type, "invalid argument type");
+  return t > type::none_type && t <= type::last_numeric_type;
+}
+
+template <typename Char> struct string_value {
+  const Char* data;
+  std::size_t size;
+};
+
+template <typename Context> struct custom_value {
+  using parse_context = basic_format_parse_context<typename Context::char_type>;
+  const void* value;
+  void (*format)(const void* arg,
+                 typename Context::parse_context_type& parse_ctx, Context& ctx);
+};
+
+// A formatting argument value.
+template <typename Context> class value {
+ public:
+  using char_type = typename Context::char_type;
+
+  union {
+    int int_value;
+    unsigned uint_value;
+    long long long_long_value;
+    unsigned long long ulong_long_value;
+    int128_t int128_value;
+    uint128_t uint128_value;
+    bool bool_value;
+    char_type char_value;
+    float float_value;
+    double double_value;
+    long double long_double_value;
+    const void* pointer;
+    string_value<char_type> string;
+    custom_value<Context> custom;
+    const named_arg_base<char_type>* named_arg;
+  };
+
+  FMT_CONSTEXPR value(int val = 0) : int_value(val) {}
+  FMT_CONSTEXPR value(unsigned val) : uint_value(val) {}
+  value(long long val) : long_long_value(val) {}
+  value(unsigned long long val) : ulong_long_value(val) {}
+  value(int128_t val) : int128_value(val) {}
+  value(uint128_t val) : uint128_value(val) {}
+  value(float val) : float_value(val) {}
+  value(double val) : double_value(val) {}
+  value(long double val) : long_double_value(val) {}
+  value(bool val) : bool_value(val) {}
+  value(char_type val) : char_value(val) {}
+  value(const char_type* val) { string.data = val; }
+  value(basic_string_view<char_type> val) {
+    string.data = val.data();
+    string.size = val.size();
+  }
+  value(const void* val) : pointer(val) {}
+
+  template <typename T> value(const T& val) {
+    custom.value = &val;
+    // Get the formatter type through the context to allow different contexts
+    // have different extension points, e.g. `formatter<T>` for `format` and
+    // `printf_formatter<T>` for `printf`.
+    custom.format = format_custom_arg<
+        T, conditional_t<has_formatter<T, Context>::value,
+                         typename Context::template formatter_type<T>,
+                         fallback_formatter<T, char_type>>>;
+  }
+
+  value(const named_arg_base<char_type>& val) { named_arg = &val; }
+
+ private:
+  // Formats an argument of a custom type, such as a user-defined class.
+  template <typename T, typename Formatter>
+  static void format_custom_arg(const void* arg,
+                                typename Context::parse_context_type& parse_ctx,
+                                Context& ctx) {
+    Formatter f;
+    parse_ctx.advance_to(f.parse(parse_ctx));
+    ctx.advance_to(f.format(*static_cast<const T*>(arg), ctx));
+  }
+};
+
+template <typename Context, typename T>
+FMT_CONSTEXPR basic_format_arg<Context> make_arg(const T& value);
+
+// To minimize the number of types we need to deal with, long is translated
+// either to int or to long long depending on its size.
+enum { long_short = sizeof(long) == sizeof(int) };
+using long_type = conditional_t<long_short, int, long long>;
+using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
+
+// Maps formatting arguments to core types.
+template <typename Context> struct arg_mapper {
+  using char_type = typename Context::char_type;
+
+  FMT_CONSTEXPR int map(signed char val) { return val; }
+  FMT_CONSTEXPR unsigned map(unsigned char val) { return val; }
+  FMT_CONSTEXPR int map(short val) { return val; }
+  FMT_CONSTEXPR unsigned map(unsigned short val) { return val; }
+  FMT_CONSTEXPR int map(int val) { return val; }
+  FMT_CONSTEXPR unsigned map(unsigned val) { return val; }
+  FMT_CONSTEXPR long_type map(long val) { return val; }
+  FMT_CONSTEXPR ulong_type map(unsigned long val) { return val; }
+  FMT_CONSTEXPR long long map(long long val) { return val; }
+  FMT_CONSTEXPR unsigned long long map(unsigned long long val) { return val; }
+  FMT_CONSTEXPR int128_t map(int128_t val) { return val; }
+  FMT_CONSTEXPR uint128_t map(uint128_t val) { return val; }
+  FMT_CONSTEXPR bool map(bool val) { return val; }
+
+  template <typename T, FMT_ENABLE_IF(is_char<T>::value)>
+  FMT_CONSTEXPR char_type map(T val) {
+    static_assert(
+        std::is_same<T, char>::value || std::is_same<T, char_type>::value,
+        "mixing character types is disallowed");
+    return val;
+  }
+
+  FMT_CONSTEXPR float map(float val) { return val; }
+  FMT_CONSTEXPR double map(double val) { return val; }
+  FMT_CONSTEXPR long double map(long double val) { return val; }
+
+  FMT_CONSTEXPR const char_type* map(char_type* val) { return val; }
+  FMT_CONSTEXPR const char_type* map(const char_type* val) { return val; }
+  template <typename T, FMT_ENABLE_IF(is_string<T>::value)>
+  FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
+    static_assert(std::is_same<char_type, char_t<T>>::value,
+                  "mixing character types is disallowed");
+    return to_string_view(val);
+  }
+  template <typename T,
+            FMT_ENABLE_IF(
+                std::is_constructible<basic_string_view<char_type>, T>::value &&
+                !is_string<T>::value && !has_formatter<T, Context>::value &&
+                !has_fallback_formatter<T, Context>::value)>
+  FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
+    return basic_string_view<char_type>(val);
+  }
+  template <
+      typename T,
+      FMT_ENABLE_IF(
+          std::is_constructible<std_string_view<char_type>, T>::value &&
+          !std::is_constructible<basic_string_view<char_type>, T>::value &&
+          !is_string<T>::value && !has_formatter<T, Context>::value &&
+          !has_fallback_formatter<T, Context>::value)>
+  FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
+    return std_string_view<char_type>(val);
+  }
+  FMT_CONSTEXPR const char* map(const signed char* val) {
+    static_assert(std::is_same<char_type, char>::value, "invalid string type");
+    return reinterpret_cast<const char*>(val);
+  }
+  FMT_CONSTEXPR const char* map(const unsigned char* val) {
+    static_assert(std::is_same<char_type, char>::value, "invalid string type");
+    return reinterpret_cast<const char*>(val);
+  }
+
+  FMT_CONSTEXPR const void* map(void* val) { return val; }
+  FMT_CONSTEXPR const void* map(const void* val) { return val; }
+  FMT_CONSTEXPR const void* map(std::nullptr_t val) { return val; }
+  template <typename T> FMT_CONSTEXPR int map(const T*) {
+    // Formatting of arbitrary pointers is disallowed. If you want to output
+    // a pointer cast it to "void *" or "const void *". In particular, this
+    // forbids formatting of "[const] volatile char *" which is printed as bool
+    // by iostreams.
+    static_assert(!sizeof(T), "formatting of non-void pointers is disallowed");
+    return 0;
+  }
+
+  template <typename T,
+            FMT_ENABLE_IF(std::is_enum<T>::value &&
+                          !has_formatter<T, Context>::value &&
+                          !has_fallback_formatter<T, Context>::value)>
+  FMT_CONSTEXPR auto map(const T& val)
+      -> decltype(std::declval<arg_mapper>().map(
+          static_cast<typename std::underlying_type<T>::type>(val))) {
+    return map(static_cast<typename std::underlying_type<T>::type>(val));
+  }
+  template <typename T,
+            FMT_ENABLE_IF(!is_string<T>::value && !is_char<T>::value &&
+                          (has_formatter<T, Context>::value ||
+                           has_fallback_formatter<T, Context>::value))>
+  FMT_CONSTEXPR const T& map(const T& val) {
+    return val;
+  }
+
+  template <typename T>
+  FMT_CONSTEXPR const named_arg_base<char_type>& map(
+      const named_arg<T, char_type>& val) {
+    auto arg = make_arg<Context>(val.value);
+    std::memcpy(val.data, &arg, sizeof(arg));
+    return val;
+  }
+
+  int map(...) {
+    constexpr bool formattable = sizeof(Context) == 0;
+    static_assert(
+        formattable,
+        "Cannot format argument. To make type T formattable provide a "
+        "formatter<T> specialization: "
+        "https://fmt.dev/latest/api.html#formatting-user-defined-types");
+    return 0;
+  }
+};
+
+// A type constant after applying arg_mapper<Context>.
+template <typename T, typename Context>
+using mapped_type_constant =
+    type_constant<decltype(arg_mapper<Context>().map(std::declval<const T&>())),
+                  typename Context::char_type>;
+
+enum { packed_arg_bits = 5 };
+// Maximum number of arguments with packed types.
+enum { max_packed_args = 63 / packed_arg_bits };
+enum : unsigned long long { is_unpacked_bit = 1ULL << 63 };
+
+template <typename Context> class arg_map;
+}  // namespace internal
+
+// A formatting argument. It is a trivially copyable/constructible type to
+// allow storage in basic_memory_buffer.
+template <typename Context> class basic_format_arg {
+ private:
+  internal::value<Context> value_;
+  internal::type type_;
+
+  template <typename ContextType, typename T>
+  friend FMT_CONSTEXPR basic_format_arg<ContextType> internal::make_arg(
+      const T& value);
+
+  template <typename Visitor, typename Ctx>
+  friend FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis,
+                                             const basic_format_arg<Ctx>& arg)
+      -> decltype(vis(0));
+
+  friend class basic_format_args<Context>;
+  friend class internal::arg_map<Context>;
+
+  using char_type = typename Context::char_type;
+
+ public:
+  class handle {
+   public:
+    explicit handle(internal::custom_value<Context> custom) : custom_(custom) {}
+
+    void format(typename Context::parse_context_type& parse_ctx,
+                Context& ctx) const {
+      custom_.format(custom_.value, parse_ctx, ctx);
+    }
+
+   private:
+    internal::custom_value<Context> custom_;
+  };
+
+  FMT_CONSTEXPR basic_format_arg() : type_(internal::type::none_type) {}
+
+  FMT_CONSTEXPR explicit operator bool() const FMT_NOEXCEPT {
+    return type_ != internal::type::none_type;
+  }
+
+  internal::type type() const { return type_; }
+
+  bool is_integral() const { return internal::is_integral_type(type_); }
+  bool is_arithmetic() const { return internal::is_arithmetic_type(type_); }
+};
+
+/**
+  \rst
+  Visits an argument dispatching to the appropriate visit method based on
+  the argument type. For example, if the argument type is ``double`` then
+  ``vis(value)`` will be called with the value of type ``double``.
+  \endrst
+ */
+template <typename Visitor, typename Context>
+FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis,
+                                    const basic_format_arg<Context>& arg)
+    -> decltype(vis(0)) {
+  using char_type = typename Context::char_type;
+  switch (arg.type_) {
+  case internal::type::none_type:
+    break;
+  case internal::type::named_arg_type:
+    FMT_ASSERT(false, "invalid argument type");
+    break;
+  case internal::type::int_type:
+    return vis(arg.value_.int_value);
+  case internal::type::uint_type:
+    return vis(arg.value_.uint_value);
+  case internal::type::long_long_type:
+    return vis(arg.value_.long_long_value);
+  case internal::type::ulong_long_type:
+    return vis(arg.value_.ulong_long_value);
+#if FMT_USE_INT128
+  case internal::type::int128_type:
+    return vis(arg.value_.int128_value);
+  case internal::type::uint128_type:
+    return vis(arg.value_.uint128_value);
+#else
+  case internal::type::int128_type:
+  case internal::type::uint128_type:
+    break;
+#endif
+  case internal::type::bool_type:
+    return vis(arg.value_.bool_value);
+  case internal::type::char_type:
+    return vis(arg.value_.char_value);
+  case internal::type::float_type:
+    return vis(arg.value_.float_value);
+  case internal::type::double_type:
+    return vis(arg.value_.double_value);
+  case internal::type::long_double_type:
+    return vis(arg.value_.long_double_value);
+  case internal::type::cstring_type:
+    return vis(arg.value_.string.data);
+  case internal::type::string_type:
+    return vis(basic_string_view<char_type>(arg.value_.string.data,
+                                            arg.value_.string.size));
+  case internal::type::pointer_type:
+    return vis(arg.value_.pointer);
+  case internal::type::custom_type:
+    return vis(typename basic_format_arg<Context>::handle(arg.value_.custom));
+  }
+  return vis(monostate());
+}
+
+namespace internal {
+// A map from argument names to their values for named arguments.
+template <typename Context> class arg_map {
+ private:
+  using char_type = typename Context::char_type;
+
+  struct entry {
+    basic_string_view<char_type> name;
+    basic_format_arg<Context> arg;
+  };
+
+  entry* map_;
+  unsigned size_;
+
+  void push_back(value<Context> val) {
+    const auto& named = *val.named_arg;
+    map_[size_] = {named.name, named.template deserialize<Context>()};
+    ++size_;
+  }
+
+ public:
+  arg_map(const arg_map&) = delete;
+  void operator=(const arg_map&) = delete;
+  arg_map() : map_(nullptr), size_(0) {}
+  void init(const basic_format_args<Context>& args);
+  ~arg_map() { delete[] map_; }
+
+  basic_format_arg<Context> find(basic_string_view<char_type> name) const {
+    // The list is unsorted, so just return the first matching name.
+    for (entry *it = map_, *end = map_ + size_; it != end; ++it) {
+      if (it->name == name) return it->arg;
+    }
+    return {};
+  }
+};
+
+// A type-erased reference to an std::locale to avoid heavy <locale> include.
+class locale_ref {
+ private:
+  const void* locale_;  // A type-erased pointer to std::locale.
+
+ public:
+  locale_ref() : locale_(nullptr) {}
+  template <typename Locale> explicit locale_ref(const Locale& loc);
+
+  explicit operator bool() const FMT_NOEXCEPT { return locale_ != nullptr; }
+
+  template <typename Locale> Locale get() const;
+};
+
+template <typename> constexpr unsigned long long encode_types() { return 0; }
+
+template <typename Context, typename Arg, typename... Args>
+constexpr unsigned long long encode_types() {
+  return static_cast<unsigned>(mapped_type_constant<Arg, Context>::value) |
+         (encode_types<Context, Args...>() << packed_arg_bits);
+}
+
+template <typename Context, typename T>
+FMT_CONSTEXPR basic_format_arg<Context> make_arg(const T& value) {
+  basic_format_arg<Context> arg;
+  arg.type_ = mapped_type_constant<T, Context>::value;
+  arg.value_ = arg_mapper<Context>().map(value);
+  return arg;
+}
+
+// The type template parameter is there to avoid an ODR violation when using
+// a fallback formatter in one translation unit and an implicit conversion in
+// another (not recommended).
+template <bool IS_PACKED, typename Context, type, typename T,
+          FMT_ENABLE_IF(IS_PACKED)>
+inline value<Context> make_arg(const T& val) {
+  return arg_mapper<Context>().map(val);
+}
+
+template <bool IS_PACKED, typename Context, type, typename T,
+          FMT_ENABLE_IF(!IS_PACKED)>
+inline basic_format_arg<Context> make_arg(const T& value) {
+  return make_arg<Context>(value);
+}
+
+template <typename T> struct is_reference_wrapper : std::false_type {};
+
+template <typename T>
+struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
+
+class dynamic_arg_list {
+  // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
+  // templates it doesn't complain about inability to deduce single translation
+  // unit for placing vtable. So storage_node_base is made a fake template.
+  template <typename = void> struct node {
+    virtual ~node() = default;
+    std::unique_ptr<node<>> next;
+  };
+
+  template <typename T> struct typed_node : node<> {
+    T value;
+
+    template <typename Arg>
+    FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {}
+
+    template <typename Char>
+    FMT_CONSTEXPR typed_node(const basic_string_view<Char>& arg)
+        : value(arg.data(), arg.size()) {}
+  };
+
+  std::unique_ptr<node<>> head_;
+
+ public:
+  template <typename T, typename Arg> const T& push(const Arg& arg) {
+    auto node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
+    auto& value = node->value;
+    node->next = std::move(head_);
+    head_ = std::move(node);
+    return value;
+  }
+};
+}  // namespace internal
+
+// Formatting context.
+template <typename OutputIt, typename Char> class basic_format_context {
+ public:
+  /** The character type for the output. */
+  using char_type = Char;
+
+ private:
+  OutputIt out_;
+  basic_format_args<basic_format_context> args_;
+  internal::arg_map<basic_format_context> map_;
+  internal::locale_ref loc_;
+
+ public:
+  using iterator = OutputIt;
+  using format_arg = basic_format_arg<basic_format_context>;
+  using parse_context_type = basic_format_parse_context<Char>;
+  template <typename T> using formatter_type = formatter<T, char_type>;
+
+  basic_format_context(const basic_format_context&) = delete;
+  void operator=(const basic_format_context&) = delete;
+  /**
+   Constructs a ``basic_format_context`` object. References to the arguments are
+   stored in the object so make sure they have appropriate lifetimes.
+   */
+  basic_format_context(OutputIt out,
+                       basic_format_args<basic_format_context> ctx_args,
+                       internal::locale_ref loc = internal::locale_ref())
+      : out_(out), args_(ctx_args), loc_(loc) {}
+
+  format_arg arg(int id) const { return args_.get(id); }
+
+  // Checks if manual indexing is used and returns the argument with the
+  // specified name.
+  format_arg arg(basic_string_view<char_type> name);
+
+  internal::error_handler error_handler() { return {}; }
+  void on_error(const char* message) { error_handler().on_error(message); }
+
+  // Returns an iterator to the beginning of the output range.
+  iterator out() { return out_; }
+
+  // Advances the begin iterator to ``it``.
+  void advance_to(iterator it) { out_ = it; }
+
+  internal::locale_ref locale() { return loc_; }
+};
+
+template <typename Char>
+using buffer_context =
+    basic_format_context<std::back_insert_iterator<internal::buffer<Char>>,
+                         Char>;
+using format_context = buffer_context<char>;
+using wformat_context = buffer_context<wchar_t>;
+
+/**
+  \rst
+  An array of references to arguments. It can be implicitly converted into
+  `~fmt::basic_format_args` for passing into type-erased formatting functions
+  such as `~fmt::vformat`.
+  \endrst
+ */
+template <typename Context, typename... Args>
+class format_arg_store
+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
+    // Workaround a GCC template argument substitution bug.
+    : public basic_format_args<Context>
+#endif
+{
+ private:
+  static const size_t num_args = sizeof...(Args);
+  static const bool is_packed = num_args < internal::max_packed_args;
+
+  using value_type = conditional_t<is_packed, internal::value<Context>,
+                                   basic_format_arg<Context>>;
+
+  // If the arguments are not packed, add one more element to mark the end.
+  value_type data_[num_args + (num_args == 0 ? 1 : 0)];
+
+  friend class basic_format_args<Context>;
+
+ public:
+  static constexpr unsigned long long types =
+      is_packed ? internal::encode_types<Context, Args...>()
+                : internal::is_unpacked_bit | num_args;
+
+  format_arg_store(const Args&... args)
+      :
+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
+        basic_format_args<Context>(*this),
+#endif
+        data_{internal::make_arg<
+            is_packed, Context,
+            internal::mapped_type_constant<Args, Context>::value>(args)...} {
+  }
+};
+
+/**
+  \rst
+  Constructs an `~fmt::format_arg_store` object that contains references to
+  arguments and can be implicitly converted to `~fmt::format_args`. `Context`
+  can be omitted in which case it defaults to `~fmt::context`.
+  See `~fmt::arg` for lifetime considerations.
+  \endrst
+ */
+template <typename Context = format_context, typename... Args>
+inline format_arg_store<Context, Args...> make_format_args(
+    const Args&... args) {
+  return {args...};
+}
+
+/**
+  \rst
+  A dynamic version of `fmt::format_arg_store<>`.
+  It's equipped with a storage to potentially temporary objects which lifetime
+  could be shorter than the format arguments object.
+
+  It can be implicitly converted into `~fmt::basic_format_args` for passing
+  into type-erased formatting functions such as `~fmt::vformat`.
+  \endrst
+ */
+template <typename Context>
+class dynamic_format_arg_store
+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
+    // Workaround a GCC template argument substitution bug.
+    : public basic_format_args<Context>
+#endif
+{
+ private:
+  using char_type = typename Context::char_type;
+
+  template <typename T> struct need_copy {
+    static constexpr internal::type mapped_type =
+        internal::mapped_type_constant<T, Context>::value;
+
+    enum {
+      value = !(internal::is_reference_wrapper<T>::value ||
+                std::is_same<T, basic_string_view<char_type>>::value ||
+                std::is_same<T, internal::std_string_view<char_type>>::value ||
+                (mapped_type != internal::type::cstring_type &&
+                 mapped_type != internal::type::string_type &&
+                 mapped_type != internal::type::custom_type &&
+                 mapped_type != internal::type::named_arg_type))
+    };
+  };
+
+  template <typename T>
+  using stored_type = conditional_t<internal::is_string<T>::value,
+                                    std::basic_string<char_type>, T>;
+
+  // Storage of basic_format_arg must be contiguous.
+  std::vector<basic_format_arg<Context>> data_;
+
+  // Storage of arguments not fitting into basic_format_arg must grow
+  // without relocation because items in data_ refer to it.
+  internal::dynamic_arg_list dynamic_args_;
+
+  friend class basic_format_args<Context>;
+
+  unsigned long long get_types() const {
+    return internal::is_unpacked_bit | data_.size();
+  }
+
+  template <typename T> void emplace_arg(const T& arg) {
+    data_.emplace_back(internal::make_arg<Context>(arg));
+  }
+
+ public:
+  /**
+    \rst
+    Adds an argument into the dynamic store for later passing to a formating
+    function.
+
+    Note that custom types and string types (but not string views!) are copied
+    into the store with dynamic memory (in addition to resizing vector).
+
+    **Example**::
+
+      fmt::dynamic_format_arg_store<fmt::format_context> store;
+      store.push_back(42);
+      store.push_back("abc");
+      store.push_back(1.5f);
+      std::string result = fmt::vformat("{} and {} and {}", store);
+    \endrst
+  */
+  template <typename T> void push_back(const T& arg) {
+    static_assert(
+        !std::is_base_of<internal::named_arg_base<char_type>, T>::value,
+        "named arguments are not supported yet");
+    if (internal::const_check(need_copy<T>::value))
+      emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
+    else
+      emplace_arg(arg);
+  }
+
+  /**
+    Adds a reference to the argument into the dynamic store for later passing to
+    a formating function.
+  */
+  template <typename T> void push_back(std::reference_wrapper<T> arg) {
+    static_assert(
+        need_copy<T>::value,
+        "objects of built-in types and string views are always copied");
+    emplace_arg(arg.get());
+  }
+};
+
+/**
+  \rst
+  A view of a collection of formatting arguments. To avoid lifetime issues it
+  should only be used as a parameter type in type-erased functions such as
+  ``vformat``::
+
+    void vlog(string_view format_str, format_args args);  // OK
+    format_args args = make_format_args(42);  // Error: dangling reference
+  \endrst
+ */
+template <typename Context> class basic_format_args {
+ public:
+  using size_type = int;
+  using format_arg = basic_format_arg<Context>;
+
+ private:
+  // To reduce compiled code size per formatting function call, types of first
+  // max_packed_args arguments are passed in the types_ field.
+  unsigned long long types_;
+  union {
+    // If the number of arguments is less than max_packed_args, the argument
+    // values are stored in values_, otherwise they are stored in args_.
+    // This is done to reduce compiled code size as storing larger objects
+    // may require more code (at least on x86-64) even if the same amount of
+    // data is actually copied to stack. It saves ~10% on the bloat test.
+    const internal::value<Context>* values_;
+    const format_arg* args_;
+  };
+
+  bool is_packed() const { return (types_ & internal::is_unpacked_bit) == 0; }
+
+  internal::type type(int index) const {
+    int shift = index * internal::packed_arg_bits;
+    unsigned int mask = (1 << internal::packed_arg_bits) - 1;
+    return static_cast<internal::type>((types_ >> shift) & mask);
+  }
+
+  friend class internal::arg_map<Context>;
+
+  void set_data(const internal::value<Context>* values) { values_ = values; }
+  void set_data(const format_arg* args) { args_ = args; }
+
+  format_arg do_get(int index) const {
+    format_arg arg;
+    if (!is_packed()) {
+      auto num_args = max_size();
+      if (index < num_args) arg = args_[index];
+      return arg;
+    }
+    if (index > internal::max_packed_args) return arg;
+    arg.type_ = type(index);
+    if (arg.type_ == internal::type::none_type) return arg;
+    internal::value<Context>& val = arg.value_;
+    val = values_[index];
+    return arg;
+  }
+
+ public:
+  basic_format_args() : types_(0) {}
+
+  /**
+   \rst
+   Constructs a `basic_format_args` object from `~fmt::format_arg_store`.
+   \endrst
+   */
+  template <typename... Args>
+  basic_format_args(const format_arg_store<Context, Args...>& store)
+      : types_(store.types) {
+    set_data(store.data_);
+  }
+
+  /**
+   \rst
+   Constructs a `basic_format_args` object from
+   `~fmt::dynamic_format_arg_store`.
+   \endrst
+   */
+  basic_format_args(const dynamic_format_arg_store<Context>& store)
+      : types_(store.get_types()) {
+    set_data(store.data_.data());
+  }
+
+  /**
+   \rst
+   Constructs a `basic_format_args` object from a dynamic set of arguments.
+   \endrst
+   */
+  basic_format_args(const format_arg* args, int count)
+      : types_(internal::is_unpacked_bit | internal::to_unsigned(count)) {
+    set_data(args);
+  }
+
+  /** Returns the argument at specified index. */
+  format_arg get(int index) const {
+    format_arg arg = do_get(index);
+    if (arg.type_ == internal::type::named_arg_type)
+      arg = arg.value_.named_arg->template deserialize<Context>();
+    return arg;
+  }
+
+  int max_size() const {
+    unsigned long long max_packed = internal::max_packed_args;
+    return static_cast<int>(is_packed() ? max_packed
+                                        : types_ & ~internal::is_unpacked_bit);
+  }
+};
+
+/** An alias to ``basic_format_args<context>``. */
+// It is a separate type rather than an alias to make symbols readable.
+struct format_args : basic_format_args<format_context> {
+  template <typename... Args>
+  format_args(Args&&... args)
+      : basic_format_args<format_context>(static_cast<Args&&>(args)...) {}
+};
+struct wformat_args : basic_format_args<wformat_context> {
+  template <typename... Args>
+  wformat_args(Args&&... args)
+      : basic_format_args<wformat_context>(static_cast<Args&&>(args)...) {}
+};
+
+template <typename Container> struct is_contiguous : std::false_type {};
+
+template <typename Char>
+struct is_contiguous<std::basic_string<Char>> : std::true_type {};
+
+template <typename Char>
+struct is_contiguous<internal::buffer<Char>> : std::true_type {};
+
+namespace internal {
+
+template <typename OutputIt>
+struct is_contiguous_back_insert_iterator : std::false_type {};
+template <typename Container>
+struct is_contiguous_back_insert_iterator<std::back_insert_iterator<Container>>
+    : is_contiguous<Container> {};
+
+template <typename Char> struct named_arg_base {
+  basic_string_view<Char> name;
+
+  // Serialized value<context>.
+  mutable char data[sizeof(basic_format_arg<buffer_context<Char>>)];
+
+  named_arg_base(basic_string_view<Char> nm) : name(nm) {}
+
+  template <typename Context> basic_format_arg<Context> deserialize() const {
+    basic_format_arg<Context> arg;
+    std::memcpy(&arg, data, sizeof(basic_format_arg<Context>));
+    return arg;
+  }
+};
+
+struct view {};
+
+template <typename T, typename Char>
+struct named_arg : view, named_arg_base<Char> {
+  const T& value;
+
+  named_arg(basic_string_view<Char> name, const T& val)
+      : named_arg_base<Char>(name), value(val) {}
+};
+
+template <typename..., typename S, FMT_ENABLE_IF(!is_compile_string<S>::value)>
+inline void check_format_string(const S&) {
+#if defined(FMT_ENFORCE_COMPILE_STRING)
+  static_assert(is_compile_string<S>::value,
+                "FMT_ENFORCE_COMPILE_STRING requires all format strings to "
+                "utilize FMT_STRING() or fmt().");
+#endif
+}
+template <typename..., typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
+void check_format_string(S);
+
+template <bool...> struct bool_pack;
+template <bool... Args>
+using all_true =
+    std::is_same<bool_pack<Args..., true>, bool_pack<true, Args...>>;
+
+template <typename... Args, typename S, typename Char = char_t<S>>
+inline format_arg_store<buffer_context<Char>, remove_reference_t<Args>...>
+make_args_checked(const S& format_str,
+                  const remove_reference_t<Args>&... args) {
+  static_assert(
+      all_true<(!std::is_base_of<view, remove_reference_t<Args>>::value ||
+                !std::is_reference<Args>::value)...>::value,
+      "passing views as lvalues is disallowed");
+  check_format_string<Args...>(format_str);
+  return {args...};
+}
+
+template <typename Char>
+std::basic_string<Char> vformat(
+    basic_string_view<Char> format_str,
+    basic_format_args<buffer_context<type_identity_t<Char>>> args);
+
+template <typename Char>
+typename buffer_context<Char>::iterator vformat_to(
+    buffer<Char>& buf, basic_string_view<Char> format_str,
+    basic_format_args<buffer_context<type_identity_t<Char>>> args);
+
+template <typename Char, typename Args,
+          FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
+inline void vprint_mojibake(std::FILE*, basic_string_view<Char>, const Args&) {}
+
+FMT_API void vprint_mojibake(std::FILE*, string_view, format_args);
+#ifndef _WIN32
+inline void vprint_mojibake(std::FILE*, string_view, format_args) {}
+#endif
+}  // namespace internal
+
+/**
+  \rst
+  Returns a named argument to be used in a formatting function. It should only
+  be used in a call to a formatting function.
+
+  **Example**::
+
+    fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23));
+  \endrst
+ */
+template <typename S, typename T, typename Char = char_t<S>>
+inline internal::named_arg<T, Char> arg(const S& name, const T& arg) {
+  static_assert(internal::is_string<S>::value, "");
+  return {name, arg};
+}
+
+// Disable nested named arguments, e.g. ``arg("a", arg("b", 42))``.
+template <typename S, typename T, typename Char>
+void arg(S, internal::named_arg<T, Char>) = delete;
+
+/** Formats a string and writes the output to ``out``. */
+// GCC 8 and earlier cannot handle std::back_insert_iterator<Container> with
+// vformat_to<ArgFormatter>(...) overload, so SFINAE on iterator type instead.
+template <typename OutputIt, typename S, typename Char = char_t<S>,
+          FMT_ENABLE_IF(
+              internal::is_contiguous_back_insert_iterator<OutputIt>::value)>
+OutputIt vformat_to(
+    OutputIt out, const S& format_str,
+    basic_format_args<buffer_context<type_identity_t<Char>>> args) {
+  using container = remove_reference_t<decltype(internal::get_container(out))>;
+  internal::container_buffer<container> buf((internal::get_container(out)));
+  internal::vformat_to(buf, to_string_view(format_str), args);
+  return out;
+}
+
+template <typename Container, typename S, typename... Args,
+          FMT_ENABLE_IF(
+              is_contiguous<Container>::value&& internal::is_string<S>::value)>
+inline std::back_insert_iterator<Container> format_to(
+    std::back_insert_iterator<Container> out, const S& format_str,
+    Args&&... args) {
+  return vformat_to(out, to_string_view(format_str),
+                    internal::make_args_checked<Args...>(format_str, args...));
+}
+
+template <typename S, typename Char = char_t<S>>
+inline std::basic_string<Char> vformat(
+    const S& format_str,
+    basic_format_args<buffer_context<type_identity_t<Char>>> args) {
+  return internal::vformat(to_string_view(format_str), args);
+}
+
+/**
+  \rst
+  Formats arguments and returns the result as a string.
+
+  **Example**::
+
+    #include <fmt/core.h>
+    std::string message = fmt::format("The answer is {}", 42);
+  \endrst
+*/
+// Pass char_t as a default template parameter instead of using
+// std::basic_string<char_t<S>> to reduce the symbol size.
+template <typename S, typename... Args, typename Char = char_t<S>>
+inline std::basic_string<Char> format(const S& format_str, Args&&... args) {
+  return internal::vformat(
+      to_string_view(format_str),
+      internal::make_args_checked<Args...>(format_str, args...));
+}
+
+FMT_API void vprint(string_view, format_args);
+FMT_API void vprint(std::FILE*, string_view, format_args);
+
+/**
+  \rst
+  Formats ``args`` according to specifications in ``format_str`` and writes the
+  output to the file ``f``. Strings are assumed to be Unicode-encoded unless the
+  ``FMT_UNICODE`` macro is set to 0.
+
+  **Example**::
+
+    fmt::print(stderr, "Don't {}!", "panic");
+  \endrst
+ */
+template <typename S, typename... Args, typename Char = char_t<S>>
+inline void print(std::FILE* f, const S& format_str, Args&&... args) {
+  return internal::is_unicode<Char>()
+             ? vprint(f, to_string_view(format_str),
+                      internal::make_args_checked<Args...>(format_str, args...))
+             : internal::vprint_mojibake(
+                   f, to_string_view(format_str),
+                   internal::make_args_checked<Args...>(format_str, args...));
+}
+
+/**
+  \rst
+  Formats ``args`` according to specifications in ``format_str`` and writes
+  the output to ``stdout``. Strings are assumed to be Unicode-encoded unless
+  the ``FMT_UNICODE`` macro is set to 0.
+
+  **Example**::
+
+    fmt::print("Elapsed time: {0:.2f} seconds", 1.23);
+  \endrst
+ */
+template <typename S, typename... Args, typename Char = char_t<S>>
+inline void print(const S& format_str, Args&&... args) {
+  return internal::is_unicode<Char>()
+             ? vprint(to_string_view(format_str),
+                      internal::make_args_checked<Args...>(format_str, args...))
+             : internal::vprint_mojibake(
+                   stdout, to_string_view(format_str),
+                   internal::make_args_checked<Args...>(format_str, args...));
+}
+FMT_END_NAMESPACE
+
+#endif  // FMT_CORE_H_
diff --git a/GeoModelDBManager/fmt/format-inl.h b/GeoModelDBManager/fmt/format-inl.h
new file mode 100644
index 0000000000000000000000000000000000000000..f632714d81e9ae9855a686f657bef1c861f9ea3c
--- /dev/null
+++ b/GeoModelDBManager/fmt/format-inl.h
@@ -0,0 +1,1403 @@
+// Formatting library for C++ - implementation
+//
+// Copyright (c) 2012 - 2016, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_FORMAT_INL_H_
+#define FMT_FORMAT_INL_H_
+
+#include <cassert>
+#include <cctype>
+#include <climits>
+#include <cmath>
+#include <cstdarg>
+#include <cstring>  // for std::memmove
+#include <cwchar>
+
+#include "format.h"
+#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
+#  include <locale>
+#endif
+
+#ifdef _WIN32
+#  include <io.h>
+#  include <windows.h>
+#endif
+
+#ifdef _MSC_VER
+#  pragma warning(push)
+#  pragma warning(disable : 4702)  // unreachable code
+#endif
+
+// Dummy implementations of strerror_r and strerror_s called if corresponding
+// system functions are not available.
+inline fmt::internal::null<> strerror_r(int, char*, ...) { return {}; }
+inline fmt::internal::null<> strerror_s(char*, std::size_t, ...) { return {}; }
+
+FMT_BEGIN_NAMESPACE
+namespace internal {
+
+FMT_FUNC void assert_fail(const char* file, int line, const char* message) {
+  print(stderr, "{}:{}: assertion failed: {}", file, line, message);
+  std::abort();
+}
+
+#ifndef _MSC_VER
+#  define FMT_SNPRINTF snprintf
+#else  // _MSC_VER
+inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) {
+  va_list args;
+  va_start(args, format);
+  int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
+  va_end(args);
+  return result;
+}
+#  define FMT_SNPRINTF fmt_snprintf
+#endif  // _MSC_VER
+
+// A portable thread-safe version of strerror.
+// Sets buffer to point to a string describing the error code.
+// This can be either a pointer to a string stored in buffer,
+// or a pointer to some static immutable string.
+// Returns one of the following values:
+//   0      - success
+//   ERANGE - buffer is not large enough to store the error message
+//   other  - failure
+// Buffer should be at least of size 1.
+FMT_FUNC int safe_strerror(int error_code, char*& buffer,
+                           std::size_t buffer_size) FMT_NOEXCEPT {
+  FMT_ASSERT(buffer != nullptr && buffer_size != 0, "invalid buffer");
+
+  class dispatcher {
+   private:
+    int error_code_;
+    char*& buffer_;
+    std::size_t buffer_size_;
+
+    // A noop assignment operator to avoid bogus warnings.
+    void operator=(const dispatcher&) {}
+
+    // Handle the result of XSI-compliant version of strerror_r.
+    int handle(int result) {
+      // glibc versions before 2.13 return result in errno.
+      return result == -1 ? errno : result;
+    }
+
+    // Handle the result of GNU-specific version of strerror_r.
+    FMT_MAYBE_UNUSED
+    int handle(char* message) {
+      // If the buffer is full then the message is probably truncated.
+      if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
+        return ERANGE;
+      buffer_ = message;
+      return 0;
+    }
+
+    // Handle the case when strerror_r is not available.
+    FMT_MAYBE_UNUSED
+    int handle(internal::null<>) {
+      return fallback(strerror_s(buffer_, buffer_size_, error_code_));
+    }
+
+    // Fallback to strerror_s when strerror_r is not available.
+    FMT_MAYBE_UNUSED
+    int fallback(int result) {
+      // If the buffer is full then the message is probably truncated.
+      return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? ERANGE
+                                                                : result;
+    }
+
+#if !FMT_MSC_VER
+    // Fallback to strerror if strerror_r and strerror_s are not available.
+    int fallback(internal::null<>) {
+      errno = 0;
+      buffer_ = strerror(error_code_);
+      return errno;
+    }
+#endif
+
+   public:
+    dispatcher(int err_code, char*& buf, std::size_t buf_size)
+        : error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
+
+    int run() { return handle(strerror_r(error_code_, buffer_, buffer_size_)); }
+  };
+  return dispatcher(error_code, buffer, buffer_size).run();
+}
+
+FMT_FUNC void format_error_code(internal::buffer<char>& out, int error_code,
+                                string_view message) FMT_NOEXCEPT {
+  // Report error code making sure that the output fits into
+  // inline_buffer_size to avoid dynamic memory allocation and potential
+  // bad_alloc.
+  out.resize(0);
+  static const char SEP[] = ": ";
+  static const char ERROR_STR[] = "error ";
+  // Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
+  std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
+  auto abs_value = static_cast<uint32_or_64_or_128_t<int>>(error_code);
+  if (internal::is_negative(error_code)) {
+    abs_value = 0 - abs_value;
+    ++error_code_size;
+  }
+  error_code_size += internal::to_unsigned(internal::count_digits(abs_value));
+  internal::writer w(out);
+  if (message.size() <= inline_buffer_size - error_code_size) {
+    w.write(message);
+    w.write(SEP);
+  }
+  w.write(ERROR_STR);
+  w.write(error_code);
+  assert(out.size() <= inline_buffer_size);
+}
+
+FMT_FUNC void report_error(format_func func, int error_code,
+                           string_view message) FMT_NOEXCEPT {
+  memory_buffer full_message;
+  func(full_message, error_code, message);
+  // Don't use fwrite_fully because the latter may throw.
+  (void)std::fwrite(full_message.data(), full_message.size(), 1, stderr);
+  std::fputc('\n', stderr);
+}
+
+// A wrapper around fwrite that throws on error.
+FMT_FUNC void fwrite_fully(const void* ptr, size_t size, size_t count,
+                           FILE* stream) {
+  size_t written = std::fwrite(ptr, size, count, stream);
+  if (written < count) FMT_THROW(system_error(errno, "cannot write to file"));
+}
+}  // namespace internal
+
+#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
+namespace internal {
+
+template <typename Locale>
+locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
+  static_assert(std::is_same<Locale, std::locale>::value, "");
+}
+
+template <typename Locale> Locale locale_ref::get() const {
+  static_assert(std::is_same<Locale, std::locale>::value, "");
+  return locale_ ? *static_cast<const std::locale*>(locale_) : std::locale();
+}
+
+template <typename Char> FMT_FUNC std::string grouping_impl(locale_ref loc) {
+  return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>()).grouping();
+}
+template <typename Char> FMT_FUNC Char thousands_sep_impl(locale_ref loc) {
+  return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>())
+      .thousands_sep();
+}
+template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref loc) {
+  return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>())
+      .decimal_point();
+}
+}  // namespace internal
+#else
+template <typename Char>
+FMT_FUNC std::string internal::grouping_impl(locale_ref) {
+  return "\03";
+}
+template <typename Char>
+FMT_FUNC Char internal::thousands_sep_impl(locale_ref) {
+  return FMT_STATIC_THOUSANDS_SEPARATOR;
+}
+template <typename Char>
+FMT_FUNC Char internal::decimal_point_impl(locale_ref) {
+  return '.';
+}
+#endif
+
+FMT_API FMT_FUNC format_error::~format_error() FMT_NOEXCEPT = default;
+FMT_API FMT_FUNC system_error::~system_error() FMT_NOEXCEPT = default;
+
+FMT_FUNC void system_error::init(int err_code, string_view format_str,
+                                 format_args args) {
+  error_code_ = err_code;
+  memory_buffer buffer;
+  format_system_error(buffer, err_code, vformat(format_str, args));
+  std::runtime_error& base = *this;
+  base = std::runtime_error(to_string(buffer));
+}
+
+namespace internal {
+
+template <> FMT_FUNC int count_digits<4>(internal::fallback_uintptr n) {
+  // fallback_uintptr is always stored in little endian.
+  int i = static_cast<int>(sizeof(void*)) - 1;
+  while (i > 0 && n.value[i] == 0) --i;
+  auto char_digits = std::numeric_limits<unsigned char>::digits / 4;
+  return i >= 0 ? i * char_digits + count_digits<4, unsigned>(n.value[i]) : 1;
+}
+
+template <typename T>
+const char basic_data<T>::digits[] =
+    "0001020304050607080910111213141516171819"
+    "2021222324252627282930313233343536373839"
+    "4041424344454647484950515253545556575859"
+    "6061626364656667686970717273747576777879"
+    "8081828384858687888990919293949596979899";
+
+template <typename T>
+const char basic_data<T>::hex_digits[] = "0123456789abcdef";
+
+#define FMT_POWERS_OF_10(factor)                                             \
+  factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \
+      (factor)*1000000, (factor)*10000000, (factor)*100000000,               \
+      (factor)*1000000000
+
+template <typename T>
+const uint64_t basic_data<T>::powers_of_10_64[] = {
+    1, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ULL),
+    10000000000000000000ULL};
+
+template <typename T>
+const uint32_t basic_data<T>::zero_or_powers_of_10_32[] = {0,
+                                                           FMT_POWERS_OF_10(1)};
+
+template <typename T>
+const uint64_t basic_data<T>::zero_or_powers_of_10_64[] = {
+    0, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ULL),
+    10000000000000000000ULL};
+
+// Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340.
+// These are generated by support/compute-powers.py.
+template <typename T>
+const uint64_t basic_data<T>::pow10_significands[] = {
+    0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76,
+    0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df,
+    0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c,
+    0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5,
+    0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57,
+    0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7,
+    0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e,
+    0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996,
+    0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126,
+    0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053,
+    0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f,
+    0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b,
+    0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06,
+    0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb,
+    0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000,
+    0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984,
+    0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068,
+    0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8,
+    0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758,
+    0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85,
+    0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d,
+    0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25,
+    0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2,
+    0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a,
+    0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410,
+    0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129,
+    0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85,
+    0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841,
+    0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b,
+};
+
+// Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding
+// to significands above.
+template <typename T>
+const int16_t basic_data<T>::pow10_exponents[] = {
+    -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954,
+    -927,  -901,  -874,  -847,  -821,  -794,  -768,  -741,  -715,  -688, -661,
+    -635,  -608,  -582,  -555,  -529,  -502,  -475,  -449,  -422,  -396, -369,
+    -343,  -316,  -289,  -263,  -236,  -210,  -183,  -157,  -130,  -103, -77,
+    -50,   -24,   3,     30,    56,    83,    109,   136,   162,   189,  216,
+    242,   269,   295,   322,   348,   375,   402,   428,   455,   481,  508,
+    534,   561,   588,   614,   641,   667,   694,   720,   747,   774,  800,
+    827,   853,   880,   907,   933,   960,   986,   1013,  1039,  1066};
+
+template <typename T>
+const char basic_data<T>::foreground_color[] = "\x1b[38;2;";
+template <typename T>
+const char basic_data<T>::background_color[] = "\x1b[48;2;";
+template <typename T> const char basic_data<T>::reset_color[] = "\x1b[0m";
+template <typename T> const wchar_t basic_data<T>::wreset_color[] = L"\x1b[0m";
+template <typename T> const char basic_data<T>::signs[] = {0, '-', '+', ' '};
+
+template <typename T> struct bits {
+  static FMT_CONSTEXPR_DECL const int value =
+      static_cast<int>(sizeof(T) * std::numeric_limits<unsigned char>::digits);
+};
+
+class fp;
+template <int SHIFT = 0> fp normalize(fp value);
+
+// Lower (upper) boundary is a value half way between a floating-point value
+// and its predecessor (successor). Boundaries have the same exponent as the
+// value so only significands are stored.
+struct boundaries {
+  uint64_t lower;
+  uint64_t upper;
+};
+
+// A handmade floating-point number f * pow(2, e).
+class fp {
+ private:
+  using significand_type = uint64_t;
+
+ public:
+  significand_type f;
+  int e;
+
+  // All sizes are in bits.
+  // Subtract 1 to account for an implicit most significant bit in the
+  // normalized form.
+  static FMT_CONSTEXPR_DECL const int double_significand_size =
+      std::numeric_limits<double>::digits - 1;
+  static FMT_CONSTEXPR_DECL const uint64_t implicit_bit =
+      1ULL << double_significand_size;
+  static FMT_CONSTEXPR_DECL const int significand_size =
+      bits<significand_type>::value;
+
+  fp() : f(0), e(0) {}
+  fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {}
+
+  // Constructs fp from an IEEE754 double. It is a template to prevent compile
+  // errors on platforms where double is not IEEE754.
+  template <typename Double> explicit fp(Double d) { assign(d); }
+
+  // Assigns d to this and return true iff predecessor is closer than successor.
+  template <typename Double, FMT_ENABLE_IF(sizeof(Double) == sizeof(uint64_t))>
+  bool assign(Double d) {
+    // Assume double is in the format [sign][exponent][significand].
+    using limits = std::numeric_limits<Double>;
+    const int exponent_size =
+        bits<Double>::value - double_significand_size - 1;  // -1 for sign
+    const uint64_t significand_mask = implicit_bit - 1;
+    const uint64_t exponent_mask = (~0ULL >> 1) & ~significand_mask;
+    const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1;
+    auto u = bit_cast<uint64_t>(d);
+    f = u & significand_mask;
+    int biased_e =
+        static_cast<int>((u & exponent_mask) >> double_significand_size);
+    // Predecessor is closer if d is a normalized power of 2 (f == 0) other than
+    // the smallest normalized number (biased_e > 1).
+    bool is_predecessor_closer = f == 0 && biased_e > 1;
+    if (biased_e != 0)
+      f += implicit_bit;
+    else
+      biased_e = 1;  // Subnormals use biased exponent 1 (min exponent).
+    e = biased_e - exponent_bias - double_significand_size;
+    return is_predecessor_closer;
+  }
+
+  template <typename Double, FMT_ENABLE_IF(sizeof(Double) != sizeof(uint64_t))>
+  bool assign(Double) {
+    *this = fp();
+    return false;
+  }
+
+  // Assigns d to this together with computing lower and upper boundaries,
+  // where a boundary is a value half way between the number and its predecessor
+  // (lower) or successor (upper). The upper boundary is normalized and lower
+  // has the same exponent but may be not normalized.
+  template <typename Double> boundaries assign_with_boundaries(Double d) {
+    bool is_lower_closer = assign(d);
+    fp lower =
+        is_lower_closer ? fp((f << 2) - 1, e - 2) : fp((f << 1) - 1, e - 1);
+    // 1 in normalize accounts for the exponent shift above.
+    fp upper = normalize<1>(fp((f << 1) + 1, e - 1));
+    lower.f <<= lower.e - upper.e;
+    return boundaries{lower.f, upper.f};
+  }
+
+  template <typename Double> boundaries assign_float_with_boundaries(Double d) {
+    assign(d);
+    constexpr int min_normal_e = std::numeric_limits<float>::min_exponent -
+                                 std::numeric_limits<double>::digits;
+    significand_type half_ulp = 1 << (std::numeric_limits<double>::digits -
+                                      std::numeric_limits<float>::digits - 1);
+    if (min_normal_e > e) half_ulp <<= min_normal_e - e;
+    fp upper = normalize<0>(fp(f + half_ulp, e));
+    fp lower = fp(
+        f - (half_ulp >> ((f == implicit_bit && e > min_normal_e) ? 1 : 0)), e);
+    lower.f <<= lower.e - upper.e;
+    return boundaries{lower.f, upper.f};
+  }
+};
+
+// Normalizes the value converted from double and multiplied by (1 << SHIFT).
+template <int SHIFT> fp normalize(fp value) {
+  // Handle subnormals.
+  const auto shifted_implicit_bit = fp::implicit_bit << SHIFT;
+  while ((value.f & shifted_implicit_bit) == 0) {
+    value.f <<= 1;
+    --value.e;
+  }
+  // Subtract 1 to account for hidden bit.
+  const auto offset =
+      fp::significand_size - fp::double_significand_size - SHIFT - 1;
+  value.f <<= offset;
+  value.e -= offset;
+  return value;
+}
+
+inline bool operator==(fp x, fp y) { return x.f == y.f && x.e == y.e; }
+
+// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking.
+inline uint64_t multiply(uint64_t lhs, uint64_t rhs) {
+#if FMT_USE_INT128
+  auto product = static_cast<__uint128_t>(lhs) * rhs;
+  auto f = static_cast<uint64_t>(product >> 64);
+  return (static_cast<uint64_t>(product) & (1ULL << 63)) != 0 ? f + 1 : f;
+#else
+  // Multiply 32-bit parts of significands.
+  uint64_t mask = (1ULL << 32) - 1;
+  uint64_t a = lhs >> 32, b = lhs & mask;
+  uint64_t c = rhs >> 32, d = rhs & mask;
+  uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d;
+  // Compute mid 64-bit of result and round.
+  uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31);
+  return ac + (ad >> 32) + (bc >> 32) + (mid >> 32);
+#endif
+}
+
+inline fp operator*(fp x, fp y) { return {multiply(x.f, y.f), x.e + y.e + 64}; }
+
+// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its
+// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`.
+inline fp get_cached_power(int min_exponent, int& pow10_exponent) {
+  const int64_t one_over_log2_10 = 0x4d104d42;  // round(pow(2, 32) / log2(10))
+  int index = static_cast<int>(
+      ((min_exponent + fp::significand_size - 1) * one_over_log2_10 +
+       ((int64_t(1) << 32) - 1))  // ceil
+      >> 32                       // arithmetic shift
+  );
+  // Decimal exponent of the first (smallest) cached power of 10.
+  const int first_dec_exp = -348;
+  // Difference between 2 consecutive decimal exponents in cached powers of 10.
+  const int dec_exp_step = 8;
+  index = (index - first_dec_exp - 1) / dec_exp_step + 1;
+  pow10_exponent = first_dec_exp + index * dec_exp_step;
+  return {data::pow10_significands[index], data::pow10_exponents[index]};
+}
+
+// A simple accumulator to hold the sums of terms in bigint::square if uint128_t
+// is not available.
+struct accumulator {
+  uint64_t lower;
+  uint64_t upper;
+
+  accumulator() : lower(0), upper(0) {}
+  explicit operator uint32_t() const { return static_cast<uint32_t>(lower); }
+
+  void operator+=(uint64_t n) {
+    lower += n;
+    if (lower < n) ++upper;
+  }
+  void operator>>=(int shift) {
+    assert(shift == 32);
+    (void)shift;
+    lower = (upper << 32) | (lower >> 32);
+    upper >>= 32;
+  }
+};
+
+class bigint {
+ private:
+  // A bigint is stored as an array of bigits (big digits), with bigit at index
+  // 0 being the least significant one.
+  using bigit = uint32_t;
+  using double_bigit = uint64_t;
+  enum { bigits_capacity = 32 };
+  basic_memory_buffer<bigit, bigits_capacity> bigits_;
+  int exp_;
+
+  bigit operator[](int index) const { return bigits_[to_unsigned(index)]; }
+  bigit& operator[](int index) { return bigits_[to_unsigned(index)]; }
+
+  static FMT_CONSTEXPR_DECL const int bigit_bits = bits<bigit>::value;
+
+  friend struct formatter<bigint>;
+
+  void subtract_bigits(int index, bigit other, bigit& borrow) {
+    auto result = static_cast<double_bigit>((*this)[index]) - other - borrow;
+    (*this)[index] = static_cast<bigit>(result);
+    borrow = static_cast<bigit>(result >> (bigit_bits * 2 - 1));
+  }
+
+  void remove_leading_zeros() {
+    int num_bigits = static_cast<int>(bigits_.size()) - 1;
+    while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits;
+    bigits_.resize(to_unsigned(num_bigits + 1));
+  }
+
+  // Computes *this -= other assuming aligned bigints and *this >= other.
+  void subtract_aligned(const bigint& other) {
+    FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints");
+    FMT_ASSERT(compare(*this, other) >= 0, "");
+    bigit borrow = 0;
+    int i = other.exp_ - exp_;
+    for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j) {
+      subtract_bigits(i, other.bigits_[j], borrow);
+    }
+    while (borrow > 0) subtract_bigits(i, 0, borrow);
+    remove_leading_zeros();
+  }
+
+  void multiply(uint32_t value) {
+    const double_bigit wide_value = value;
+    bigit carry = 0;
+    for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
+      double_bigit result = bigits_[i] * wide_value + carry;
+      bigits_[i] = static_cast<bigit>(result);
+      carry = static_cast<bigit>(result >> bigit_bits);
+    }
+    if (carry != 0) bigits_.push_back(carry);
+  }
+
+  void multiply(uint64_t value) {
+    const bigit mask = ~bigit(0);
+    const double_bigit lower = value & mask;
+    const double_bigit upper = value >> bigit_bits;
+    double_bigit carry = 0;
+    for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
+      double_bigit result = bigits_[i] * lower + (carry & mask);
+      carry =
+          bigits_[i] * upper + (result >> bigit_bits) + (carry >> bigit_bits);
+      bigits_[i] = static_cast<bigit>(result);
+    }
+    while (carry != 0) {
+      bigits_.push_back(carry & mask);
+      carry >>= bigit_bits;
+    }
+  }
+
+ public:
+  bigint() : exp_(0) {}
+  explicit bigint(uint64_t n) { assign(n); }
+  ~bigint() { assert(bigits_.capacity() <= bigits_capacity); }
+
+  bigint(const bigint&) = delete;
+  void operator=(const bigint&) = delete;
+
+  void assign(const bigint& other) {
+    bigits_.resize(other.bigits_.size());
+    auto data = other.bigits_.data();
+    std::copy(data, data + other.bigits_.size(), bigits_.data());
+    exp_ = other.exp_;
+  }
+
+  void assign(uint64_t n) {
+    size_t num_bigits = 0;
+    do {
+      bigits_[num_bigits++] = n & ~bigit(0);
+      n >>= bigit_bits;
+    } while (n != 0);
+    bigits_.resize(num_bigits);
+    exp_ = 0;
+  }
+
+  int num_bigits() const { return static_cast<int>(bigits_.size()) + exp_; }
+
+  bigint& operator<<=(int shift) {
+    assert(shift >= 0);
+    exp_ += shift / bigit_bits;
+    shift %= bigit_bits;
+    if (shift == 0) return *this;
+    bigit carry = 0;
+    for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
+      bigit c = bigits_[i] >> (bigit_bits - shift);
+      bigits_[i] = (bigits_[i] << shift) + carry;
+      carry = c;
+    }
+    if (carry != 0) bigits_.push_back(carry);
+    return *this;
+  }
+
+  template <typename Int> bigint& operator*=(Int value) {
+    FMT_ASSERT(value > 0, "");
+    multiply(uint32_or_64_or_128_t<Int>(value));
+    return *this;
+  }
+
+  friend int compare(const bigint& lhs, const bigint& rhs) {
+    int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits();
+    if (num_lhs_bigits != num_rhs_bigits)
+      return num_lhs_bigits > num_rhs_bigits ? 1 : -1;
+    int i = static_cast<int>(lhs.bigits_.size()) - 1;
+    int j = static_cast<int>(rhs.bigits_.size()) - 1;
+    int end = i - j;
+    if (end < 0) end = 0;
+    for (; i >= end; --i, --j) {
+      bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j];
+      if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1;
+    }
+    if (i != j) return i > j ? 1 : -1;
+    return 0;
+  }
+
+  // Returns compare(lhs1 + lhs2, rhs).
+  friend int add_compare(const bigint& lhs1, const bigint& lhs2,
+                         const bigint& rhs) {
+    int max_lhs_bigits = (std::max)(lhs1.num_bigits(), lhs2.num_bigits());
+    int num_rhs_bigits = rhs.num_bigits();
+    if (max_lhs_bigits + 1 < num_rhs_bigits) return -1;
+    if (max_lhs_bigits > num_rhs_bigits) return 1;
+    auto get_bigit = [](const bigint& n, int i) -> bigit {
+      return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0;
+    };
+    double_bigit borrow = 0;
+    int min_exp = (std::min)((std::min)(lhs1.exp_, lhs2.exp_), rhs.exp_);
+    for (int i = num_rhs_bigits - 1; i >= min_exp; --i) {
+      double_bigit sum =
+          static_cast<double_bigit>(get_bigit(lhs1, i)) + get_bigit(lhs2, i);
+      bigit rhs_bigit = get_bigit(rhs, i);
+      if (sum > rhs_bigit + borrow) return 1;
+      borrow = rhs_bigit + borrow - sum;
+      if (borrow > 1) return -1;
+      borrow <<= bigit_bits;
+    }
+    return borrow != 0 ? -1 : 0;
+  }
+
+  // Assigns pow(10, exp) to this bigint.
+  void assign_pow10(int exp) {
+    assert(exp >= 0);
+    if (exp == 0) return assign(1);
+    // Find the top bit.
+    int bitmask = 1;
+    while (exp >= bitmask) bitmask <<= 1;
+    bitmask >>= 1;
+    // pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by
+    // repeated squaring and multiplication.
+    assign(5);
+    bitmask >>= 1;
+    while (bitmask != 0) {
+      square();
+      if ((exp & bitmask) != 0) *this *= 5;
+      bitmask >>= 1;
+    }
+    *this <<= exp;  // Multiply by pow(2, exp) by shifting.
+  }
+
+  void square() {
+    basic_memory_buffer<bigit, bigits_capacity> n(std::move(bigits_));
+    int num_bigits = static_cast<int>(bigits_.size());
+    int num_result_bigits = 2 * num_bigits;
+    bigits_.resize(to_unsigned(num_result_bigits));
+    using accumulator_t = conditional_t<FMT_USE_INT128, uint128_t, accumulator>;
+    auto sum = accumulator_t();
+    for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) {
+      // Compute bigit at position bigit_index of the result by adding
+      // cross-product terms n[i] * n[j] such that i + j == bigit_index.
+      for (int i = 0, j = bigit_index; j >= 0; ++i, --j) {
+        // Most terms are multiplied twice which can be optimized in the future.
+        sum += static_cast<double_bigit>(n[i]) * n[j];
+      }
+      (*this)[bigit_index] = static_cast<bigit>(sum);
+      sum >>= bits<bigit>::value;  // Compute the carry.
+    }
+    // Do the same for the top half.
+    for (int bigit_index = num_bigits; bigit_index < num_result_bigits;
+         ++bigit_index) {
+      for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;)
+        sum += static_cast<double_bigit>(n[i++]) * n[j--];
+      (*this)[bigit_index] = static_cast<bigit>(sum);
+      sum >>= bits<bigit>::value;
+    }
+    --num_result_bigits;
+    remove_leading_zeros();
+    exp_ *= 2;
+  }
+
+  // Divides this bignum by divisor, assigning the remainder to this and
+  // returning the quotient.
+  int divmod_assign(const bigint& divisor) {
+    FMT_ASSERT(this != &divisor, "");
+    if (compare(*this, divisor) < 0) return 0;
+    int num_bigits = static_cast<int>(bigits_.size());
+    FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, "");
+    int exp_difference = exp_ - divisor.exp_;
+    if (exp_difference > 0) {
+      // Align bigints by adding trailing zeros to simplify subtraction.
+      bigits_.resize(to_unsigned(num_bigits + exp_difference));
+      for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j)
+        bigits_[j] = bigits_[i];
+      std::uninitialized_fill_n(bigits_.data(), exp_difference, 0);
+      exp_ -= exp_difference;
+    }
+    int quotient = 0;
+    do {
+      subtract_aligned(divisor);
+      ++quotient;
+    } while (compare(*this, divisor) >= 0);
+    return quotient;
+  }
+};
+
+enum class round_direction { unknown, up, down };
+
+// Given the divisor (normally a power of 10), the remainder = v % divisor for
+// some number v and the error, returns whether v should be rounded up, down, or
+// whether the rounding direction can't be determined due to error.
+// error should be less than divisor / 2.
+inline round_direction get_round_direction(uint64_t divisor, uint64_t remainder,
+                                           uint64_t error) {
+  FMT_ASSERT(remainder < divisor, "");  // divisor - remainder won't overflow.
+  FMT_ASSERT(error < divisor, "");      // divisor - error won't overflow.
+  FMT_ASSERT(error < divisor - error, "");  // error * 2 won't overflow.
+  // Round down if (remainder + error) * 2 <= divisor.
+  if (remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2)
+    return round_direction::down;
+  // Round up if (remainder - error) * 2 >= divisor.
+  if (remainder >= error &&
+      remainder - error >= divisor - (remainder - error)) {
+    return round_direction::up;
+  }
+  return round_direction::unknown;
+}
+
+namespace digits {
+enum result {
+  more,  // Generate more digits.
+  done,  // Done generating digits.
+  error  // Digit generation cancelled due to an error.
+};
+}
+
+// A version of count_digits optimized for grisu_gen_digits.
+inline int grisu_count_digits(uint32_t n) {
+  if (n < 10) return 1;
+  if (n < 100) return 2;
+  if (n < 1000) return 3;
+  if (n < 10000) return 4;
+  if (n < 100000) return 5;
+  if (n < 1000000) return 6;
+  if (n < 10000000) return 7;
+  if (n < 100000000) return 8;
+  if (n < 1000000000) return 9;
+  return 10;
+}
+
+// Generates output using the Grisu digit-gen algorithm.
+// error: the size of the region (lower, upper) outside of which numbers
+// definitely do not round to value (Delta in Grisu3).
+template <typename Handler>
+FMT_ALWAYS_INLINE digits::result grisu_gen_digits(fp value, uint64_t error,
+                                                  int& exp, Handler& handler) {
+  const fp one(1ULL << -value.e, value.e);
+  // The integral part of scaled value (p1 in Grisu) = value / one. It cannot be
+  // zero because it contains a product of two 64-bit numbers with MSB set (due
+  // to normalization) - 1, shifted right by at most 60 bits.
+  auto integral = static_cast<uint32_t>(value.f >> -one.e);
+  FMT_ASSERT(integral != 0, "");
+  FMT_ASSERT(integral == value.f >> -one.e, "");
+  // The fractional part of scaled value (p2 in Grisu) c = value % one.
+  uint64_t fractional = value.f & (one.f - 1);
+  exp = grisu_count_digits(integral);  // kappa in Grisu.
+  // Divide by 10 to prevent overflow.
+  auto result = handler.on_start(data::powers_of_10_64[exp - 1] << -one.e,
+                                 value.f / 10, error * 10, exp);
+  if (result != digits::more) return result;
+  // Generate digits for the integral part. This can produce up to 10 digits.
+  do {
+    uint32_t digit = 0;
+    auto divmod_integral = [&](uint32_t divisor) {
+      digit = integral / divisor;
+      integral %= divisor;
+    };
+    // This optimization by Milo Yip reduces the number of integer divisions by
+    // one per iteration.
+    switch (exp) {
+    case 10:
+      divmod_integral(1000000000);
+      break;
+    case 9:
+      divmod_integral(100000000);
+      break;
+    case 8:
+      divmod_integral(10000000);
+      break;
+    case 7:
+      divmod_integral(1000000);
+      break;
+    case 6:
+      divmod_integral(100000);
+      break;
+    case 5:
+      divmod_integral(10000);
+      break;
+    case 4:
+      divmod_integral(1000);
+      break;
+    case 3:
+      divmod_integral(100);
+      break;
+    case 2:
+      divmod_integral(10);
+      break;
+    case 1:
+      digit = integral;
+      integral = 0;
+      break;
+    default:
+      FMT_ASSERT(false, "invalid number of digits");
+    }
+    --exp;
+    uint64_t remainder =
+        (static_cast<uint64_t>(integral) << -one.e) + fractional;
+    result = handler.on_digit(static_cast<char>('0' + digit),
+                              data::powers_of_10_64[exp] << -one.e, remainder,
+                              error, exp, true);
+    if (result != digits::more) return result;
+  } while (exp > 0);
+  // Generate digits for the fractional part.
+  for (;;) {
+    fractional *= 10;
+    error *= 10;
+    char digit =
+        static_cast<char>('0' + static_cast<char>(fractional >> -one.e));
+    fractional &= one.f - 1;
+    --exp;
+    result = handler.on_digit(digit, one.f, fractional, error, exp, false);
+    if (result != digits::more) return result;
+  }
+}
+
+// The fixed precision digit handler.
+struct fixed_handler {
+  char* buf;
+  int size;
+  int precision;
+  int exp10;
+  bool fixed;
+
+  digits::result on_start(uint64_t divisor, uint64_t remainder, uint64_t error,
+                          int& exp) {
+    // Non-fixed formats require at least one digit and no precision adjustment.
+    if (!fixed) return digits::more;
+    // Adjust fixed precision by exponent because it is relative to decimal
+    // point.
+    precision += exp + exp10;
+    // Check if precision is satisfied just by leading zeros, e.g.
+    // format("{:.2f}", 0.001) gives "0.00" without generating any digits.
+    if (precision > 0) return digits::more;
+    if (precision < 0) return digits::done;
+    auto dir = get_round_direction(divisor, remainder, error);
+    if (dir == round_direction::unknown) return digits::error;
+    buf[size++] = dir == round_direction::up ? '1' : '0';
+    return digits::done;
+  }
+
+  digits::result on_digit(char digit, uint64_t divisor, uint64_t remainder,
+                          uint64_t error, int, bool integral) {
+    FMT_ASSERT(remainder < divisor, "");
+    buf[size++] = digit;
+    if (size < precision) return digits::more;
+    if (!integral) {
+      // Check if error * 2 < divisor with overflow prevention.
+      // The check is not needed for the integral part because error = 1
+      // and divisor > (1 << 32) there.
+      if (error >= divisor || error >= divisor - error) return digits::error;
+    } else {
+      FMT_ASSERT(error == 1 && divisor > 2, "");
+    }
+    auto dir = get_round_direction(divisor, remainder, error);
+    if (dir != round_direction::up)
+      return dir == round_direction::down ? digits::done : digits::error;
+    ++buf[size - 1];
+    for (int i = size - 1; i > 0 && buf[i] > '9'; --i) {
+      buf[i] = '0';
+      ++buf[i - 1];
+    }
+    if (buf[0] > '9') {
+      buf[0] = '1';
+      buf[size++] = '0';
+    }
+    return digits::done;
+  }
+};
+
+// The shortest representation digit handler.
+struct grisu_shortest_handler {
+  char* buf;
+  int size;
+  // Distance between scaled value and upper bound (wp_W in Grisu3).
+  uint64_t diff;
+
+  digits::result on_start(uint64_t, uint64_t, uint64_t, int&) {
+    return digits::more;
+  }
+
+  // Decrement the generated number approaching value from above.
+  void round(uint64_t d, uint64_t divisor, uint64_t& remainder,
+             uint64_t error) {
+    while (
+        remainder < d && error - remainder >= divisor &&
+        (remainder + divisor < d || d - remainder >= remainder + divisor - d)) {
+      --buf[size - 1];
+      remainder += divisor;
+    }
+  }
+
+  // Implements Grisu's round_weed.
+  digits::result on_digit(char digit, uint64_t divisor, uint64_t remainder,
+                          uint64_t error, int exp, bool integral) {
+    buf[size++] = digit;
+    if (remainder >= error) return digits::more;
+    uint64_t unit = integral ? 1 : data::powers_of_10_64[-exp];
+    uint64_t up = (diff - 1) * unit;  // wp_Wup
+    round(up, divisor, remainder, error);
+    uint64_t down = (diff + 1) * unit;  // wp_Wdown
+    if (remainder < down && error - remainder >= divisor &&
+        (remainder + divisor < down ||
+         down - remainder > remainder + divisor - down)) {
+      return digits::error;
+    }
+    return 2 * unit <= remainder && remainder <= error - 4 * unit
+               ? digits::done
+               : digits::error;
+  }
+};
+
+// Formats value using a variation of the Fixed-Precision Positive
+// Floating-Point Printout ((FPP)^2) algorithm by Steele & White:
+// https://fmt.dev/p372-steele.pdf.
+template <typename Double>
+void fallback_format(Double d, buffer<char>& buf, int& exp10) {
+  bigint numerator;    // 2 * R in (FPP)^2.
+  bigint denominator;  // 2 * S in (FPP)^2.
+  // lower and upper are differences between value and corresponding boundaries.
+  bigint lower;             // (M^- in (FPP)^2).
+  bigint upper_store;       // upper's value if different from lower.
+  bigint* upper = nullptr;  // (M^+ in (FPP)^2).
+  fp value;
+  // Shift numerator and denominator by an extra bit or two (if lower boundary
+  // is closer) to make lower and upper integers. This eliminates multiplication
+  // by 2 during later computations.
+  // TODO: handle float
+  int shift = value.assign(d) ? 2 : 1;
+  uint64_t significand = value.f << shift;
+  if (value.e >= 0) {
+    numerator.assign(significand);
+    numerator <<= value.e;
+    lower.assign(1);
+    lower <<= value.e;
+    if (shift != 1) {
+      upper_store.assign(1);
+      upper_store <<= value.e + 1;
+      upper = &upper_store;
+    }
+    denominator.assign_pow10(exp10);
+    denominator <<= 1;
+  } else if (exp10 < 0) {
+    numerator.assign_pow10(-exp10);
+    lower.assign(numerator);
+    if (shift != 1) {
+      upper_store.assign(numerator);
+      upper_store <<= 1;
+      upper = &upper_store;
+    }
+    numerator *= significand;
+    denominator.assign(1);
+    denominator <<= shift - value.e;
+  } else {
+    numerator.assign(significand);
+    denominator.assign_pow10(exp10);
+    denominator <<= shift - value.e;
+    lower.assign(1);
+    if (shift != 1) {
+      upper_store.assign(1ULL << 1);
+      upper = &upper_store;
+    }
+  }
+  if (!upper) upper = &lower;
+  // Invariant: value == (numerator / denominator) * pow(10, exp10).
+  bool even = (value.f & 1) == 0;
+  int num_digits = 0;
+  char* data = buf.data();
+  for (;;) {
+    int digit = numerator.divmod_assign(denominator);
+    bool low = compare(numerator, lower) - even < 0;  // numerator <[=] lower.
+    // numerator + upper >[=] pow10:
+    bool high = add_compare(numerator, *upper, denominator) + even > 0;
+    data[num_digits++] = static_cast<char>('0' + digit);
+    if (low || high) {
+      if (!low) {
+        ++data[num_digits - 1];
+      } else if (high) {
+        int result = add_compare(numerator, numerator, denominator);
+        // Round half to even.
+        if (result > 0 || (result == 0 && (digit % 2) != 0))
+          ++data[num_digits - 1];
+      }
+      buf.resize(to_unsigned(num_digits));
+      exp10 -= num_digits - 1;
+      return;
+    }
+    numerator *= 10;
+    lower *= 10;
+    if (upper != &lower) *upper *= 10;
+  }
+}
+
+// Formats value using the Grisu algorithm
+// (https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf)
+// if T is a IEEE754 binary32 or binary64 and snprintf otherwise.
+template <typename T>
+int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
+  static_assert(!std::is_same<T, float>::value, "");
+  FMT_ASSERT(value >= 0, "value is negative");
+
+  const bool fixed = specs.format == float_format::fixed;
+  if (value <= 0) {  // <= instead of == to silence a warning.
+    if (precision <= 0 || !fixed) {
+      buf.push_back('0');
+      return 0;
+    }
+    buf.resize(to_unsigned(precision));
+    std::uninitialized_fill_n(buf.data(), precision, '0');
+    return -precision;
+  }
+
+  if (!specs.use_grisu) return snprintf_float(value, precision, specs, buf);
+
+  int exp = 0;
+  const int min_exp = -60;  // alpha in Grisu.
+  int cached_exp10 = 0;     // K in Grisu.
+  if (precision < 0) {
+    fp fp_value;
+    auto boundaries = specs.binary32
+                          ? fp_value.assign_float_with_boundaries(value)
+                          : fp_value.assign_with_boundaries(value);
+    fp_value = normalize(fp_value);
+    // Find a cached power of 10 such that multiplying value by it will bring
+    // the exponent in the range [min_exp, -32].
+    const fp cached_pow = get_cached_power(
+        min_exp - (fp_value.e + fp::significand_size), cached_exp10);
+    // Multiply value and boundaries by the cached power of 10.
+    fp_value = fp_value * cached_pow;
+    boundaries.lower = multiply(boundaries.lower, cached_pow.f);
+    boundaries.upper = multiply(boundaries.upper, cached_pow.f);
+    assert(min_exp <= fp_value.e && fp_value.e <= -32);
+    --boundaries.lower;  // \tilde{M}^- - 1 ulp -> M^-_{\downarrow}.
+    ++boundaries.upper;  // \tilde{M}^+ + 1 ulp -> M^+_{\uparrow}.
+    // Numbers outside of (lower, upper) definitely do not round to value.
+    grisu_shortest_handler handler{buf.data(), 0,
+                                   boundaries.upper - fp_value.f};
+    auto result =
+        grisu_gen_digits(fp(boundaries.upper, fp_value.e),
+                         boundaries.upper - boundaries.lower, exp, handler);
+    if (result == digits::error) {
+      exp += handler.size - cached_exp10 - 1;
+      fallback_format(value, buf, exp);
+      return exp;
+    }
+    buf.resize(to_unsigned(handler.size));
+  } else {
+    if (precision > 17) return snprintf_float(value, precision, specs, buf);
+    fp normalized = normalize(fp(value));
+    const auto cached_pow = get_cached_power(
+        min_exp - (normalized.e + fp::significand_size), cached_exp10);
+    normalized = normalized * cached_pow;
+    fixed_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
+    if (grisu_gen_digits(normalized, 1, exp, handler) == digits::error)
+      return snprintf_float(value, precision, specs, buf);
+    int num_digits = handler.size;
+    if (!fixed) {
+      // Remove trailing zeros.
+      while (num_digits > 0 && buf[num_digits - 1] == '0') {
+        --num_digits;
+        ++exp;
+      }
+    }
+    buf.resize(to_unsigned(num_digits));
+  }
+  return exp - cached_exp10;
+}
+
+template <typename T>
+int snprintf_float(T value, int precision, float_specs specs,
+                   buffer<char>& buf) {
+  // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
+  FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer");
+  static_assert(!std::is_same<T, float>::value, "");
+
+  // Subtract 1 to account for the difference in precision since we use %e for
+  // both general and exponent format.
+  if (specs.format == float_format::general ||
+      specs.format == float_format::exp)
+    precision = (precision >= 0 ? precision : 6) - 1;
+
+  // Build the format string.
+  enum { max_format_size = 7 };  // Ths longest format is "%#.*Le".
+  char format[max_format_size];
+  char* format_ptr = format;
+  *format_ptr++ = '%';
+  if (specs.showpoint && specs.format == float_format::hex) *format_ptr++ = '#';
+  if (precision >= 0) {
+    *format_ptr++ = '.';
+    *format_ptr++ = '*';
+  }
+  if (std::is_same<T, long double>()) *format_ptr++ = 'L';
+  *format_ptr++ = specs.format != float_format::hex
+                      ? (specs.format == float_format::fixed ? 'f' : 'e')
+                      : (specs.upper ? 'A' : 'a');
+  *format_ptr = '\0';
+
+  // Format using snprintf.
+  auto offset = buf.size();
+  for (;;) {
+    auto begin = buf.data() + offset;
+    auto capacity = buf.capacity() - offset;
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+    if (precision > 100000)
+      throw std::runtime_error(
+          "fuzz mode - avoid large allocation inside snprintf");
+#endif
+    // Suppress the warning about a nonliteral format string.
+    // Cannot use auto becase of a bug in MinGW (#1532).
+    int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
+    int result = precision >= 0
+                     ? snprintf_ptr(begin, capacity, format, precision, value)
+                     : snprintf_ptr(begin, capacity, format, value);
+    if (result < 0) {
+      buf.reserve(buf.capacity() + 1);  // The buffer will grow exponentially.
+      continue;
+    }
+    auto size = to_unsigned(result);
+    // Size equal to capacity means that the last character was truncated.
+    if (size >= capacity) {
+      buf.reserve(size + offset + 1);  // Add 1 for the terminating '\0'.
+      continue;
+    }
+    auto is_digit = [](char c) { return c >= '0' && c <= '9'; };
+    if (specs.format == float_format::fixed) {
+      if (precision == 0) {
+        buf.resize(size);
+        return 0;
+      }
+      // Find and remove the decimal point.
+      auto end = begin + size, p = end;
+      do {
+        --p;
+      } while (is_digit(*p));
+      int fraction_size = static_cast<int>(end - p - 1);
+      std::memmove(p, p + 1, to_unsigned(fraction_size));
+      buf.resize(size - 1);
+      return -fraction_size;
+    }
+    if (specs.format == float_format::hex) {
+      buf.resize(size + offset);
+      return 0;
+    }
+    // Find and parse the exponent.
+    auto end = begin + size, exp_pos = end;
+    do {
+      --exp_pos;
+    } while (*exp_pos != 'e');
+    char sign = exp_pos[1];
+    assert(sign == '+' || sign == '-');
+    int exp = 0;
+    auto p = exp_pos + 2;  // Skip 'e' and sign.
+    do {
+      assert(is_digit(*p));
+      exp = exp * 10 + (*p++ - '0');
+    } while (p != end);
+    if (sign == '-') exp = -exp;
+    int fraction_size = 0;
+    if (exp_pos != begin + 1) {
+      // Remove trailing zeros.
+      auto fraction_end = exp_pos - 1;
+      while (*fraction_end == '0') --fraction_end;
+      // Move the fractional part left to get rid of the decimal point.
+      fraction_size = static_cast<int>(fraction_end - begin - 1);
+      std::memmove(begin + 1, begin + 2, to_unsigned(fraction_size));
+    }
+    buf.resize(to_unsigned(fraction_size) + offset + 1);
+    return exp - fraction_size;
+  }
+}
+
+// A public domain branchless UTF-8 decoder by Christopher Wellons:
+// https://github.com/skeeto/branchless-utf8
+/* Decode the next character, c, from buf, reporting errors in e.
+ *
+ * Since this is a branchless decoder, four bytes will be read from the
+ * buffer regardless of the actual length of the next character. This
+ * means the buffer _must_ have at least three bytes of zero padding
+ * following the end of the data stream.
+ *
+ * Errors are reported in e, which will be non-zero if the parsed
+ * character was somehow invalid: invalid byte sequence, non-canonical
+ * encoding, or a surrogate half.
+ *
+ * The function returns a pointer to the next character. When an error
+ * occurs, this pointer will be a guess that depends on the particular
+ * error, but it will always advance at least one byte.
+ */
+FMT_FUNC const char* utf8_decode(const char* buf, uint32_t* c, int* e) {
+  static const char lengths[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                                 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
+                                 0, 0, 2, 2, 2, 2, 3, 3, 4, 0};
+  static const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07};
+  static const uint32_t mins[] = {4194304, 0, 128, 2048, 65536};
+  static const int shiftc[] = {0, 18, 12, 6, 0};
+  static const int shifte[] = {0, 6, 4, 2, 0};
+
+  auto s = reinterpret_cast<const unsigned char*>(buf);
+  int len = lengths[s[0] >> 3];
+
+  // Compute the pointer to the next character early so that the next
+  // iteration can start working on the next character. Neither Clang
+  // nor GCC figure out this reordering on their own.
+  const char* next = buf + len + !len;
+
+  // Assume a four-byte character and load four bytes. Unused bits are
+  // shifted out.
+  *c = uint32_t(s[0] & masks[len]) << 18;
+  *c |= uint32_t(s[1] & 0x3f) << 12;
+  *c |= uint32_t(s[2] & 0x3f) << 6;
+  *c |= uint32_t(s[3] & 0x3f) << 0;
+  *c >>= shiftc[len];
+
+  // Accumulate the various error conditions.
+  *e = (*c < mins[len]) << 6;       // non-canonical encoding
+  *e |= ((*c >> 11) == 0x1b) << 7;  // surrogate half?
+  *e |= (*c > 0x10FFFF) << 8;       // out of range?
+  *e |= (s[1] & 0xc0) >> 2;
+  *e |= (s[2] & 0xc0) >> 4;
+  *e |= (s[3]) >> 6;
+  *e ^= 0x2a;  // top two bits of each tail byte correct?
+  *e >>= shifte[len];
+
+  return next;
+}
+}  // namespace internal
+
+template <> struct formatter<internal::bigint> {
+  format_parse_context::iterator parse(format_parse_context& ctx) {
+    return ctx.begin();
+  }
+
+  format_context::iterator format(const internal::bigint& n,
+                                  format_context& ctx) {
+    auto out = ctx.out();
+    bool first = true;
+    for (auto i = n.bigits_.size(); i > 0; --i) {
+      auto value = n.bigits_[i - 1u];
+      if (first) {
+        out = format_to(out, "{:x}", value);
+        first = false;
+        continue;
+      }
+      out = format_to(out, "{:08x}", value);
+    }
+    if (n.exp_ > 0)
+      out = format_to(out, "p{}", n.exp_ * internal::bigint::bigit_bits);
+    return out;
+  }
+};
+
+FMT_FUNC internal::utf8_to_utf16::utf8_to_utf16(string_view s) {
+  auto transcode = [this](const char* p) {
+    auto cp = uint32_t();
+    auto error = 0;
+    p = utf8_decode(p, &cp, &error);
+    if (error != 0) FMT_THROW(std::runtime_error("invalid utf8"));
+    if (cp <= 0xFFFF) {
+      buffer_.push_back(static_cast<wchar_t>(cp));
+    } else {
+      cp -= 0x10000;
+      buffer_.push_back(static_cast<wchar_t>(0xD800 + (cp >> 10)));
+      buffer_.push_back(static_cast<wchar_t>(0xDC00 + (cp & 0x3FF)));
+    }
+    return p;
+  };
+  auto p = s.data();
+  const size_t block_size = 4;  // utf8_decode always reads blocks of 4 chars.
+  if (s.size() >= block_size) {
+    for (auto end = p + s.size() - block_size + 1; p < end;) p = transcode(p);
+  }
+  if (auto num_chars_left = s.data() + s.size() - p) {
+    char buf[2 * block_size - 1] = {};
+    memcpy(buf, p, to_unsigned(num_chars_left));
+    p = buf;
+    do {
+      p = transcode(p);
+    } while (p - buf < num_chars_left);
+  }
+  buffer_.push_back(0);
+}
+
+FMT_FUNC void format_system_error(internal::buffer<char>& out, int error_code,
+                                  string_view message) FMT_NOEXCEPT {
+  FMT_TRY {
+    memory_buffer buf;
+    buf.resize(inline_buffer_size);
+    for (;;) {
+      char* system_message = &buf[0];
+      int result =
+          internal::safe_strerror(error_code, system_message, buf.size());
+      if (result == 0) {
+        internal::writer w(out);
+        w.write(message);
+        w.write(": ");
+        w.write(system_message);
+        return;
+      }
+      if (result != ERANGE)
+        break;  // Can't get error message, report error code instead.
+      buf.resize(buf.size() * 2);
+    }
+  }
+  FMT_CATCH(...) {}
+  format_error_code(out, error_code, message);
+}
+
+FMT_FUNC void internal::error_handler::on_error(const char* message) {
+  FMT_THROW(format_error(message));
+}
+
+FMT_FUNC void report_system_error(int error_code,
+                                  fmt::string_view message) FMT_NOEXCEPT {
+  report_error(format_system_error, error_code, message);
+}
+
+FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) {
+  memory_buffer buffer;
+  internal::vformat_to(buffer, format_str,
+                       basic_format_args<buffer_context<char>>(args));
+#ifdef _WIN32
+  auto fd = _fileno(f);
+  if (_isatty(fd)) {
+    internal::utf8_to_utf16 u16(string_view(buffer.data(), buffer.size()));
+    auto written = DWORD();
+    if (!WriteConsoleW(reinterpret_cast<HANDLE>(_get_osfhandle(fd)),
+                       u16.c_str(), static_cast<DWORD>(u16.size()), &written,
+                       nullptr)) {
+      FMT_THROW(format_error("failed to write to console"));
+    }
+    return;
+  }
+#endif
+  internal::fwrite_fully(buffer.data(), 1, buffer.size(), f);
+}
+
+#ifdef _WIN32
+// Print assuming legacy (non-Unicode) encoding.
+FMT_FUNC void internal::vprint_mojibake(std::FILE* f, string_view format_str,
+                                        format_args args) {
+  memory_buffer buffer;
+  internal::vformat_to(buffer, format_str,
+                       basic_format_args<buffer_context<char>>(args));
+  fwrite_fully(buffer.data(), 1, buffer.size(), f);
+}
+#endif
+
+FMT_FUNC void vprint(string_view format_str, format_args args) {
+  vprint(stdout, format_str, args);
+}
+
+FMT_END_NAMESPACE
+
+#ifdef _MSC_VER
+#  pragma warning(pop)
+#endif
+
+#endif  // FMT_FORMAT_INL_H_
diff --git a/GeoModelDBManager/fmt/format.h b/GeoModelDBManager/fmt/format.h
new file mode 100644
index 0000000000000000000000000000000000000000..4e96539fa2a078c093d77e9d30a83752339fda24
--- /dev/null
+++ b/GeoModelDBManager/fmt/format.h
@@ -0,0 +1,3648 @@
+/*
+ Formatting library for C++
+
+ Copyright (c) 2012 - present, Victor Zverovich
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+ --- Optional exception to the license ---
+
+ As an exception, if, as a result of your compiling your source code, portions
+ of this Software are embedded into a machine-executable object form of such
+ source code, you may redistribute such embedded portions in such object form
+ without including the above copyright and permission notices.
+ */
+
+#ifndef FMT_FORMAT_H_
+#define FMT_FORMAT_H_
+
+#include <algorithm>
+#include <cerrno>
+#include <cmath>
+#include <cstdint>
+#include <limits>
+#include <memory>
+#include <stdexcept>
+
+#include "core.h"
+
+#ifdef FMT_DEPRECATED_INCLUDE_OS
+#  include "os.h"
+#endif
+
+#ifdef __INTEL_COMPILER
+#  define FMT_ICC_VERSION __INTEL_COMPILER
+#elif defined(__ICL)
+#  define FMT_ICC_VERSION __ICL
+#else
+#  define FMT_ICC_VERSION 0
+#endif
+
+#ifdef __NVCC__
+#  define FMT_CUDA_VERSION (__CUDACC_VER_MAJOR__ * 100 + __CUDACC_VER_MINOR__)
+#else
+#  define FMT_CUDA_VERSION 0
+#endif
+
+#ifdef __has_builtin
+#  define FMT_HAS_BUILTIN(x) __has_builtin(x)
+#else
+#  define FMT_HAS_BUILTIN(x) 0
+#endif
+
+#if FMT_GCC_VERSION || FMT_CLANG_VERSION
+#  define FMT_NOINLINE __attribute__((noinline))
+#else
+#  define FMT_NOINLINE
+#endif
+
+#if __cplusplus == 201103L || __cplusplus == 201402L
+#  if defined(__clang__)
+#    define FMT_FALLTHROUGH [[clang::fallthrough]]
+#  elif FMT_GCC_VERSION >= 700 && !defined(__PGI)
+#    define FMT_FALLTHROUGH [[gnu::fallthrough]]
+#  else
+#    define FMT_FALLTHROUGH
+#  endif
+#elif FMT_HAS_CPP17_ATTRIBUTE(fallthrough) || \
+    (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
+#  define FMT_FALLTHROUGH [[fallthrough]]
+#else
+#  define FMT_FALLTHROUGH
+#endif
+
+#ifndef FMT_THROW
+#  if FMT_EXCEPTIONS
+#    if FMT_MSC_VER || FMT_NVCC
+FMT_BEGIN_NAMESPACE
+namespace internal {
+template <typename Exception> inline void do_throw(const Exception& x) {
+  // Silence unreachable code warnings in MSVC and NVCC because these
+  // are nearly impossible to fix in a generic code.
+  volatile bool b = true;
+  if (b) throw x;
+}
+}  // namespace internal
+FMT_END_NAMESPACE
+#      define FMT_THROW(x) internal::do_throw(x)
+#    else
+#      define FMT_THROW(x) throw x
+#    endif
+#  else
+#    define FMT_THROW(x)              \
+      do {                            \
+        static_cast<void>(sizeof(x)); \
+        FMT_ASSERT(false, "");        \
+      } while (false)
+#  endif
+#endif
+
+#if FMT_EXCEPTIONS
+#  define FMT_TRY try
+#  define FMT_CATCH(x) catch (x)
+#else
+#  define FMT_TRY if (true)
+#  define FMT_CATCH(x) if (false)
+#endif
+
+#ifndef FMT_USE_USER_DEFINED_LITERALS
+// For Intel and NVIDIA compilers both they and the system gcc/msc support UDLs.
+#  if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 ||      \
+       FMT_MSC_VER >= 1900) &&                                              \
+      (!(FMT_ICC_VERSION || FMT_CUDA_VERSION) || FMT_ICC_VERSION >= 1500 || \
+       FMT_CUDA_VERSION >= 700)
+#    define FMT_USE_USER_DEFINED_LITERALS 1
+#  else
+#    define FMT_USE_USER_DEFINED_LITERALS 0
+#  endif
+#endif
+
+#ifndef FMT_USE_UDL_TEMPLATE
+// EDG front end based compilers (icc, nvcc) and GCC < 6.4 do not propertly
+// support UDL templates and GCC >= 9 warns about them.
+#  if FMT_USE_USER_DEFINED_LITERALS && FMT_ICC_VERSION == 0 && \
+      FMT_CUDA_VERSION == 0 &&                                 \
+      ((FMT_GCC_VERSION >= 604 && FMT_GCC_VERSION <= 900 &&    \
+        __cplusplus >= 201402L) ||                             \
+       FMT_CLANG_VERSION >= 304)
+#    define FMT_USE_UDL_TEMPLATE 1
+#  else
+#    define FMT_USE_UDL_TEMPLATE 0
+#  endif
+#endif
+
+#ifndef FMT_USE_FLOAT
+#  define FMT_USE_FLOAT 1
+#endif
+
+#ifndef FMT_USE_DOUBLE
+#  define FMT_USE_DOUBLE 1
+#endif
+
+#ifndef FMT_USE_LONG_DOUBLE
+#  define FMT_USE_LONG_DOUBLE 1
+#endif
+
+// __builtin_clz is broken in clang with Microsoft CodeGen:
+// https://github.com/fmtlib/fmt/issues/519
+#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clz)) && !FMT_MSC_VER
+#  define FMT_BUILTIN_CLZ(n) __builtin_clz(n)
+#endif
+#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clzll)) && !FMT_MSC_VER
+#  define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n)
+#endif
+
+// Some compilers masquerade as both MSVC and GCC-likes or otherwise support
+// __builtin_clz and __builtin_clzll, so only define FMT_BUILTIN_CLZ using the
+// MSVC intrinsics if the clz and clzll builtins are not available.
+#if FMT_MSC_VER && !defined(FMT_BUILTIN_CLZLL) && !defined(_MANAGED)
+#  include <intrin.h>  // _BitScanReverse, _BitScanReverse64
+
+FMT_BEGIN_NAMESPACE
+namespace internal {
+// Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning.
+#  ifndef __clang__
+#    pragma intrinsic(_BitScanReverse)
+#  endif
+inline uint32_t clz(uint32_t x) {
+  unsigned long r = 0;
+  _BitScanReverse(&r, x);
+
+  FMT_ASSERT(x != 0, "");
+  // Static analysis complains about using uninitialized data
+  // "r", but the only way that can happen is if "x" is 0,
+  // which the callers guarantee to not happen.
+#  pragma warning(suppress : 6102)
+  return 31 - r;
+}
+#  define FMT_BUILTIN_CLZ(n) internal::clz(n)
+
+#  if defined(_WIN64) && !defined(__clang__)
+#    pragma intrinsic(_BitScanReverse64)
+#  endif
+
+inline uint32_t clzll(uint64_t x) {
+  unsigned long r = 0;
+#  ifdef _WIN64
+  _BitScanReverse64(&r, x);
+#  else
+  // Scan the high 32 bits.
+  if (_BitScanReverse(&r, static_cast<uint32_t>(x >> 32))) return 63 - (r + 32);
+
+  // Scan the low 32 bits.
+  _BitScanReverse(&r, static_cast<uint32_t>(x));
+#  endif
+
+  FMT_ASSERT(x != 0, "");
+  // Static analysis complains about using uninitialized data
+  // "r", but the only way that can happen is if "x" is 0,
+  // which the callers guarantee to not happen.
+#  pragma warning(suppress : 6102)
+  return 63 - r;
+}
+#  define FMT_BUILTIN_CLZLL(n) internal::clzll(n)
+}  // namespace internal
+FMT_END_NAMESPACE
+#endif
+
+// Enable the deprecated numeric alignment.
+#ifndef FMT_NUMERIC_ALIGN
+#  define FMT_NUMERIC_ALIGN 1
+#endif
+
+// Enable the deprecated percent specifier.
+#ifndef FMT_DEPRECATED_PERCENT
+#  define FMT_DEPRECATED_PERCENT 0
+#endif
+
+FMT_BEGIN_NAMESPACE
+namespace internal {
+
+// An equivalent of `*reinterpret_cast<Dest*>(&source)` that doesn't have
+// undefined behavior (e.g. due to type aliasing).
+// Example: uint64_t d = bit_cast<uint64_t>(2.718);
+template <typename Dest, typename Source>
+inline Dest bit_cast(const Source& source) {
+  static_assert(sizeof(Dest) == sizeof(Source), "size mismatch");
+  Dest dest;
+  std::memcpy(&dest, &source, sizeof(dest));
+  return dest;
+}
+
+inline bool is_big_endian() {
+  const auto u = 1u;
+  struct bytes {
+    char data[sizeof(u)];
+  };
+  return bit_cast<bytes>(u).data[0] == 0;
+}
+
+// A fallback implementation of uintptr_t for systems that lack it.
+struct fallback_uintptr {
+  unsigned char value[sizeof(void*)];
+
+  fallback_uintptr() = default;
+  explicit fallback_uintptr(const void* p) {
+    *this = bit_cast<fallback_uintptr>(p);
+    if (is_big_endian()) {
+      for (size_t i = 0, j = sizeof(void*) - 1; i < j; ++i, --j)
+        std::swap(value[i], value[j]);
+    }
+  }
+};
+#ifdef UINTPTR_MAX
+using uintptr_t = ::uintptr_t;
+inline uintptr_t to_uintptr(const void* p) { return bit_cast<uintptr_t>(p); }
+#else
+using uintptr_t = fallback_uintptr;
+inline fallback_uintptr to_uintptr(const void* p) {
+  return fallback_uintptr(p);
+}
+#endif
+
+// Returns the largest possible value for type T. Same as
+// std::numeric_limits<T>::max() but shorter and not affected by the max macro.
+template <typename T> constexpr T max_value() {
+  return (std::numeric_limits<T>::max)();
+}
+template <typename T> constexpr int num_bits() {
+  return std::numeric_limits<T>::digits;
+}
+template <> constexpr int num_bits<fallback_uintptr>() {
+  return static_cast<int>(sizeof(void*) *
+                          std::numeric_limits<unsigned char>::digits);
+}
+
+// An approximation of iterator_t for pre-C++20 systems.
+template <typename T>
+using iterator_t = decltype(std::begin(std::declval<T&>()));
+
+// Detect the iterator category of *any* given type in a SFINAE-friendly way.
+// Unfortunately, older implementations of std::iterator_traits are not safe
+// for use in a SFINAE-context.
+template <typename It, typename Enable = void>
+struct iterator_category : std::false_type {};
+
+template <typename T> struct iterator_category<T*> {
+  using type = std::random_access_iterator_tag;
+};
+
+template <typename It>
+struct iterator_category<It, void_t<typename It::iterator_category>> {
+  using type = typename It::iterator_category;
+};
+
+// Detect if *any* given type models the OutputIterator concept.
+template <typename It> class is_output_iterator {
+  // Check for mutability because all iterator categories derived from
+  // std::input_iterator_tag *may* also meet the requirements of an
+  // OutputIterator, thereby falling into the category of 'mutable iterators'
+  // [iterator.requirements.general] clause 4. The compiler reveals this
+  // property only at the point of *actually dereferencing* the iterator!
+  template <typename U>
+  static decltype(*(std::declval<U>())) test(std::input_iterator_tag);
+  template <typename U> static char& test(std::output_iterator_tag);
+  template <typename U> static const char& test(...);
+
+  using type = decltype(test<It>(typename iterator_category<It>::type{}));
+
+ public:
+  enum { value = !std::is_const<remove_reference_t<type>>::value };
+};
+
+// A workaround for std::string not having mutable data() until C++17.
+template <typename Char> inline Char* get_data(std::basic_string<Char>& s) {
+  return &s[0];
+}
+template <typename Container>
+inline typename Container::value_type* get_data(Container& c) {
+  return c.data();
+}
+
+#if defined(_SECURE_SCL) && _SECURE_SCL
+// Make a checked iterator to avoid MSVC warnings.
+template <typename T> using checked_ptr = stdext::checked_array_iterator<T*>;
+template <typename T> checked_ptr<T> make_checked(T* p, std::size_t size) {
+  return {p, size};
+}
+#else
+template <typename T> using checked_ptr = T*;
+template <typename T> inline T* make_checked(T* p, std::size_t) { return p; }
+#endif
+
+template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)>
+inline checked_ptr<typename Container::value_type> reserve(
+    std::back_insert_iterator<Container>& it, std::size_t n) {
+  Container& c = get_container(it);
+  std::size_t size = c.size();
+  c.resize(size + n);
+  return make_checked(get_data(c) + size, n);
+}
+
+template <typename Iterator>
+inline Iterator& reserve(Iterator& it, std::size_t) {
+  return it;
+}
+
+// An output iterator that counts the number of objects written to it and
+// discards them.
+class counting_iterator {
+ private:
+  std::size_t count_;
+
+ public:
+  using iterator_category = std::output_iterator_tag;
+  using difference_type = std::ptrdiff_t;
+  using pointer = void;
+  using reference = void;
+  using _Unchecked_type = counting_iterator;  // Mark iterator as checked.
+
+  struct value_type {
+    template <typename T> void operator=(const T&) {}
+  };
+
+  counting_iterator() : count_(0) {}
+
+  std::size_t count() const { return count_; }
+
+  counting_iterator& operator++() {
+    ++count_;
+    return *this;
+  }
+
+  counting_iterator operator++(int) {
+    auto it = *this;
+    ++*this;
+    return it;
+  }
+
+  value_type operator*() const { return {}; }
+};
+
+template <typename OutputIt> class truncating_iterator_base {
+ protected:
+  OutputIt out_;
+  std::size_t limit_;
+  std::size_t count_;
+
+  truncating_iterator_base(OutputIt out, std::size_t limit)
+      : out_(out), limit_(limit), count_(0) {}
+
+ public:
+  using iterator_category = std::output_iterator_tag;
+  using value_type = typename std::iterator_traits<OutputIt>::value_type;
+  using difference_type = void;
+  using pointer = void;
+  using reference = void;
+  using _Unchecked_type =
+      truncating_iterator_base;  // Mark iterator as checked.
+
+  OutputIt base() const { return out_; }
+  std::size_t count() const { return count_; }
+};
+
+// An output iterator that truncates the output and counts the number of objects
+// written to it.
+template <typename OutputIt,
+          typename Enable = typename std::is_void<
+              typename std::iterator_traits<OutputIt>::value_type>::type>
+class truncating_iterator;
+
+template <typename OutputIt>
+class truncating_iterator<OutputIt, std::false_type>
+    : public truncating_iterator_base<OutputIt> {
+  mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;
+
+ public:
+  using value_type = typename truncating_iterator_base<OutputIt>::value_type;
+
+  truncating_iterator(OutputIt out, std::size_t limit)
+      : truncating_iterator_base<OutputIt>(out, limit) {}
+
+  truncating_iterator& operator++() {
+    if (this->count_++ < this->limit_) ++this->out_;
+    return *this;
+  }
+
+  truncating_iterator operator++(int) {
+    auto it = *this;
+    ++*this;
+    return it;
+  }
+
+  value_type& operator*() const {
+    return this->count_ < this->limit_ ? *this->out_ : blackhole_;
+  }
+};
+
+template <typename OutputIt>
+class truncating_iterator<OutputIt, std::true_type>
+    : public truncating_iterator_base<OutputIt> {
+ public:
+  truncating_iterator(OutputIt out, std::size_t limit)
+      : truncating_iterator_base<OutputIt>(out, limit) {}
+
+  template <typename T> truncating_iterator& operator=(T val) {
+    if (this->count_++ < this->limit_) *this->out_++ = val;
+    return *this;
+  }
+
+  truncating_iterator& operator++() { return *this; }
+  truncating_iterator& operator++(int) { return *this; }
+  truncating_iterator& operator*() { return *this; }
+};
+
+// A range with the specified output iterator and value type.
+template <typename OutputIt, typename T = typename OutputIt::value_type>
+class output_range {
+ private:
+  OutputIt it_;
+
+ public:
+  using value_type = T;
+  using iterator = OutputIt;
+  struct sentinel {};
+
+  explicit output_range(OutputIt it) : it_(it) {}
+  OutputIt begin() const { return it_; }
+  sentinel end() const { return {}; }  // Sentinel is not used yet.
+};
+
+template <typename Char>
+inline size_t count_code_points(basic_string_view<Char> s) {
+  return s.size();
+}
+
+// Counts the number of code points in a UTF-8 string.
+inline size_t count_code_points(basic_string_view<char> s) {
+  const char* data = s.data();
+  size_t num_code_points = 0;
+  for (size_t i = 0, size = s.size(); i != size; ++i) {
+    if ((data[i] & 0xc0) != 0x80) ++num_code_points;
+  }
+  return num_code_points;
+}
+
+inline size_t count_code_points(basic_string_view<char8_type> s) {
+  return count_code_points(basic_string_view<char>(
+      reinterpret_cast<const char*>(s.data()), s.size()));
+}
+
+template <typename Char>
+inline size_t code_point_index(basic_string_view<Char> s, size_t n) {
+  size_t size = s.size();
+  return n < size ? n : size;
+}
+
+// Calculates the index of the nth code point in a UTF-8 string.
+inline size_t code_point_index(basic_string_view<char8_type> s, size_t n) {
+  const char8_type* data = s.data();
+  size_t num_code_points = 0;
+  for (size_t i = 0, size = s.size(); i != size; ++i) {
+    if ((data[i] & 0xc0) != 0x80 && ++num_code_points > n) {
+      return i;
+    }
+  }
+  return s.size();
+}
+
+inline char8_type to_char8_t(char c) { return static_cast<char8_type>(c); }
+
+template <typename InputIt, typename OutChar>
+using needs_conversion = bool_constant<
+    std::is_same<typename std::iterator_traits<InputIt>::value_type,
+                 char>::value &&
+    std::is_same<OutChar, char8_type>::value>;
+
+template <typename OutChar, typename InputIt, typename OutputIt,
+          FMT_ENABLE_IF(!needs_conversion<InputIt, OutChar>::value)>
+OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) {
+  return std::copy(begin, end, it);
+}
+
+template <typename OutChar, typename InputIt, typename OutputIt,
+          FMT_ENABLE_IF(needs_conversion<InputIt, OutChar>::value)>
+OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) {
+  return std::transform(begin, end, it, to_char8_t);
+}
+
+#ifndef FMT_USE_GRISU
+#  define FMT_USE_GRISU 1
+#endif
+
+template <typename T> constexpr bool use_grisu() {
+  return FMT_USE_GRISU && std::numeric_limits<double>::is_iec559 &&
+         sizeof(T) <= sizeof(double);
+}
+
+template <typename T>
+template <typename U>
+void buffer<T>::append(const U* begin, const U* end) {
+  std::size_t new_size = size_ + to_unsigned(end - begin);
+  reserve(new_size);
+  std::uninitialized_copy(begin, end, make_checked(ptr_, capacity_) + size_);
+  size_ = new_size;
+}
+}  // namespace internal
+
+// A range with an iterator appending to a buffer.
+template <typename T>
+class buffer_range : public internal::output_range<
+                         std::back_insert_iterator<internal::buffer<T>>, T> {
+ public:
+  using iterator = std::back_insert_iterator<internal::buffer<T>>;
+  using internal::output_range<iterator, T>::output_range;
+  buffer_range(internal::buffer<T>& buf)
+      : internal::output_range<iterator, T>(std::back_inserter(buf)) {}
+};
+
+class FMT_DEPRECATED u8string_view
+    : public basic_string_view<internal::char8_type> {
+ public:
+  u8string_view(const char* s)
+      : basic_string_view<internal::char8_type>(
+            reinterpret_cast<const internal::char8_type*>(s)) {}
+  u8string_view(const char* s, size_t count) FMT_NOEXCEPT
+      : basic_string_view<internal::char8_type>(
+            reinterpret_cast<const internal::char8_type*>(s), count) {}
+};
+
+#if FMT_USE_USER_DEFINED_LITERALS
+inline namespace literals {
+FMT_DEPRECATED inline basic_string_view<internal::char8_type> operator"" _u(
+    const char* s, std::size_t n) {
+  return {reinterpret_cast<const internal::char8_type*>(s), n};
+}
+}  // namespace literals
+#endif
+
+// The number of characters to store in the basic_memory_buffer object itself
+// to avoid dynamic memory allocation.
+enum { inline_buffer_size = 500 };
+
+/**
+  \rst
+  A dynamically growing memory buffer for trivially copyable/constructible types
+  with the first ``SIZE`` elements stored in the object itself.
+
+  You can use one of the following type aliases for common character types:
+
+  +----------------+------------------------------+
+  | Type           | Definition                   |
+  +================+==============================+
+  | memory_buffer  | basic_memory_buffer<char>    |
+  +----------------+------------------------------+
+  | wmemory_buffer | basic_memory_buffer<wchar_t> |
+  +----------------+------------------------------+
+
+  **Example**::
+
+     fmt::memory_buffer out;
+     format_to(out, "The answer is {}.", 42);
+
+  This will append the following output to the ``out`` object:
+
+  .. code-block:: none
+
+     The answer is 42.
+
+  The output can be converted to an ``std::string`` with ``to_string(out)``.
+  \endrst
+ */
+template <typename T, std::size_t SIZE = inline_buffer_size,
+          typename Allocator = std::allocator<T>>
+class basic_memory_buffer : private Allocator, public internal::buffer<T> {
+ private:
+  T store_[SIZE];
+
+  // Deallocate memory allocated by the buffer.
+  void deallocate() {
+    T* data = this->data();
+    if (data != store_) Allocator::deallocate(data, this->capacity());
+  }
+
+ protected:
+  void grow(std::size_t size) FMT_OVERRIDE;
+
+ public:
+  using value_type = T;
+  using const_reference = const T&;
+
+  explicit basic_memory_buffer(const Allocator& alloc = Allocator())
+      : Allocator(alloc) {
+    this->set(store_, SIZE);
+  }
+  ~basic_memory_buffer() FMT_OVERRIDE { deallocate(); }
+
+ private:
+  // Move data from other to this buffer.
+  void move(basic_memory_buffer& other) {
+    Allocator &this_alloc = *this, &other_alloc = other;
+    this_alloc = std::move(other_alloc);
+    T* data = other.data();
+    std::size_t size = other.size(), capacity = other.capacity();
+    if (data == other.store_) {
+      this->set(store_, capacity);
+      std::uninitialized_copy(other.store_, other.store_ + size,
+                              internal::make_checked(store_, capacity));
+    } else {
+      this->set(data, capacity);
+      // Set pointer to the inline array so that delete is not called
+      // when deallocating.
+      other.set(other.store_, 0);
+    }
+    this->resize(size);
+  }
+
+ public:
+  /**
+    \rst
+    Constructs a :class:`fmt::basic_memory_buffer` object moving the content
+    of the other object to it.
+    \endrst
+   */
+  basic_memory_buffer(basic_memory_buffer&& other) FMT_NOEXCEPT { move(other); }
+
+  /**
+    \rst
+    Moves the content of the other ``basic_memory_buffer`` object to this one.
+    \endrst
+   */
+  basic_memory_buffer& operator=(basic_memory_buffer&& other) FMT_NOEXCEPT {
+    FMT_ASSERT(this != &other, "");
+    deallocate();
+    move(other);
+    return *this;
+  }
+
+  // Returns a copy of the allocator associated with this buffer.
+  Allocator get_allocator() const { return *this; }
+};
+
+template <typename T, std::size_t SIZE, typename Allocator>
+void basic_memory_buffer<T, SIZE, Allocator>::grow(std::size_t size) {
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+  if (size > 1000) throw std::runtime_error("fuzz mode - won't grow that much");
+#endif
+  std::size_t old_capacity = this->capacity();
+  std::size_t new_capacity = old_capacity + old_capacity / 2;
+  if (size > new_capacity) new_capacity = size;
+  T* old_data = this->data();
+  T* new_data = std::allocator_traits<Allocator>::allocate(*this, new_capacity);
+  // The following code doesn't throw, so the raw pointer above doesn't leak.
+  std::uninitialized_copy(old_data, old_data + this->size(),
+                          internal::make_checked(new_data, new_capacity));
+  this->set(new_data, new_capacity);
+  // deallocate must not throw according to the standard, but even if it does,
+  // the buffer already uses the new storage and will deallocate it in
+  // destructor.
+  if (old_data != store_) Allocator::deallocate(old_data, old_capacity);
+}
+
+using memory_buffer = basic_memory_buffer<char>;
+using wmemory_buffer = basic_memory_buffer<wchar_t>;
+
+/** A formatting error such as invalid format string. */
+FMT_CLASS_API
+class FMT_API format_error : public std::runtime_error {
+ public:
+  explicit format_error(const char* message) : std::runtime_error(message) {}
+  explicit format_error(const std::string& message)
+      : std::runtime_error(message) {}
+  format_error(const format_error&) = default;
+  format_error& operator=(const format_error&) = default;
+  format_error(format_error&&) = default;
+  format_error& operator=(format_error&&) = default;
+  ~format_error() FMT_NOEXCEPT FMT_OVERRIDE;
+};
+
+namespace internal {
+
+// Returns true if value is negative, false otherwise.
+// Same as `value < 0` but doesn't produce warnings if T is an unsigned type.
+template <typename T, FMT_ENABLE_IF(std::numeric_limits<T>::is_signed)>
+FMT_CONSTEXPR bool is_negative(T value) {
+  return value < 0;
+}
+template <typename T, FMT_ENABLE_IF(!std::numeric_limits<T>::is_signed)>
+FMT_CONSTEXPR bool is_negative(T) {
+  return false;
+}
+
+template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
+FMT_CONSTEXPR bool is_supported_floating_point(T) {
+  return (std::is_same<T, float>::value && FMT_USE_FLOAT) ||
+         (std::is_same<T, double>::value && FMT_USE_DOUBLE) ||
+         (std::is_same<T, long double>::value && FMT_USE_LONG_DOUBLE);
+}
+
+// Smallest of uint32_t, uint64_t, uint128_t that is large enough to
+// represent all values of T.
+template <typename T>
+using uint32_or_64_or_128_t = conditional_t<
+    std::numeric_limits<T>::digits <= 32, uint32_t,
+    conditional_t<std::numeric_limits<T>::digits <= 64, uint64_t, uint128_t>>;
+
+// Static data is placed in this class template for the header-only config.
+template <typename T = void> struct FMT_EXTERN_TEMPLATE_API basic_data {
+  static const uint64_t powers_of_10_64[];
+  static const uint32_t zero_or_powers_of_10_32[];
+  static const uint64_t zero_or_powers_of_10_64[];
+  static const uint64_t pow10_significands[];
+  static const int16_t pow10_exponents[];
+  static const char digits[];
+  static const char hex_digits[];
+  static const char foreground_color[];
+  static const char background_color[];
+  static const char reset_color[5];
+  static const wchar_t wreset_color[5];
+  static const char signs[];
+};
+
+FMT_EXTERN template struct basic_data<void>;
+
+// This is a struct rather than an alias to avoid shadowing warnings in gcc.
+struct data : basic_data<> {};
+
+#ifdef FMT_BUILTIN_CLZLL
+// Returns the number of decimal digits in n. Leading zeros are not counted
+// except for n == 0 in which case count_digits returns 1.
+inline int count_digits(uint64_t n) {
+  // Based on http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
+  // and the benchmark https://github.com/localvoid/cxx-benchmark-count-digits.
+  int t = (64 - FMT_BUILTIN_CLZLL(n | 1)) * 1233 >> 12;
+  return t - (n < data::zero_or_powers_of_10_64[t]) + 1;
+}
+#else
+// Fallback version of count_digits used when __builtin_clz is not available.
+inline int count_digits(uint64_t n) {
+  int count = 1;
+  for (;;) {
+    // Integer division is slow so do it for a group of four digits instead
+    // of for every digit. The idea comes from the talk by Alexandrescu
+    // "Three Optimization Tips for C++". See speed-test for a comparison.
+    if (n < 10) return count;
+    if (n < 100) return count + 1;
+    if (n < 1000) return count + 2;
+    if (n < 10000) return count + 3;
+    n /= 10000u;
+    count += 4;
+  }
+}
+#endif
+
+#if FMT_USE_INT128
+inline int count_digits(uint128_t n) {
+  int count = 1;
+  for (;;) {
+    // Integer division is slow so do it for a group of four digits instead
+    // of for every digit. The idea comes from the talk by Alexandrescu
+    // "Three Optimization Tips for C++". See speed-test for a comparison.
+    if (n < 10) return count;
+    if (n < 100) return count + 1;
+    if (n < 1000) return count + 2;
+    if (n < 10000) return count + 3;
+    n /= 10000U;
+    count += 4;
+  }
+}
+#endif
+
+// Counts the number of digits in n. BITS = log2(radix).
+template <unsigned BITS, typename UInt> inline int count_digits(UInt n) {
+  int num_digits = 0;
+  do {
+    ++num_digits;
+  } while ((n >>= BITS) != 0);
+  return num_digits;
+}
+
+template <> int count_digits<4>(internal::fallback_uintptr n);
+
+#if FMT_GCC_VERSION || FMT_CLANG_VERSION
+#  define FMT_ALWAYS_INLINE inline __attribute__((always_inline))
+#else
+#  define FMT_ALWAYS_INLINE
+#endif
+
+#ifdef FMT_BUILTIN_CLZ
+// Optional version of count_digits for better performance on 32-bit platforms.
+inline int count_digits(uint32_t n) {
+  int t = (32 - FMT_BUILTIN_CLZ(n | 1)) * 1233 >> 12;
+  return t - (n < data::zero_or_powers_of_10_32[t]) + 1;
+}
+#endif
+
+template <typename Char> FMT_API std::string grouping_impl(locale_ref loc);
+template <typename Char> inline std::string grouping(locale_ref loc) {
+  return grouping_impl<char>(loc);
+}
+template <> inline std::string grouping<wchar_t>(locale_ref loc) {
+  return grouping_impl<wchar_t>(loc);
+}
+
+template <typename Char> FMT_API Char thousands_sep_impl(locale_ref loc);
+template <typename Char> inline Char thousands_sep(locale_ref loc) {
+  return Char(thousands_sep_impl<char>(loc));
+}
+template <> inline wchar_t thousands_sep(locale_ref loc) {
+  return thousands_sep_impl<wchar_t>(loc);
+}
+
+template <typename Char> FMT_API Char decimal_point_impl(locale_ref loc);
+template <typename Char> inline Char decimal_point(locale_ref loc) {
+  return Char(decimal_point_impl<char>(loc));
+}
+template <> inline wchar_t decimal_point(locale_ref loc) {
+  return decimal_point_impl<wchar_t>(loc);
+}
+
+// Formats a decimal unsigned integer value writing into buffer.
+// add_thousands_sep is called after writing each char to add a thousands
+// separator if necessary.
+template <typename UInt, typename Char, typename F>
+inline Char* format_decimal(Char* buffer, UInt value, int num_digits,
+                            F add_thousands_sep) {
+  FMT_ASSERT(num_digits >= 0, "invalid digit count");
+  buffer += num_digits;
+  Char* end = buffer;
+  while (value >= 100) {
+    // Integer division is slow so do it for a group of two digits instead
+    // of for every digit. The idea comes from the talk by Alexandrescu
+    // "Three Optimization Tips for C++". See speed-test for a comparison.
+    auto index = static_cast<unsigned>((value % 100) * 2);
+    value /= 100;
+    *--buffer = static_cast<Char>(data::digits[index + 1]);
+    add_thousands_sep(buffer);
+    *--buffer = static_cast<Char>(data::digits[index]);
+    add_thousands_sep(buffer);
+  }
+  if (value < 10) {
+    *--buffer = static_cast<Char>('0' + value);
+    return end;
+  }
+  auto index = static_cast<unsigned>(value * 2);
+  *--buffer = static_cast<Char>(data::digits[index + 1]);
+  add_thousands_sep(buffer);
+  *--buffer = static_cast<Char>(data::digits[index]);
+  return end;
+}
+
+template <typename Int> constexpr int digits10() FMT_NOEXCEPT {
+  return std::numeric_limits<Int>::digits10;
+}
+template <> constexpr int digits10<int128_t>() FMT_NOEXCEPT { return 38; }
+template <> constexpr int digits10<uint128_t>() FMT_NOEXCEPT { return 38; }
+
+template <typename Char, typename UInt, typename Iterator, typename F>
+inline Iterator format_decimal(Iterator out, UInt value, int num_digits,
+                               F add_thousands_sep) {
+  FMT_ASSERT(num_digits >= 0, "invalid digit count");
+  // Buffer should be large enough to hold all digits (<= digits10 + 1).
+  enum { max_size = digits10<UInt>() + 1 };
+  Char buffer[2 * max_size];
+  auto end = format_decimal(buffer, value, num_digits, add_thousands_sep);
+  return internal::copy_str<Char>(buffer, end, out);
+}
+
+template <typename Char, typename It, typename UInt>
+inline It format_decimal(It out, UInt value, int num_digits) {
+  return format_decimal<Char>(out, value, num_digits, [](Char*) {});
+}
+
+template <unsigned BASE_BITS, typename Char, typename UInt>
+inline Char* format_uint(Char* buffer, UInt value, int num_digits,
+                         bool upper = false) {
+  buffer += num_digits;
+  Char* end = buffer;
+  do {
+    const char* digits = upper ? "0123456789ABCDEF" : data::hex_digits;
+    unsigned digit = (value & ((1 << BASE_BITS) - 1));
+    *--buffer = static_cast<Char>(BASE_BITS < 4 ? static_cast<char>('0' + digit)
+                                                : digits[digit]);
+  } while ((value >>= BASE_BITS) != 0);
+  return end;
+}
+
+template <unsigned BASE_BITS, typename Char>
+Char* format_uint(Char* buffer, internal::fallback_uintptr n, int num_digits,
+                  bool = false) {
+  auto char_digits = std::numeric_limits<unsigned char>::digits / 4;
+  int start = (num_digits + char_digits - 1) / char_digits - 1;
+  if (int start_digits = num_digits % char_digits) {
+    unsigned value = n.value[start--];
+    buffer = format_uint<BASE_BITS>(buffer, value, start_digits);
+  }
+  for (; start >= 0; --start) {
+    unsigned value = n.value[start];
+    buffer += char_digits;
+    auto p = buffer;
+    for (int i = 0; i < char_digits; ++i) {
+      unsigned digit = (value & ((1 << BASE_BITS) - 1));
+      *--p = static_cast<Char>(data::hex_digits[digit]);
+      value >>= BASE_BITS;
+    }
+  }
+  return buffer;
+}
+
+template <unsigned BASE_BITS, typename Char, typename It, typename UInt>
+inline It format_uint(It out, UInt value, int num_digits, bool upper = false) {
+  // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1).
+  char buffer[num_bits<UInt>() / BASE_BITS + 1];
+  format_uint<BASE_BITS>(buffer, value, num_digits, upper);
+  return internal::copy_str<Char>(buffer, buffer + num_digits, out);
+}
+
+// A converter from UTF-8 to UTF-16.
+class utf8_to_utf16 {
+ private:
+  wmemory_buffer buffer_;
+
+ public:
+  FMT_API explicit utf8_to_utf16(string_view s);
+  operator wstring_view() const { return {&buffer_[0], size()}; }
+  size_t size() const { return buffer_.size() - 1; }
+  const wchar_t* c_str() const { return &buffer_[0]; }
+  std::wstring str() const { return {&buffer_[0], size()}; }
+};
+
+template <typename T = void> struct null {};
+
+// Workaround an array initialization issue in gcc 4.8.
+template <typename Char> struct fill_t {
+ private:
+  enum { max_size = 4 };
+  Char data_[max_size];
+  unsigned char size_;
+
+ public:
+  FMT_CONSTEXPR void operator=(basic_string_view<Char> s) {
+    auto size = s.size();
+    if (size > max_size) {
+      FMT_THROW(format_error("invalid fill"));
+      return;
+    }
+    for (size_t i = 0; i < size; ++i) data_[i] = s[i];
+    size_ = static_cast<unsigned char>(size);
+  }
+
+  size_t size() const { return size_; }
+  const Char* data() const { return data_; }
+
+  FMT_CONSTEXPR Char& operator[](size_t index) { return data_[index]; }
+  FMT_CONSTEXPR const Char& operator[](size_t index) const {
+    return data_[index];
+  }
+
+  static FMT_CONSTEXPR fill_t<Char> make() {
+    auto fill = fill_t<Char>();
+    fill[0] = Char(' ');
+    fill.size_ = 1;
+    return fill;
+  }
+};
+}  // namespace internal
+
+// We cannot use enum classes as bit fields because of a gcc bug
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414.
+namespace align {
+enum type { none, left, right, center, numeric };
+}
+using align_t = align::type;
+
+namespace sign {
+enum type { none, minus, plus, space };
+}
+using sign_t = sign::type;
+
+// Format specifiers for built-in and string types.
+template <typename Char> struct basic_format_specs {
+  int width;
+  int precision;
+  char type;
+  align_t align : 4;
+  sign_t sign : 3;
+  bool alt : 1;  // Alternate form ('#').
+  internal::fill_t<Char> fill;
+
+  constexpr basic_format_specs()
+      : width(0),
+        precision(-1),
+        type(0),
+        align(align::none),
+        sign(sign::none),
+        alt(false),
+        fill(internal::fill_t<Char>::make()) {}
+};
+
+using format_specs = basic_format_specs<char>;
+
+namespace internal {
+
+// A floating-point presentation format.
+enum class float_format : unsigned char {
+  general,  // General: exponent notation or fixed point based on magnitude.
+  exp,      // Exponent notation with the default precision of 6, e.g. 1.2e-3.
+  fixed,    // Fixed point with the default precision of 6, e.g. 0.0012.
+  hex
+};
+
+struct float_specs {
+  int precision;
+  float_format format : 8;
+  sign_t sign : 8;
+  bool upper : 1;
+  bool locale : 1;
+  bool percent : 1;
+  bool binary32 : 1;
+  bool use_grisu : 1;
+  bool showpoint : 1;
+};
+
+// Writes the exponent exp in the form "[+-]d{2,3}" to buffer.
+template <typename Char, typename It> It write_exponent(int exp, It it) {
+  FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range");
+  if (exp < 0) {
+    *it++ = static_cast<Char>('-');
+    exp = -exp;
+  } else {
+    *it++ = static_cast<Char>('+');
+  }
+  if (exp >= 100) {
+    const char* top = data::digits + (exp / 100) * 2;
+    if (exp >= 1000) *it++ = static_cast<Char>(top[0]);
+    *it++ = static_cast<Char>(top[1]);
+    exp %= 100;
+  }
+  const char* d = data::digits + exp * 2;
+  *it++ = static_cast<Char>(d[0]);
+  *it++ = static_cast<Char>(d[1]);
+  return it;
+}
+
+template <typename Char> class float_writer {
+ private:
+  // The number is given as v = digits_ * pow(10, exp_).
+  const char* digits_;
+  int num_digits_;
+  int exp_;
+  size_t size_;
+  float_specs specs_;
+  Char decimal_point_;
+
+  template <typename It> It prettify(It it) const {
+    // pow(10, full_exp - 1) <= v <= pow(10, full_exp).
+    int full_exp = num_digits_ + exp_;
+    if (specs_.format == float_format::exp) {
+      // Insert a decimal point after the first digit and add an exponent.
+      *it++ = static_cast<Char>(*digits_);
+      int num_zeros = specs_.precision - num_digits_;
+      if (num_digits_ > 1 || specs_.showpoint) *it++ = decimal_point_;
+      it = copy_str<Char>(digits_ + 1, digits_ + num_digits_, it);
+      if (num_zeros > 0 && specs_.showpoint)
+        it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
+      *it++ = static_cast<Char>(specs_.upper ? 'E' : 'e');
+      return write_exponent<Char>(full_exp - 1, it);
+    }
+    if (num_digits_ <= full_exp) {
+      // 1234e7 -> 12340000000[.0+]
+      it = copy_str<Char>(digits_, digits_ + num_digits_, it);
+      it = std::fill_n(it, full_exp - num_digits_, static_cast<Char>('0'));
+      if (specs_.showpoint || specs_.precision < 0) {
+        *it++ = decimal_point_;
+        int num_zeros = specs_.precision - full_exp;
+        if (num_zeros <= 0) {
+          if (specs_.format != float_format::fixed)
+            *it++ = static_cast<Char>('0');
+          return it;
+        }
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+        if (num_zeros > 1000)
+          throw std::runtime_error("fuzz mode - avoiding excessive cpu use");
+#endif
+        it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
+      }
+    } else if (full_exp > 0) {
+      // 1234e-2 -> 12.34[0+]
+      it = copy_str<Char>(digits_, digits_ + full_exp, it);
+      if (!specs_.showpoint) {
+        // Remove trailing zeros.
+        int num_digits = num_digits_;
+        while (num_digits > full_exp && digits_[num_digits - 1] == '0')
+          --num_digits;
+        if (num_digits != full_exp) *it++ = decimal_point_;
+        return copy_str<Char>(digits_ + full_exp, digits_ + num_digits, it);
+      }
+      *it++ = decimal_point_;
+      it = copy_str<Char>(digits_ + full_exp, digits_ + num_digits_, it);
+      if (specs_.precision > num_digits_) {
+        // Add trailing zeros.
+        int num_zeros = specs_.precision - num_digits_;
+        it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
+      }
+    } else {
+      // 1234e-6 -> 0.001234
+      *it++ = static_cast<Char>('0');
+      int num_zeros = -full_exp;
+      int num_digits = num_digits_;
+      if (num_digits == 0 && specs_.precision >= 0 &&
+          specs_.precision < num_zeros) {
+        num_zeros = specs_.precision;
+      }
+      // Remove trailing zeros.
+      if (!specs_.showpoint)
+        while (num_digits > 0 && digits_[num_digits - 1] == '0') --num_digits;
+      if (num_zeros != 0 || num_digits != 0 || specs_.showpoint) {
+        *it++ = decimal_point_;
+        it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
+        it = copy_str<Char>(digits_, digits_ + num_digits, it);
+      }
+    }
+    return it;
+  }
+
+ public:
+  float_writer(const char* digits, int num_digits, int exp, float_specs specs,
+               Char decimal_point)
+      : digits_(digits),
+        num_digits_(num_digits),
+        exp_(exp),
+        specs_(specs),
+        decimal_point_(decimal_point) {
+    int full_exp = num_digits + exp - 1;
+    int precision = specs.precision > 0 ? specs.precision : 16;
+    if (specs_.format == float_format::general &&
+        !(full_exp >= -4 && full_exp < precision)) {
+      specs_.format = float_format::exp;
+    }
+    size_ = prettify(counting_iterator()).count();
+    size_ += specs.sign ? 1 : 0;
+  }
+
+  size_t size() const { return size_; }
+  size_t width() const { return size(); }
+
+  template <typename It> void operator()(It&& it) {
+    if (specs_.sign) *it++ = static_cast<Char>(data::signs[specs_.sign]);
+    it = prettify(it);
+  }
+};
+
+template <typename T>
+int format_float(T value, int precision, float_specs specs, buffer<char>& buf);
+
+// Formats a floating-point number with snprintf.
+template <typename T>
+int snprintf_float(T value, int precision, float_specs specs,
+                   buffer<char>& buf);
+
+template <typename T> T promote_float(T value) { return value; }
+inline double promote_float(float value) { return static_cast<double>(value); }
+
+template <typename Handler>
+FMT_CONSTEXPR void handle_int_type_spec(char spec, Handler&& handler) {
+  switch (spec) {
+  case 0:
+  case 'd':
+    handler.on_dec();
+    break;
+  case 'x':
+  case 'X':
+    handler.on_hex();
+    break;
+  case 'b':
+  case 'B':
+    handler.on_bin();
+    break;
+  case 'o':
+    handler.on_oct();
+    break;
+  case 'n':
+  case 'L':
+    handler.on_num();
+    break;
+  default:
+    handler.on_error();
+  }
+}
+
+template <typename ErrorHandler = error_handler, typename Char>
+FMT_CONSTEXPR float_specs parse_float_type_spec(
+    const basic_format_specs<Char>& specs, ErrorHandler&& eh = {}) {
+  auto result = float_specs();
+  result.showpoint = specs.alt;
+  switch (specs.type) {
+  case 0:
+    result.format = float_format::general;
+    result.showpoint |= specs.precision > 0;
+    break;
+  case 'G':
+    result.upper = true;
+    FMT_FALLTHROUGH;
+  case 'g':
+    result.format = float_format::general;
+    break;
+  case 'E':
+    result.upper = true;
+    FMT_FALLTHROUGH;
+  case 'e':
+    result.format = float_format::exp;
+    result.showpoint |= specs.precision != 0;
+    break;
+  case 'F':
+    result.upper = true;
+    FMT_FALLTHROUGH;
+  case 'f':
+    result.format = float_format::fixed;
+    result.showpoint |= specs.precision != 0;
+    break;
+#if FMT_DEPRECATED_PERCENT
+  case '%':
+    result.format = float_format::fixed;
+    result.percent = true;
+    break;
+#endif
+  case 'A':
+    result.upper = true;
+    FMT_FALLTHROUGH;
+  case 'a':
+    result.format = float_format::hex;
+    break;
+  case 'n':
+    result.locale = true;
+    break;
+  default:
+    eh.on_error("invalid type specifier");
+    break;
+  }
+  return result;
+}
+
+template <typename Char, typename Handler>
+FMT_CONSTEXPR void handle_char_specs(const basic_format_specs<Char>* specs,
+                                     Handler&& handler) {
+  if (!specs) return handler.on_char();
+  if (specs->type && specs->type != 'c') return handler.on_int();
+  if (specs->align == align::numeric || specs->sign != sign::none || specs->alt)
+    handler.on_error("invalid format specifier for char");
+  handler.on_char();
+}
+
+template <typename Char, typename Handler>
+FMT_CONSTEXPR void handle_cstring_type_spec(Char spec, Handler&& handler) {
+  if (spec == 0 || spec == 's')
+    handler.on_string();
+  else if (spec == 'p')
+    handler.on_pointer();
+  else
+    handler.on_error("invalid type specifier");
+}
+
+template <typename Char, typename ErrorHandler>
+FMT_CONSTEXPR void check_string_type_spec(Char spec, ErrorHandler&& eh) {
+  if (spec != 0 && spec != 's') eh.on_error("invalid type specifier");
+}
+
+template <typename Char, typename ErrorHandler>
+FMT_CONSTEXPR void check_pointer_type_spec(Char spec, ErrorHandler&& eh) {
+  if (spec != 0 && spec != 'p') eh.on_error("invalid type specifier");
+}
+
+template <typename ErrorHandler> class int_type_checker : private ErrorHandler {
+ public:
+  FMT_CONSTEXPR explicit int_type_checker(ErrorHandler eh) : ErrorHandler(eh) {}
+
+  FMT_CONSTEXPR void on_dec() {}
+  FMT_CONSTEXPR void on_hex() {}
+  FMT_CONSTEXPR void on_bin() {}
+  FMT_CONSTEXPR void on_oct() {}
+  FMT_CONSTEXPR void on_num() {}
+
+  FMT_CONSTEXPR void on_error() {
+    ErrorHandler::on_error("invalid type specifier");
+  }
+};
+
+template <typename ErrorHandler>
+class char_specs_checker : public ErrorHandler {
+ private:
+  char type_;
+
+ public:
+  FMT_CONSTEXPR char_specs_checker(char type, ErrorHandler eh)
+      : ErrorHandler(eh), type_(type) {}
+
+  FMT_CONSTEXPR void on_int() {
+    handle_int_type_spec(type_, int_type_checker<ErrorHandler>(*this));
+  }
+  FMT_CONSTEXPR void on_char() {}
+};
+
+template <typename ErrorHandler>
+class cstring_type_checker : public ErrorHandler {
+ public:
+  FMT_CONSTEXPR explicit cstring_type_checker(ErrorHandler eh)
+      : ErrorHandler(eh) {}
+
+  FMT_CONSTEXPR void on_string() {}
+  FMT_CONSTEXPR void on_pointer() {}
+};
+
+template <typename Context>
+void arg_map<Context>::init(const basic_format_args<Context>& args) {
+  if (map_) return;
+  map_ = new entry[internal::to_unsigned(args.max_size())];
+  if (args.is_packed()) {
+    for (int i = 0;; ++i) {
+      internal::type arg_type = args.type(i);
+      if (arg_type == internal::type::none_type) return;
+      if (arg_type == internal::type::named_arg_type)
+        push_back(args.values_[i]);
+    }
+  }
+  for (int i = 0, n = args.max_size(); i < n; ++i) {
+    auto type = args.args_[i].type_;
+    if (type == internal::type::named_arg_type) push_back(args.args_[i].value_);
+  }
+}
+
+template <typename Char> struct nonfinite_writer {
+  sign_t sign;
+  const char* str;
+  static constexpr size_t str_size = 3;
+
+  size_t size() const { return str_size + (sign ? 1 : 0); }
+  size_t width() const { return size(); }
+
+  template <typename It> void operator()(It&& it) const {
+    if (sign) *it++ = static_cast<Char>(data::signs[sign]);
+    it = copy_str<Char>(str, str + str_size, it);
+  }
+};
+
+template <typename OutputIt, typename Char>
+FMT_NOINLINE OutputIt fill(OutputIt it, size_t n, const fill_t<Char>& fill) {
+  auto fill_size = fill.size();
+  if (fill_size == 1) return std::fill_n(it, n, fill[0]);
+  for (size_t i = 0; i < n; ++i) it = std::copy_n(fill.data(), fill_size, it);
+  return it;
+}
+
+// This template provides operations for formatting and writing data into a
+// character range.
+template <typename Range> class basic_writer {
+ public:
+  using char_type = typename Range::value_type;
+  using iterator = typename Range::iterator;
+  using format_specs = basic_format_specs<char_type>;
+
+ private:
+  iterator out_;  // Output iterator.
+  locale_ref locale_;
+
+  // Attempts to reserve space for n extra characters in the output range.
+  // Returns a pointer to the reserved range or a reference to out_.
+  auto reserve(std::size_t n) -> decltype(internal::reserve(out_, n)) {
+    return internal::reserve(out_, n);
+  }
+
+  template <typename F> struct padded_int_writer {
+    size_t size_;
+    string_view prefix;
+    char_type fill;
+    std::size_t padding;
+    F f;
+
+    size_t size() const { return size_; }
+    size_t width() const { return size_; }
+
+    template <typename It> void operator()(It&& it) const {
+      if (prefix.size() != 0)
+        it = copy_str<char_type>(prefix.begin(), prefix.end(), it);
+      it = std::fill_n(it, padding, fill);
+      f(it);
+    }
+  };
+
+  // Writes an integer in the format
+  //   <left-padding><prefix><numeric-padding><digits><right-padding>
+  // where <digits> are written by f(it).
+  template <typename F>
+  void write_int(int num_digits, string_view prefix, format_specs specs, F f) {
+    std::size_t size = prefix.size() + to_unsigned(num_digits);
+    char_type fill = specs.fill[0];
+    std::size_t padding = 0;
+    if (specs.align == align::numeric) {
+      auto unsiged_width = to_unsigned(specs.width);
+      if (unsiged_width > size) {
+        padding = unsiged_width - size;
+        size = unsiged_width;
+      }
+    } else if (specs.precision > num_digits) {
+      size = prefix.size() + to_unsigned(specs.precision);
+      padding = to_unsigned(specs.precision - num_digits);
+      fill = static_cast<char_type>('0');
+    }
+    if (specs.align == align::none) specs.align = align::right;
+    write_padded(specs, padded_int_writer<F>{size, prefix, fill, padding, f});
+  }
+
+  // Writes a decimal integer.
+  template <typename Int> void write_decimal(Int value) {
+    auto abs_value = static_cast<uint32_or_64_or_128_t<Int>>(value);
+    bool negative = is_negative(value);
+    // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer.
+    if (negative) abs_value = ~abs_value + 1;
+    int num_digits = count_digits(abs_value);
+    auto&& it = reserve((negative ? 1 : 0) + static_cast<size_t>(num_digits));
+    if (negative) *it++ = static_cast<char_type>('-');
+    it = format_decimal<char_type>(it, abs_value, num_digits);
+  }
+
+  // The handle_int_type_spec handler that writes an integer.
+  template <typename Int, typename Specs> struct int_writer {
+    using unsigned_type = uint32_or_64_or_128_t<Int>;
+
+    basic_writer<Range>& writer;
+    const Specs& specs;
+    unsigned_type abs_value;
+    char prefix[4];
+    unsigned prefix_size;
+
+    string_view get_prefix() const { return string_view(prefix, prefix_size); }
+
+    int_writer(basic_writer<Range>& w, Int value, const Specs& s)
+        : writer(w),
+          specs(s),
+          abs_value(static_cast<unsigned_type>(value)),
+          prefix_size(0) {
+      if (is_negative(value)) {
+        prefix[0] = '-';
+        ++prefix_size;
+        abs_value = 0 - abs_value;
+      } else if (specs.sign != sign::none && specs.sign != sign::minus) {
+        prefix[0] = specs.sign == sign::plus ? '+' : ' ';
+        ++prefix_size;
+      }
+    }
+
+    struct dec_writer {
+      unsigned_type abs_value;
+      int num_digits;
+
+      template <typename It> void operator()(It&& it) const {
+        it = internal::format_decimal<char_type>(it, abs_value, num_digits);
+      }
+    };
+
+    void on_dec() {
+      int num_digits = count_digits(abs_value);
+      writer.write_int(num_digits, get_prefix(), specs,
+                       dec_writer{abs_value, num_digits});
+    }
+
+    struct hex_writer {
+      int_writer& self;
+      int num_digits;
+
+      template <typename It> void operator()(It&& it) const {
+        it = format_uint<4, char_type>(it, self.abs_value, num_digits,
+                                       self.specs.type != 'x');
+      }
+    };
+
+    void on_hex() {
+      if (specs.alt) {
+        prefix[prefix_size++] = '0';
+        prefix[prefix_size++] = specs.type;
+      }
+      int num_digits = count_digits<4>(abs_value);
+      writer.write_int(num_digits, get_prefix(), specs,
+                       hex_writer{*this, num_digits});
+    }
+
+    template <int BITS> struct bin_writer {
+      unsigned_type abs_value;
+      int num_digits;
+
+      template <typename It> void operator()(It&& it) const {
+        it = format_uint<BITS, char_type>(it, abs_value, num_digits);
+      }
+    };
+
+    void on_bin() {
+      if (specs.alt) {
+        prefix[prefix_size++] = '0';
+        prefix[prefix_size++] = static_cast<char>(specs.type);
+      }
+      int num_digits = count_digits<1>(abs_value);
+      writer.write_int(num_digits, get_prefix(), specs,
+                       bin_writer<1>{abs_value, num_digits});
+    }
+
+    void on_oct() {
+      int num_digits = count_digits<3>(abs_value);
+      if (specs.alt && specs.precision <= num_digits && abs_value != 0) {
+        // Octal prefix '0' is counted as a digit, so only add it if precision
+        // is not greater than the number of digits.
+        prefix[prefix_size++] = '0';
+      }
+      writer.write_int(num_digits, get_prefix(), specs,
+                       bin_writer<3>{abs_value, num_digits});
+    }
+
+    enum { sep_size = 1 };
+
+    struct num_writer {
+      unsigned_type abs_value;
+      int size;
+      const std::string& groups;
+      char_type sep;
+
+      template <typename It> void operator()(It&& it) const {
+        basic_string_view<char_type> s(&sep, sep_size);
+        // Index of a decimal digit with the least significant digit having
+        // index 0.
+        int digit_index = 0;
+        std::string::const_iterator group = groups.cbegin();
+        it = format_decimal<char_type>(
+            it, abs_value, size,
+            [this, s, &group, &digit_index](char_type*& buffer) {
+              if (*group <= 0 || ++digit_index % *group != 0 ||
+                  *group == max_value<char>())
+                return;
+              if (group + 1 != groups.cend()) {
+                digit_index = 0;
+                ++group;
+              }
+              buffer -= s.size();
+              std::uninitialized_copy(s.data(), s.data() + s.size(),
+                                      make_checked(buffer, s.size()));
+            });
+      }
+    };
+
+    void on_num() {
+      std::string groups = grouping<char_type>(writer.locale_);
+      if (groups.empty()) return on_dec();
+      auto sep = thousands_sep<char_type>(writer.locale_);
+      if (!sep) return on_dec();
+      int num_digits = count_digits(abs_value);
+      int size = num_digits;
+      std::string::const_iterator group = groups.cbegin();
+      while (group != groups.cend() && num_digits > *group && *group > 0 &&
+             *group != max_value<char>()) {
+        size += sep_size;
+        num_digits -= *group;
+        ++group;
+      }
+      if (group == groups.cend())
+        size += sep_size * ((num_digits - 1) / groups.back());
+      writer.write_int(size, get_prefix(), specs,
+                       num_writer{abs_value, size, groups, sep});
+    }
+
+    FMT_NORETURN void on_error() {
+      FMT_THROW(format_error("invalid type specifier"));
+    }
+  };
+
+  template <typename Char> struct str_writer {
+    const Char* s;
+    size_t size_;
+
+    size_t size() const { return size_; }
+    size_t width() const {
+      return count_code_points(basic_string_view<Char>(s, size_));
+    }
+
+    template <typename It> void operator()(It&& it) const {
+      it = copy_str<char_type>(s, s + size_, it);
+    }
+  };
+
+  struct bytes_writer {
+    string_view bytes;
+
+    size_t size() const { return bytes.size(); }
+    size_t width() const { return bytes.size(); }
+
+    template <typename It> void operator()(It&& it) const {
+      const char* data = bytes.data();
+      it = copy_str<char>(data, data + size(), it);
+    }
+  };
+
+  template <typename UIntPtr> struct pointer_writer {
+    UIntPtr value;
+    int num_digits;
+
+    size_t size() const { return to_unsigned(num_digits) + 2; }
+    size_t width() const { return size(); }
+
+    template <typename It> void operator()(It&& it) const {
+      *it++ = static_cast<char_type>('0');
+      *it++ = static_cast<char_type>('x');
+      it = format_uint<4, char_type>(it, value, num_digits);
+    }
+  };
+
+ public:
+  explicit basic_writer(Range out, locale_ref loc = locale_ref())
+      : out_(out.begin()), locale_(loc) {}
+
+  iterator out() const { return out_; }
+
+  // Writes a value in the format
+  //   <left-padding><value><right-padding>
+  // where <value> is written by f(it).
+  template <typename F> void write_padded(const format_specs& specs, F&& f) {
+    // User-perceived width (in code points).
+    unsigned width = to_unsigned(specs.width);
+    size_t size = f.size();  // The number of code units.
+    size_t num_code_points = width != 0 ? f.width() : size;
+    if (width <= num_code_points) return f(reserve(size));
+    size_t padding = width - num_code_points;
+    size_t fill_size = specs.fill.size();
+    auto&& it = reserve(size + padding * fill_size);
+    if (specs.align == align::right) {
+      it = fill(it, padding, specs.fill);
+      f(it);
+    } else if (specs.align == align::center) {
+      std::size_t left_padding = padding / 2;
+      it = fill(it, left_padding, specs.fill);
+      f(it);
+      it = fill(it, padding - left_padding, specs.fill);
+    } else {
+      f(it);
+      it = fill(it, padding, specs.fill);
+    }
+  }
+
+  void write(int value) { write_decimal(value); }
+  void write(long value) { write_decimal(value); }
+  void write(long long value) { write_decimal(value); }
+
+  void write(unsigned value) { write_decimal(value); }
+  void write(unsigned long value) { write_decimal(value); }
+  void write(unsigned long long value) { write_decimal(value); }
+
+#if FMT_USE_INT128
+  void write(int128_t value) { write_decimal(value); }
+  void write(uint128_t value) { write_decimal(value); }
+#endif
+
+  template <typename T, typename Spec>
+  void write_int(T value, const Spec& spec) {
+    handle_int_type_spec(spec.type, int_writer<T, Spec>(*this, value, spec));
+  }
+
+  template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
+  void write(T value, format_specs specs = {}) {
+    if (const_check(!is_supported_floating_point(value))) {
+      return;
+    }
+    float_specs fspecs = parse_float_type_spec(specs);
+    fspecs.sign = specs.sign;
+    if (std::signbit(value)) {  // value < 0 is false for NaN so use signbit.
+      fspecs.sign = sign::minus;
+      value = -value;
+    } else if (fspecs.sign == sign::minus) {
+      fspecs.sign = sign::none;
+    }
+
+    if (!std::isfinite(value)) {
+      auto str = std::isinf(value) ? (fspecs.upper ? "INF" : "inf")
+                                   : (fspecs.upper ? "NAN" : "nan");
+      return write_padded(specs, nonfinite_writer<char_type>{fspecs.sign, str});
+    }
+
+    if (specs.align == align::none) {
+      specs.align = align::right;
+    } else if (specs.align == align::numeric) {
+      if (fspecs.sign) {
+        auto&& it = reserve(1);
+        *it++ = static_cast<char_type>(data::signs[fspecs.sign]);
+        fspecs.sign = sign::none;
+        if (specs.width != 0) --specs.width;
+      }
+      specs.align = align::right;
+    }
+
+    memory_buffer buffer;
+    if (fspecs.format == float_format::hex) {
+      if (fspecs.sign) buffer.push_back(data::signs[fspecs.sign]);
+      snprintf_float(promote_float(value), specs.precision, fspecs, buffer);
+      write_padded(specs, str_writer<char>{buffer.data(), buffer.size()});
+      return;
+    }
+    int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6;
+    if (fspecs.format == float_format::exp) {
+      if (precision == max_value<int>())
+        FMT_THROW(format_error("number is too big"));
+      else
+        ++precision;
+    }
+    if (const_check(std::is_same<T, float>())) fspecs.binary32 = true;
+    fspecs.use_grisu = use_grisu<T>();
+    if (const_check(FMT_DEPRECATED_PERCENT) && fspecs.percent) value *= 100;
+    int exp = format_float(promote_float(value), precision, fspecs, buffer);
+    if (const_check(FMT_DEPRECATED_PERCENT) && fspecs.percent) {
+      buffer.push_back('%');
+      --exp;  // Adjust decimal place position.
+    }
+    fspecs.precision = precision;
+    char_type point = fspecs.locale ? decimal_point<char_type>(locale_)
+                                    : static_cast<char_type>('.');
+    write_padded(specs, float_writer<char_type>(buffer.data(),
+                                                static_cast<int>(buffer.size()),
+                                                exp, fspecs, point));
+  }
+
+  void write(char value) {
+    auto&& it = reserve(1);
+    *it++ = value;
+  }
+
+  template <typename Char, FMT_ENABLE_IF(std::is_same<Char, char_type>::value)>
+  void write(Char value) {
+    auto&& it = reserve(1);
+    *it++ = value;
+  }
+
+  void write(string_view value) {
+    auto&& it = reserve(value.size());
+    it = copy_str<char_type>(value.begin(), value.end(), it);
+  }
+  void write(wstring_view value) {
+    static_assert(std::is_same<char_type, wchar_t>::value, "");
+    auto&& it = reserve(value.size());
+    it = std::copy(value.begin(), value.end(), it);
+  }
+
+  template <typename Char>
+  void write(const Char* s, std::size_t size, const format_specs& specs) {
+    write_padded(specs, str_writer<Char>{s, size});
+  }
+
+  template <typename Char>
+  void write(basic_string_view<Char> s, const format_specs& specs = {}) {
+    const Char* data = s.data();
+    std::size_t size = s.size();
+    if (specs.precision >= 0 && to_unsigned(specs.precision) < size)
+      size = code_point_index(s, to_unsigned(specs.precision));
+    write(data, size, specs);
+  }
+
+  void write_bytes(string_view bytes, const format_specs& specs) {
+    write_padded(specs, bytes_writer{bytes});
+  }
+
+  template <typename UIntPtr>
+  void write_pointer(UIntPtr value, const format_specs* specs) {
+    int num_digits = count_digits<4>(value);
+    auto pw = pointer_writer<UIntPtr>{value, num_digits};
+    if (!specs) return pw(reserve(to_unsigned(num_digits) + 2));
+    format_specs specs_copy = *specs;
+    if (specs_copy.align == align::none) specs_copy.align = align::right;
+    write_padded(specs_copy, pw);
+  }
+};
+
+using writer = basic_writer<buffer_range<char>>;
+
+template <typename T> struct is_integral : std::is_integral<T> {};
+template <> struct is_integral<int128_t> : std::true_type {};
+template <> struct is_integral<uint128_t> : std::true_type {};
+
+template <typename Range, typename ErrorHandler = internal::error_handler>
+class arg_formatter_base {
+ public:
+  using char_type = typename Range::value_type;
+  using iterator = typename Range::iterator;
+  using format_specs = basic_format_specs<char_type>;
+
+ private:
+  using writer_type = basic_writer<Range>;
+  writer_type writer_;
+  format_specs* specs_;
+
+  struct char_writer {
+    char_type value;
+
+    size_t size() const { return 1; }
+    size_t width() const { return 1; }
+
+    template <typename It> void operator()(It&& it) const { *it++ = value; }
+  };
+
+  void write_char(char_type value) {
+    if (specs_)
+      writer_.write_padded(*specs_, char_writer{value});
+    else
+      writer_.write(value);
+  }
+
+  void write_pointer(const void* p) {
+    writer_.write_pointer(internal::to_uintptr(p), specs_);
+  }
+
+ protected:
+  writer_type& writer() { return writer_; }
+  FMT_DEPRECATED format_specs* spec() { return specs_; }
+  format_specs* specs() { return specs_; }
+  iterator out() { return writer_.out(); }
+
+  void write(bool value) {
+    string_view sv(value ? "true" : "false");
+    specs_ ? writer_.write(sv, *specs_) : writer_.write(sv);
+  }
+
+  void write(const char_type* value) {
+    if (!value) {
+      FMT_THROW(format_error("string pointer is null"));
+    } else {
+      auto length = std::char_traits<char_type>::length(value);
+      basic_string_view<char_type> sv(value, length);
+      specs_ ? writer_.write(sv, *specs_) : writer_.write(sv);
+    }
+  }
+
+ public:
+  arg_formatter_base(Range r, format_specs* s, locale_ref loc)
+      : writer_(r, loc), specs_(s) {}
+
+  iterator operator()(monostate) {
+    FMT_ASSERT(false, "invalid argument type");
+    return out();
+  }
+
+  template <typename T, FMT_ENABLE_IF(is_integral<T>::value)>
+  iterator operator()(T value) {
+    if (specs_)
+      writer_.write_int(value, *specs_);
+    else
+      writer_.write(value);
+    return out();
+  }
+
+  iterator operator()(char_type value) {
+    internal::handle_char_specs(
+        specs_, char_spec_handler(*this, static_cast<char_type>(value)));
+    return out();
+  }
+
+  iterator operator()(bool value) {
+    if (specs_ && specs_->type) return (*this)(value ? 1 : 0);
+    write(value != 0);
+    return out();
+  }
+
+  template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
+  iterator operator()(T value) {
+    if (const_check(is_supported_floating_point(value)))
+      writer_.write(value, specs_ ? *specs_ : format_specs());
+    else
+      FMT_ASSERT(false, "unsupported float argument type");
+    return out();
+  }
+
+  struct char_spec_handler : ErrorHandler {
+    arg_formatter_base& formatter;
+    char_type value;
+
+    char_spec_handler(arg_formatter_base& f, char_type val)
+        : formatter(f), value(val) {}
+
+    void on_int() {
+      if (formatter.specs_)
+        formatter.writer_.write_int(value, *formatter.specs_);
+      else
+        formatter.writer_.write(value);
+    }
+    void on_char() { formatter.write_char(value); }
+  };
+
+  struct cstring_spec_handler : internal::error_handler {
+    arg_formatter_base& formatter;
+    const char_type* value;
+
+    cstring_spec_handler(arg_formatter_base& f, const char_type* val)
+        : formatter(f), value(val) {}
+
+    void on_string() { formatter.write(value); }
+    void on_pointer() { formatter.write_pointer(value); }
+  };
+
+  iterator operator()(const char_type* value) {
+    if (!specs_) return write(value), out();
+    internal::handle_cstring_type_spec(specs_->type,
+                                       cstring_spec_handler(*this, value));
+    return out();
+  }
+
+  iterator operator()(basic_string_view<char_type> value) {
+    if (specs_) {
+      internal::check_string_type_spec(specs_->type, internal::error_handler());
+      writer_.write(value, *specs_);
+    } else {
+      writer_.write(value);
+    }
+    return out();
+  }
+
+  iterator operator()(const void* value) {
+    if (specs_)
+      check_pointer_type_spec(specs_->type, internal::error_handler());
+    write_pointer(value);
+    return out();
+  }
+};
+
+template <typename Char> FMT_CONSTEXPR bool is_name_start(Char c) {
+  return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c;
+}
+
+// Parses the range [begin, end) as an unsigned integer. This function assumes
+// that the range is non-empty and the first character is a digit.
+template <typename Char, typename ErrorHandler>
+FMT_CONSTEXPR int parse_nonnegative_int(const Char*& begin, const Char* end,
+                                        ErrorHandler&& eh) {
+  FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', "");
+  unsigned value = 0;
+  // Convert to unsigned to prevent a warning.
+  constexpr unsigned max_int = max_value<int>();
+  unsigned big = max_int / 10;
+  do {
+    // Check for overflow.
+    if (value > big) {
+      value = max_int + 1;
+      break;
+    }
+    value = value * 10 + unsigned(*begin - '0');
+    ++begin;
+  } while (begin != end && '0' <= *begin && *begin <= '9');
+  if (value > max_int) eh.on_error("number is too big");
+  return static_cast<int>(value);
+}
+
+template <typename Context> class custom_formatter {
+ private:
+  using char_type = typename Context::char_type;
+
+  basic_format_parse_context<char_type>& parse_ctx_;
+  Context& ctx_;
+
+ public:
+  explicit custom_formatter(basic_format_parse_context<char_type>& parse_ctx,
+                            Context& ctx)
+      : parse_ctx_(parse_ctx), ctx_(ctx) {}
+
+  bool operator()(typename basic_format_arg<Context>::handle h) const {
+    h.format(parse_ctx_, ctx_);
+    return true;
+  }
+
+  template <typename T> bool operator()(T) const { return false; }
+};
+
+template <typename T>
+using is_integer =
+    bool_constant<is_integral<T>::value && !std::is_same<T, bool>::value &&
+                  !std::is_same<T, char>::value &&
+                  !std::is_same<T, wchar_t>::value>;
+
+template <typename ErrorHandler> class width_checker {
+ public:
+  explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {}
+
+  template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
+  FMT_CONSTEXPR unsigned long long operator()(T value) {
+    if (is_negative(value)) handler_.on_error("negative width");
+    return static_cast<unsigned long long>(value);
+  }
+
+  template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
+  FMT_CONSTEXPR unsigned long long operator()(T) {
+    handler_.on_error("width is not integer");
+    return 0;
+  }
+
+ private:
+  ErrorHandler& handler_;
+};
+
+template <typename ErrorHandler> class precision_checker {
+ public:
+  explicit FMT_CONSTEXPR precision_checker(ErrorHandler& eh) : handler_(eh) {}
+
+  template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
+  FMT_CONSTEXPR unsigned long long operator()(T value) {
+    if (is_negative(value)) handler_.on_error("negative precision");
+    return static_cast<unsigned long long>(value);
+  }
+
+  template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
+  FMT_CONSTEXPR unsigned long long operator()(T) {
+    handler_.on_error("precision is not integer");
+    return 0;
+  }
+
+ private:
+  ErrorHandler& handler_;
+};
+
+// A format specifier handler that sets fields in basic_format_specs.
+template <typename Char> class specs_setter {
+ public:
+  explicit FMT_CONSTEXPR specs_setter(basic_format_specs<Char>& specs)
+      : specs_(specs) {}
+
+  FMT_CONSTEXPR specs_setter(const specs_setter& other)
+      : specs_(other.specs_) {}
+
+  FMT_CONSTEXPR void on_align(align_t align) { specs_.align = align; }
+  FMT_CONSTEXPR void on_fill(basic_string_view<Char> fill) {
+    specs_.fill = fill;
+  }
+  FMT_CONSTEXPR void on_plus() { specs_.sign = sign::plus; }
+  FMT_CONSTEXPR void on_minus() { specs_.sign = sign::minus; }
+  FMT_CONSTEXPR void on_space() { specs_.sign = sign::space; }
+  FMT_CONSTEXPR void on_hash() { specs_.alt = true; }
+
+  FMT_CONSTEXPR void on_zero() {
+    specs_.align = align::numeric;
+    specs_.fill[0] = Char('0');
+  }
+
+  FMT_CONSTEXPR void on_width(int width) { specs_.width = width; }
+  FMT_CONSTEXPR void on_precision(int precision) {
+    specs_.precision = precision;
+  }
+  FMT_CONSTEXPR void end_precision() {}
+
+  FMT_CONSTEXPR void on_type(Char type) {
+    specs_.type = static_cast<char>(type);
+  }
+
+ protected:
+  basic_format_specs<Char>& specs_;
+};
+
+template <typename ErrorHandler> class numeric_specs_checker {
+ public:
+  FMT_CONSTEXPR numeric_specs_checker(ErrorHandler& eh, internal::type arg_type)
+      : error_handler_(eh), arg_type_(arg_type) {}
+
+  FMT_CONSTEXPR void require_numeric_argument() {
+    if (!is_arithmetic_type(arg_type_))
+      error_handler_.on_error("format specifier requires numeric argument");
+  }
+
+  FMT_CONSTEXPR void check_sign() {
+    require_numeric_argument();
+    if (is_integral_type(arg_type_) && arg_type_ != type::int_type &&
+        arg_type_ != type::long_long_type && arg_type_ != type::char_type) {
+      error_handler_.on_error("format specifier requires signed argument");
+    }
+  }
+
+  FMT_CONSTEXPR void check_precision() {
+    if (is_integral_type(arg_type_) || arg_type_ == type::pointer_type)
+      error_handler_.on_error("precision not allowed for this argument type");
+  }
+
+ private:
+  ErrorHandler& error_handler_;
+  internal::type arg_type_;
+};
+
+// A format specifier handler that checks if specifiers are consistent with the
+// argument type.
+template <typename Handler> class specs_checker : public Handler {
+ public:
+  FMT_CONSTEXPR specs_checker(const Handler& handler, internal::type arg_type)
+      : Handler(handler), checker_(*this, arg_type) {}
+
+  FMT_CONSTEXPR specs_checker(const specs_checker& other)
+      : Handler(other), checker_(*this, other.arg_type_) {}
+
+  FMT_CONSTEXPR void on_align(align_t align) {
+    if (align == align::numeric) checker_.require_numeric_argument();
+    Handler::on_align(align);
+  }
+
+  FMT_CONSTEXPR void on_plus() {
+    checker_.check_sign();
+    Handler::on_plus();
+  }
+
+  FMT_CONSTEXPR void on_minus() {
+    checker_.check_sign();
+    Handler::on_minus();
+  }
+
+  FMT_CONSTEXPR void on_space() {
+    checker_.check_sign();
+    Handler::on_space();
+  }
+
+  FMT_CONSTEXPR void on_hash() {
+    checker_.require_numeric_argument();
+    Handler::on_hash();
+  }
+
+  FMT_CONSTEXPR void on_zero() {
+    checker_.require_numeric_argument();
+    Handler::on_zero();
+  }
+
+  FMT_CONSTEXPR void end_precision() { checker_.check_precision(); }
+
+ private:
+  numeric_specs_checker<Handler> checker_;
+};
+
+template <template <typename> class Handler, typename FormatArg,
+          typename ErrorHandler>
+FMT_CONSTEXPR int get_dynamic_spec(FormatArg arg, ErrorHandler eh) {
+  unsigned long long value = visit_format_arg(Handler<ErrorHandler>(eh), arg);
+  if (value > to_unsigned(max_value<int>())) eh.on_error("number is too big");
+  return static_cast<int>(value);
+}
+
+struct auto_id {};
+
+template <typename Context>
+FMT_CONSTEXPR typename Context::format_arg get_arg(Context& ctx, int id) {
+  auto arg = ctx.arg(id);
+  if (!arg) ctx.on_error("argument index out of range");
+  return arg;
+}
+
+// The standard format specifier handler with checking.
+template <typename ParseContext, typename Context>
+class specs_handler : public specs_setter<typename Context::char_type> {
+ public:
+  using char_type = typename Context::char_type;
+
+  FMT_CONSTEXPR specs_handler(basic_format_specs<char_type>& specs,
+                              ParseContext& parse_ctx, Context& ctx)
+      : specs_setter<char_type>(specs),
+        parse_context_(parse_ctx),
+        context_(ctx) {}
+
+  template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
+    this->specs_.width = get_dynamic_spec<width_checker>(
+        get_arg(arg_id), context_.error_handler());
+  }
+
+  template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
+    this->specs_.precision = get_dynamic_spec<precision_checker>(
+        get_arg(arg_id), context_.error_handler());
+  }
+
+  void on_error(const char* message) { context_.on_error(message); }
+
+ private:
+  // This is only needed for compatibility with gcc 4.4.
+  using format_arg = typename Context::format_arg;
+
+  FMT_CONSTEXPR format_arg get_arg(auto_id) {
+    return internal::get_arg(context_, parse_context_.next_arg_id());
+  }
+
+  FMT_CONSTEXPR format_arg get_arg(int arg_id) {
+    parse_context_.check_arg_id(arg_id);
+    return internal::get_arg(context_, arg_id);
+  }
+
+  FMT_CONSTEXPR format_arg get_arg(basic_string_view<char_type> arg_id) {
+    parse_context_.check_arg_id(arg_id);
+    return context_.arg(arg_id);
+  }
+
+  ParseContext& parse_context_;
+  Context& context_;
+};
+
+enum class arg_id_kind { none, index, name };
+
+// An argument reference.
+template <typename Char> struct arg_ref {
+  FMT_CONSTEXPR arg_ref() : kind(arg_id_kind::none), val() {}
+
+  FMT_CONSTEXPR explicit arg_ref(int index)
+      : kind(arg_id_kind::index), val(index) {}
+  FMT_CONSTEXPR explicit arg_ref(basic_string_view<Char> name)
+      : kind(arg_id_kind::name), val(name) {}
+
+  FMT_CONSTEXPR arg_ref& operator=(int idx) {
+    kind = arg_id_kind::index;
+    val.index = idx;
+    return *this;
+  }
+
+  arg_id_kind kind;
+  union value {
+    FMT_CONSTEXPR value(int id = 0) : index{id} {}
+    FMT_CONSTEXPR value(basic_string_view<Char> n) : name(n) {}
+
+    int index;
+    basic_string_view<Char> name;
+  } val;
+};
+
+// Format specifiers with width and precision resolved at formatting rather
+// than parsing time to allow re-using the same parsed specifiers with
+// different sets of arguments (precompilation of format strings).
+template <typename Char>
+struct dynamic_format_specs : basic_format_specs<Char> {
+  arg_ref<Char> width_ref;
+  arg_ref<Char> precision_ref;
+};
+
+// Format spec handler that saves references to arguments representing dynamic
+// width and precision to be resolved at formatting time.
+template <typename ParseContext>
+class dynamic_specs_handler
+    : public specs_setter<typename ParseContext::char_type> {
+ public:
+  using char_type = typename ParseContext::char_type;
+
+  FMT_CONSTEXPR dynamic_specs_handler(dynamic_format_specs<char_type>& specs,
+                                      ParseContext& ctx)
+      : specs_setter<char_type>(specs), specs_(specs), context_(ctx) {}
+
+  FMT_CONSTEXPR dynamic_specs_handler(const dynamic_specs_handler& other)
+      : specs_setter<char_type>(other),
+        specs_(other.specs_),
+        context_(other.context_) {}
+
+  template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
+    specs_.width_ref = make_arg_ref(arg_id);
+  }
+
+  template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
+    specs_.precision_ref = make_arg_ref(arg_id);
+  }
+
+  FMT_CONSTEXPR void on_error(const char* message) {
+    context_.on_error(message);
+  }
+
+ private:
+  using arg_ref_type = arg_ref<char_type>;
+
+  FMT_CONSTEXPR arg_ref_type make_arg_ref(int arg_id) {
+    context_.check_arg_id(arg_id);
+    return arg_ref_type(arg_id);
+  }
+
+  FMT_CONSTEXPR arg_ref_type make_arg_ref(auto_id) {
+    return arg_ref_type(context_.next_arg_id());
+  }
+
+  FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view<char_type> arg_id) {
+    context_.check_arg_id(arg_id);
+    basic_string_view<char_type> format_str(
+        context_.begin(), to_unsigned(context_.end() - context_.begin()));
+    return arg_ref_type(arg_id);
+  }
+
+  dynamic_format_specs<char_type>& specs_;
+  ParseContext& context_;
+};
+
+template <typename Char, typename IDHandler>
+FMT_CONSTEXPR const Char* parse_arg_id(const Char* begin, const Char* end,
+                                       IDHandler&& handler) {
+  FMT_ASSERT(begin != end, "");
+  Char c = *begin;
+  if (c == '}' || c == ':') {
+    handler();
+    return begin;
+  }
+  if (c >= '0' && c <= '9') {
+    int index = 0;
+    if (c != '0')
+      index = parse_nonnegative_int(begin, end, handler);
+    else
+      ++begin;
+    if (begin == end || (*begin != '}' && *begin != ':'))
+      handler.on_error("invalid format string");
+    else
+      handler(index);
+    return begin;
+  }
+  if (!is_name_start(c)) {
+    handler.on_error("invalid format string");
+    return begin;
+  }
+  auto it = begin;
+  do {
+    ++it;
+  } while (it != end && (is_name_start(c = *it) || ('0' <= c && c <= '9')));
+  handler(basic_string_view<Char>(begin, to_unsigned(it - begin)));
+  return it;
+}
+
+// Adapts SpecHandler to IDHandler API for dynamic width.
+template <typename SpecHandler, typename Char> struct width_adapter {
+  explicit FMT_CONSTEXPR width_adapter(SpecHandler& h) : handler(h) {}
+
+  FMT_CONSTEXPR void operator()() { handler.on_dynamic_width(auto_id()); }
+  FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_width(id); }
+  FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
+    handler.on_dynamic_width(id);
+  }
+
+  FMT_CONSTEXPR void on_error(const char* message) {
+    handler.on_error(message);
+  }
+
+  SpecHandler& handler;
+};
+
+// Adapts SpecHandler to IDHandler API for dynamic precision.
+template <typename SpecHandler, typename Char> struct precision_adapter {
+  explicit FMT_CONSTEXPR precision_adapter(SpecHandler& h) : handler(h) {}
+
+  FMT_CONSTEXPR void operator()() { handler.on_dynamic_precision(auto_id()); }
+  FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_precision(id); }
+  FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
+    handler.on_dynamic_precision(id);
+  }
+
+  FMT_CONSTEXPR void on_error(const char* message) {
+    handler.on_error(message);
+  }
+
+  SpecHandler& handler;
+};
+
+template <typename Char>
+FMT_CONSTEXPR const Char* next_code_point(const Char* begin, const Char* end) {
+  if (const_check(sizeof(Char) != 1) || (*begin & 0x80) == 0) return begin + 1;
+  do {
+    ++begin;
+  } while (begin != end && (*begin & 0xc0) == 0x80);
+  return begin;
+}
+
+// Parses fill and alignment.
+template <typename Char, typename Handler>
+FMT_CONSTEXPR const Char* parse_align(const Char* begin, const Char* end,
+                                      Handler&& handler) {
+  FMT_ASSERT(begin != end, "");
+  auto align = align::none;
+  auto p = next_code_point(begin, end);
+  if (p == end) p = begin;
+  for (;;) {
+    switch (static_cast<char>(*p)) {
+    case '<':
+      align = align::left;
+      break;
+    case '>':
+      align = align::right;
+      break;
+#if FMT_NUMERIC_ALIGN
+    case '=':
+      align = align::numeric;
+      break;
+#endif
+    case '^':
+      align = align::center;
+      break;
+    }
+    if (align != align::none) {
+      if (p != begin) {
+        auto c = *begin;
+        if (c == '{')
+          return handler.on_error("invalid fill character '{'"), begin;
+        handler.on_fill(basic_string_view<Char>(begin, to_unsigned(p - begin)));
+        begin = p + 1;
+      } else
+        ++begin;
+      handler.on_align(align);
+      break;
+    } else if (p == begin) {
+      break;
+    }
+    p = begin;
+  }
+  return begin;
+}
+
+template <typename Char, typename Handler>
+FMT_CONSTEXPR const Char* parse_width(const Char* begin, const Char* end,
+                                      Handler&& handler) {
+  FMT_ASSERT(begin != end, "");
+  if ('0' <= *begin && *begin <= '9') {
+    handler.on_width(parse_nonnegative_int(begin, end, handler));
+  } else if (*begin == '{') {
+    ++begin;
+    if (begin != end)
+      begin = parse_arg_id(begin, end, width_adapter<Handler, Char>(handler));
+    if (begin == end || *begin != '}')
+      return handler.on_error("invalid format string"), begin;
+    ++begin;
+  }
+  return begin;
+}
+
+template <typename Char, typename Handler>
+FMT_CONSTEXPR const Char* parse_precision(const Char* begin, const Char* end,
+                                          Handler&& handler) {
+  ++begin;
+  auto c = begin != end ? *begin : Char();
+  if ('0' <= c && c <= '9') {
+    handler.on_precision(parse_nonnegative_int(begin, end, handler));
+  } else if (c == '{') {
+    ++begin;
+    if (begin != end) {
+      begin =
+          parse_arg_id(begin, end, precision_adapter<Handler, Char>(handler));
+    }
+    if (begin == end || *begin++ != '}')
+      return handler.on_error("invalid format string"), begin;
+  } else {
+    return handler.on_error("missing precision specifier"), begin;
+  }
+  handler.end_precision();
+  return begin;
+}
+
+// Parses standard format specifiers and sends notifications about parsed
+// components to handler.
+template <typename Char, typename SpecHandler>
+FMT_CONSTEXPR const Char* parse_format_specs(const Char* begin, const Char* end,
+                                             SpecHandler&& handler) {
+  if (begin == end || *begin == '}') return begin;
+
+  begin = parse_align(begin, end, handler);
+  if (begin == end) return begin;
+
+  // Parse sign.
+  switch (static_cast<char>(*begin)) {
+  case '+':
+    handler.on_plus();
+    ++begin;
+    break;
+  case '-':
+    handler.on_minus();
+    ++begin;
+    break;
+  case ' ':
+    handler.on_space();
+    ++begin;
+    break;
+  }
+  if (begin == end) return begin;
+
+  if (*begin == '#') {
+    handler.on_hash();
+    if (++begin == end) return begin;
+  }
+
+  // Parse zero flag.
+  if (*begin == '0') {
+    handler.on_zero();
+    if (++begin == end) return begin;
+  }
+
+  begin = parse_width(begin, end, handler);
+  if (begin == end) return begin;
+
+  // Parse precision.
+  if (*begin == '.') {
+    begin = parse_precision(begin, end, handler);
+  }
+
+  // Parse type.
+  if (begin != end && *begin != '}') handler.on_type(*begin++);
+  return begin;
+}
+
+// Return the result via the out param to workaround gcc bug 77539.
+template <bool IS_CONSTEXPR, typename T, typename Ptr = const T*>
+FMT_CONSTEXPR bool find(Ptr first, Ptr last, T value, Ptr& out) {
+  for (out = first; out != last; ++out) {
+    if (*out == value) return true;
+  }
+  return false;
+}
+
+template <>
+inline bool find<false, char>(const char* first, const char* last, char value,
+                              const char*& out) {
+  out = static_cast<const char*>(
+      std::memchr(first, value, internal::to_unsigned(last - first)));
+  return out != nullptr;
+}
+
+template <typename Handler, typename Char> struct id_adapter {
+  FMT_CONSTEXPR void operator()() { handler.on_arg_id(); }
+  FMT_CONSTEXPR void operator()(int id) { handler.on_arg_id(id); }
+  FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
+    handler.on_arg_id(id);
+  }
+  FMT_CONSTEXPR void on_error(const char* message) {
+    handler.on_error(message);
+  }
+  Handler& handler;
+};
+
+template <bool IS_CONSTEXPR, typename Char, typename Handler>
+FMT_CONSTEXPR void parse_format_string(basic_string_view<Char> format_str,
+                                       Handler&& handler) {
+  struct pfs_writer {
+    FMT_CONSTEXPR void operator()(const Char* begin, const Char* end) {
+      if (begin == end) return;
+      for (;;) {
+        const Char* p = nullptr;
+        if (!find<IS_CONSTEXPR>(begin, end, '}', p))
+          return handler_.on_text(begin, end);
+        ++p;
+        if (p == end || *p != '}')
+          return handler_.on_error("unmatched '}' in format string");
+        handler_.on_text(begin, p);
+        begin = p + 1;
+      }
+    }
+    Handler& handler_;
+  } write{handler};
+  auto begin = format_str.data();
+  auto end = begin + format_str.size();
+  while (begin != end) {
+    // Doing two passes with memchr (one for '{' and another for '}') is up to
+    // 2.5x faster than the naive one-pass implementation on big format strings.
+    const Char* p = begin;
+    if (*begin != '{' && !find<IS_CONSTEXPR>(begin + 1, end, '{', p))
+      return write(begin, end);
+    write(begin, p);
+    ++p;
+    if (p == end) return handler.on_error("invalid format string");
+    if (static_cast<char>(*p) == '}') {
+      handler.on_arg_id();
+      handler.on_replacement_field(p);
+    } else if (*p == '{') {
+      handler.on_text(p, p + 1);
+    } else {
+      p = parse_arg_id(p, end, id_adapter<Handler, Char>{handler});
+      Char c = p != end ? *p : Char();
+      if (c == '}') {
+        handler.on_replacement_field(p);
+      } else if (c == ':') {
+        p = handler.on_format_specs(p + 1, end);
+        if (p == end || *p != '}')
+          return handler.on_error("unknown format specifier");
+      } else {
+        return handler.on_error("missing '}' in format string");
+      }
+    }
+    begin = p + 1;
+  }
+}
+
+template <typename T, typename ParseContext>
+FMT_CONSTEXPR const typename ParseContext::char_type* parse_format_specs(
+    ParseContext& ctx) {
+  using char_type = typename ParseContext::char_type;
+  using context = buffer_context<char_type>;
+  using mapped_type =
+      conditional_t<internal::mapped_type_constant<T, context>::value !=
+                        type::custom_type,
+                    decltype(arg_mapper<context>().map(std::declval<T>())), T>;
+  auto f = conditional_t<has_formatter<mapped_type, context>::value,
+                         formatter<mapped_type, char_type>,
+                         internal::fallback_formatter<T, char_type>>();
+  return f.parse(ctx);
+}
+
+template <typename Char, typename ErrorHandler, typename... Args>
+class format_string_checker {
+ public:
+  explicit FMT_CONSTEXPR format_string_checker(
+      basic_string_view<Char> format_str, ErrorHandler eh)
+      : arg_id_(-1),
+        context_(format_str, eh),
+        parse_funcs_{&parse_format_specs<Args, parse_context_type>...} {}
+
+  FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
+
+  FMT_CONSTEXPR void on_arg_id() {
+    arg_id_ = context_.next_arg_id();
+    check_arg_id();
+  }
+  FMT_CONSTEXPR void on_arg_id(int id) {
+    arg_id_ = id;
+    context_.check_arg_id(id);
+    check_arg_id();
+  }
+  FMT_CONSTEXPR void on_arg_id(basic_string_view<Char>) {
+    on_error("compile-time checks don't support named arguments");
+  }
+
+  FMT_CONSTEXPR void on_replacement_field(const Char*) {}
+
+  FMT_CONSTEXPR const Char* on_format_specs(const Char* begin, const Char*) {
+    advance_to(context_, begin);
+    return arg_id_ < num_args ? parse_funcs_[arg_id_](context_) : begin;
+  }
+
+  FMT_CONSTEXPR void on_error(const char* message) {
+    context_.on_error(message);
+  }
+
+ private:
+  using parse_context_type = basic_format_parse_context<Char, ErrorHandler>;
+  enum { num_args = sizeof...(Args) };
+
+  FMT_CONSTEXPR void check_arg_id() {
+    if (arg_id_ >= num_args) context_.on_error("argument index out of range");
+  }
+
+  // Format specifier parsing function.
+  using parse_func = const Char* (*)(parse_context_type&);
+
+  int arg_id_;
+  parse_context_type context_;
+  parse_func parse_funcs_[num_args > 0 ? num_args : 1];
+};
+
+template <typename Char, typename ErrorHandler, typename... Args>
+FMT_CONSTEXPR bool do_check_format_string(basic_string_view<Char> s,
+                                          ErrorHandler eh = ErrorHandler()) {
+  format_string_checker<Char, ErrorHandler, Args...> checker(s, eh);
+  parse_format_string<true>(s, checker);
+  return true;
+}
+
+template <typename... Args, typename S,
+          enable_if_t<(is_compile_string<S>::value), int>>
+void check_format_string(S format_str) {
+  FMT_CONSTEXPR_DECL bool invalid_format = internal::do_check_format_string<
+      typename S::char_type, internal::error_handler,
+      remove_const_t<remove_reference_t<Args>>...>(to_string_view(format_str));
+  (void)invalid_format;
+}
+
+template <template <typename> class Handler, typename Context>
+void handle_dynamic_spec(int& value, arg_ref<typename Context::char_type> ref,
+                         Context& ctx) {
+  switch (ref.kind) {
+  case arg_id_kind::none:
+    break;
+  case arg_id_kind::index:
+    value = internal::get_dynamic_spec<Handler>(ctx.arg(ref.val.index),
+                                                ctx.error_handler());
+    break;
+  case arg_id_kind::name:
+    value = internal::get_dynamic_spec<Handler>(ctx.arg(ref.val.name),
+                                                ctx.error_handler());
+    break;
+  }
+}
+
+using format_func = void (*)(internal::buffer<char>&, int, string_view);
+
+FMT_API void format_error_code(buffer<char>& out, int error_code,
+                               string_view message) FMT_NOEXCEPT;
+
+FMT_API void report_error(format_func func, int error_code,
+                          string_view message) FMT_NOEXCEPT;
+}  // namespace internal
+
+template <typename Range>
+using basic_writer FMT_DEPRECATED_ALIAS = internal::basic_writer<Range>;
+using writer FMT_DEPRECATED_ALIAS = internal::writer;
+using wwriter FMT_DEPRECATED_ALIAS =
+    internal::basic_writer<buffer_range<wchar_t>>;
+
+/** The default argument formatter. */
+template <typename Range>
+class arg_formatter : public internal::arg_formatter_base<Range> {
+ private:
+  using char_type = typename Range::value_type;
+  using base = internal::arg_formatter_base<Range>;
+  using context_type = basic_format_context<typename base::iterator, char_type>;
+
+  context_type& ctx_;
+  basic_format_parse_context<char_type>* parse_ctx_;
+
+ public:
+  using range = Range;
+  using iterator = typename base::iterator;
+  using format_specs = typename base::format_specs;
+
+  /**
+    \rst
+    Constructs an argument formatter object.
+    *ctx* is a reference to the formatting context,
+    *specs* contains format specifier information for standard argument types.
+    \endrst
+   */
+  explicit arg_formatter(
+      context_type& ctx,
+      basic_format_parse_context<char_type>* parse_ctx = nullptr,
+      format_specs* specs = nullptr)
+      : base(Range(ctx.out()), specs, ctx.locale()),
+        ctx_(ctx),
+        parse_ctx_(parse_ctx) {}
+
+  using base::operator();
+
+  /** Formats an argument of a user-defined type. */
+  iterator operator()(typename basic_format_arg<context_type>::handle handle) {
+    handle.format(*parse_ctx_, ctx_);
+    return ctx_.out();
+  }
+};
+
+/**
+ An error returned by an operating system or a language runtime,
+ for example a file opening error.
+*/
+FMT_CLASS_API
+class FMT_API system_error : public std::runtime_error {
+ private:
+  void init(int err_code, string_view format_str, format_args args);
+
+ protected:
+  int error_code_;
+
+  system_error() : std::runtime_error(""), error_code_(0) {}
+
+ public:
+  /**
+   \rst
+   Constructs a :class:`fmt::system_error` object with a description
+   formatted with `fmt::format_system_error`. *message* and additional
+   arguments passed into the constructor are formatted similarly to
+   `fmt::format`.
+
+   **Example**::
+
+     // This throws a system_error with the description
+     //   cannot open file 'madeup': No such file or directory
+     // or similar (system message may vary).
+     const char *filename = "madeup";
+     std::FILE *file = std::fopen(filename, "r");
+     if (!file)
+       throw fmt::system_error(errno, "cannot open file '{}'", filename);
+   \endrst
+  */
+  template <typename... Args>
+  system_error(int error_code, string_view message, const Args&... args)
+      : std::runtime_error("") {
+    init(error_code, message, make_format_args(args...));
+  }
+  system_error(const system_error&) = default;
+  system_error& operator=(const system_error&) = default;
+  system_error(system_error&&) = default;
+  system_error& operator=(system_error&&) = default;
+  ~system_error() FMT_NOEXCEPT FMT_OVERRIDE;
+
+  int error_code() const { return error_code_; }
+};
+
+/**
+  \rst
+  Formats an error returned by an operating system or a language runtime,
+  for example a file opening error, and writes it to *out* in the following
+  form:
+
+  .. parsed-literal::
+     *<message>*: *<system-message>*
+
+  where *<message>* is the passed message and *<system-message>* is
+  the system message corresponding to the error code.
+  *error_code* is a system error code as given by ``errno``.
+  If *error_code* is not a valid error code such as -1, the system message
+  may look like "Unknown error -1" and is platform-dependent.
+  \endrst
+ */
+FMT_API void format_system_error(internal::buffer<char>& out, int error_code,
+                                 string_view message) FMT_NOEXCEPT;
+
+// Reports a system error without throwing an exception.
+// Can be used to report errors from destructors.
+FMT_API void report_system_error(int error_code,
+                                 string_view message) FMT_NOEXCEPT;
+
+/** Fast integer formatter. */
+class format_int {
+ private:
+  // Buffer should be large enough to hold all digits (digits10 + 1),
+  // a sign and a null character.
+  enum { buffer_size = std::numeric_limits<unsigned long long>::digits10 + 3 };
+  mutable char buffer_[buffer_size];
+  char* str_;
+
+  // Formats value in reverse and returns a pointer to the beginning.
+  char* format_decimal(unsigned long long value) {
+    char* ptr = buffer_ + (buffer_size - 1);  // Parens to workaround MSVC bug.
+    while (value >= 100) {
+      // Integer division is slow so do it for a group of two digits instead
+      // of for every digit. The idea comes from the talk by Alexandrescu
+      // "Three Optimization Tips for C++". See speed-test for a comparison.
+      auto index = static_cast<unsigned>((value % 100) * 2);
+      value /= 100;
+      *--ptr = internal::data::digits[index + 1];
+      *--ptr = internal::data::digits[index];
+    }
+    if (value < 10) {
+      *--ptr = static_cast<char>('0' + value);
+      return ptr;
+    }
+    auto index = static_cast<unsigned>(value * 2);
+    *--ptr = internal::data::digits[index + 1];
+    *--ptr = internal::data::digits[index];
+    return ptr;
+  }
+
+  void format_signed(long long value) {
+    auto abs_value = static_cast<unsigned long long>(value);
+    bool negative = value < 0;
+    if (negative) abs_value = 0 - abs_value;
+    str_ = format_decimal(abs_value);
+    if (negative) *--str_ = '-';
+  }
+
+ public:
+  explicit format_int(int value) { format_signed(value); }
+  explicit format_int(long value) { format_signed(value); }
+  explicit format_int(long long value) { format_signed(value); }
+  explicit format_int(unsigned value) : str_(format_decimal(value)) {}
+  explicit format_int(unsigned long value) : str_(format_decimal(value)) {}
+  explicit format_int(unsigned long long value) : str_(format_decimal(value)) {}
+
+  /** Returns the number of characters written to the output buffer. */
+  std::size_t size() const {
+    return internal::to_unsigned(buffer_ - str_ + buffer_size - 1);
+  }
+
+  /**
+    Returns a pointer to the output buffer content. No terminating null
+    character is appended.
+   */
+  const char* data() const { return str_; }
+
+  /**
+    Returns a pointer to the output buffer content with terminating null
+    character appended.
+   */
+  const char* c_str() const {
+    buffer_[buffer_size - 1] = '\0';
+    return str_;
+  }
+
+  /**
+    \rst
+    Returns the content of the output buffer as an ``std::string``.
+    \endrst
+   */
+  std::string str() const { return std::string(str_, size()); }
+};
+
+// A formatter specialization for the core types corresponding to internal::type
+// constants.
+template <typename T, typename Char>
+struct formatter<T, Char,
+                 enable_if_t<internal::type_constant<T, Char>::value !=
+                             internal::type::custom_type>> {
+  FMT_CONSTEXPR formatter() = default;
+
+  // Parses format specifiers stopping either at the end of the range or at the
+  // terminating '}'.
+  template <typename ParseContext>
+  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+    using handler_type = internal::dynamic_specs_handler<ParseContext>;
+    auto type = internal::type_constant<T, Char>::value;
+    internal::specs_checker<handler_type> handler(handler_type(specs_, ctx),
+                                                  type);
+    auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
+    auto eh = ctx.error_handler();
+    switch (type) {
+    case internal::type::none_type:
+    case internal::type::named_arg_type:
+      FMT_ASSERT(false, "invalid argument type");
+      break;
+    case internal::type::int_type:
+    case internal::type::uint_type:
+    case internal::type::long_long_type:
+    case internal::type::ulong_long_type:
+    case internal::type::int128_type:
+    case internal::type::uint128_type:
+    case internal::type::bool_type:
+      handle_int_type_spec(specs_.type,
+                           internal::int_type_checker<decltype(eh)>(eh));
+      break;
+    case internal::type::char_type:
+      handle_char_specs(
+          &specs_, internal::char_specs_checker<decltype(eh)>(specs_.type, eh));
+      break;
+    case internal::type::float_type:
+      if (internal::const_check(FMT_USE_FLOAT)) {
+        internal::parse_float_type_spec(specs_, eh);
+      } else {
+        FMT_ASSERT(false, "float support disabled");
+      }
+      break;
+    case internal::type::double_type:
+      if (internal::const_check(FMT_USE_DOUBLE)) {
+        internal::parse_float_type_spec(specs_, eh);
+      } else {
+        FMT_ASSERT(false, "double support disabled");
+      }
+      break;
+    case internal::type::long_double_type:
+      if (internal::const_check(FMT_USE_LONG_DOUBLE)) {
+        internal::parse_float_type_spec(specs_, eh);
+      } else {
+        FMT_ASSERT(false, "long double support disabled");
+      }
+      break;
+    case internal::type::cstring_type:
+      internal::handle_cstring_type_spec(
+          specs_.type, internal::cstring_type_checker<decltype(eh)>(eh));
+      break;
+    case internal::type::string_type:
+      internal::check_string_type_spec(specs_.type, eh);
+      break;
+    case internal::type::pointer_type:
+      internal::check_pointer_type_spec(specs_.type, eh);
+      break;
+    case internal::type::custom_type:
+      // Custom format specifiers should be checked in parse functions of
+      // formatter specializations.
+      break;
+    }
+    return it;
+  }
+
+  template <typename FormatContext>
+  auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) {
+    internal::handle_dynamic_spec<internal::width_checker>(
+        specs_.width, specs_.width_ref, ctx);
+    internal::handle_dynamic_spec<internal::precision_checker>(
+        specs_.precision, specs_.precision_ref, ctx);
+    using range_type =
+        internal::output_range<typename FormatContext::iterator,
+                               typename FormatContext::char_type>;
+    return visit_format_arg(arg_formatter<range_type>(ctx, nullptr, &specs_),
+                            internal::make_arg<FormatContext>(val));
+  }
+
+ private:
+  internal::dynamic_format_specs<Char> specs_;
+};
+
+#define FMT_FORMAT_AS(Type, Base)                                             \
+  template <typename Char>                                                    \
+  struct formatter<Type, Char> : formatter<Base, Char> {                      \
+    template <typename FormatContext>                                         \
+    auto format(Type const& val, FormatContext& ctx) -> decltype(ctx.out()) { \
+      return formatter<Base, Char>::format(val, ctx);                         \
+    }                                                                         \
+  }
+
+FMT_FORMAT_AS(signed char, int);
+FMT_FORMAT_AS(unsigned char, unsigned);
+FMT_FORMAT_AS(short, int);
+FMT_FORMAT_AS(unsigned short, unsigned);
+FMT_FORMAT_AS(long, long long);
+FMT_FORMAT_AS(unsigned long, unsigned long long);
+FMT_FORMAT_AS(Char*, const Char*);
+FMT_FORMAT_AS(std::basic_string<Char>, basic_string_view<Char>);
+FMT_FORMAT_AS(std::nullptr_t, const void*);
+FMT_FORMAT_AS(internal::std_string_view<Char>, basic_string_view<Char>);
+
+template <typename Char>
+struct formatter<void*, Char> : formatter<const void*, Char> {
+  template <typename FormatContext>
+  auto format(void* val, FormatContext& ctx) -> decltype(ctx.out()) {
+    return formatter<const void*, Char>::format(val, ctx);
+  }
+};
+
+template <typename Char, size_t N>
+struct formatter<Char[N], Char> : formatter<basic_string_view<Char>, Char> {
+  template <typename FormatContext>
+  auto format(const Char* val, FormatContext& ctx) -> decltype(ctx.out()) {
+    return formatter<basic_string_view<Char>, Char>::format(val, ctx);
+  }
+};
+
+// A formatter for types known only at run time such as variant alternatives.
+//
+// Usage:
+//   using variant = std::variant<int, std::string>;
+//   template <>
+//   struct formatter<variant>: dynamic_formatter<> {
+//     void format(buffer &buf, const variant &v, context &ctx) {
+//       visit([&](const auto &val) { format(buf, val, ctx); }, v);
+//     }
+//   };
+template <typename Char = char> class dynamic_formatter {
+ private:
+  struct null_handler : internal::error_handler {
+    void on_align(align_t) {}
+    void on_plus() {}
+    void on_minus() {}
+    void on_space() {}
+    void on_hash() {}
+  };
+
+ public:
+  template <typename ParseContext>
+  auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+    format_str_ = ctx.begin();
+    // Checks are deferred to formatting time when the argument type is known.
+    internal::dynamic_specs_handler<ParseContext> handler(specs_, ctx);
+    return parse_format_specs(ctx.begin(), ctx.end(), handler);
+  }
+
+  template <typename T, typename FormatContext>
+  auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) {
+    handle_specs(ctx);
+    internal::specs_checker<null_handler> checker(
+        null_handler(),
+        internal::mapped_type_constant<T, FormatContext>::value);
+    checker.on_align(specs_.align);
+    switch (specs_.sign) {
+    case sign::none:
+      break;
+    case sign::plus:
+      checker.on_plus();
+      break;
+    case sign::minus:
+      checker.on_minus();
+      break;
+    case sign::space:
+      checker.on_space();
+      break;
+    }
+    if (specs_.alt) checker.on_hash();
+    if (specs_.precision >= 0) checker.end_precision();
+    using range = internal::output_range<typename FormatContext::iterator,
+                                         typename FormatContext::char_type>;
+    visit_format_arg(arg_formatter<range>(ctx, nullptr, &specs_),
+                     internal::make_arg<FormatContext>(val));
+    return ctx.out();
+  }
+
+ private:
+  template <typename Context> void handle_specs(Context& ctx) {
+    internal::handle_dynamic_spec<internal::width_checker>(
+        specs_.width, specs_.width_ref, ctx);
+    internal::handle_dynamic_spec<internal::precision_checker>(
+        specs_.precision, specs_.precision_ref, ctx);
+  }
+
+  internal::dynamic_format_specs<Char> specs_;
+  const Char* format_str_;
+};
+
+template <typename Range, typename Char>
+typename basic_format_context<Range, Char>::format_arg
+basic_format_context<Range, Char>::arg(basic_string_view<char_type> name) {
+  map_.init(args_);
+  format_arg arg = map_.find(name);
+  if (arg.type() == internal::type::none_type)
+    this->on_error("argument not found");
+  return arg;
+}
+
+template <typename Char, typename ErrorHandler>
+FMT_CONSTEXPR void advance_to(
+    basic_format_parse_context<Char, ErrorHandler>& ctx, const Char* p) {
+  ctx.advance_to(ctx.begin() + (p - &*ctx.begin()));
+}
+
+template <typename ArgFormatter, typename Char, typename Context>
+struct format_handler : internal::error_handler {
+  using range = typename ArgFormatter::range;
+
+  format_handler(range r, basic_string_view<Char> str,
+                 basic_format_args<Context> format_args,
+                 internal::locale_ref loc)
+      : parse_context(str), context(r.begin(), format_args, loc) {}
+
+  void on_text(const Char* begin, const Char* end) {
+    auto size = internal::to_unsigned(end - begin);
+    auto out = context.out();
+    auto&& it = internal::reserve(out, size);
+    it = std::copy_n(begin, size, it);
+    context.advance_to(out);
+  }
+
+  void get_arg(int id) { arg = internal::get_arg(context, id); }
+
+  void on_arg_id() { get_arg(parse_context.next_arg_id()); }
+  void on_arg_id(int id) {
+    parse_context.check_arg_id(id);
+    get_arg(id);
+  }
+  void on_arg_id(basic_string_view<Char> id) { arg = context.arg(id); }
+
+  void on_replacement_field(const Char* p) {
+    advance_to(parse_context, p);
+    context.advance_to(
+        visit_format_arg(ArgFormatter(context, &parse_context), arg));
+  }
+
+  const Char* on_format_specs(const Char* begin, const Char* end) {
+    advance_to(parse_context, begin);
+    internal::custom_formatter<Context> f(parse_context, context);
+    if (visit_format_arg(f, arg)) return parse_context.begin();
+    basic_format_specs<Char> specs;
+    using internal::specs_handler;
+    using parse_context_t = basic_format_parse_context<Char>;
+    internal::specs_checker<specs_handler<parse_context_t, Context>> handler(
+        specs_handler<parse_context_t, Context>(specs, parse_context, context),
+        arg.type());
+    begin = parse_format_specs(begin, end, handler);
+    if (begin == end || *begin != '}') on_error("missing '}' in format string");
+    advance_to(parse_context, begin);
+    context.advance_to(
+        visit_format_arg(ArgFormatter(context, &parse_context, &specs), arg));
+    return begin;
+  }
+
+  basic_format_parse_context<Char> parse_context;
+  Context context;
+  basic_format_arg<Context> arg;
+};
+
+/** Formats arguments and writes the output to the range. */
+template <typename ArgFormatter, typename Char, typename Context>
+typename Context::iterator vformat_to(
+    typename ArgFormatter::range out, basic_string_view<Char> format_str,
+    basic_format_args<Context> args,
+    internal::locale_ref loc = internal::locale_ref()) {
+  format_handler<ArgFormatter, Char, Context> h(out, format_str, args, loc);
+  internal::parse_format_string<false>(format_str, h);
+  return h.context.out();
+}
+
+// Casts ``p`` to ``const void*`` for pointer formatting.
+// Example:
+//   auto s = format("{}", ptr(p));
+template <typename T> inline const void* ptr(const T* p) { return p; }
+template <typename T> inline const void* ptr(const std::unique_ptr<T>& p) {
+  return p.get();
+}
+template <typename T> inline const void* ptr(const std::shared_ptr<T>& p) {
+  return p.get();
+}
+
+class bytes {
+ private:
+  string_view data_;
+  friend struct formatter<bytes>;
+
+ public:
+  explicit bytes(string_view data) : data_(data) {}
+};
+
+template <> struct formatter<bytes> {
+  template <typename ParseContext>
+  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+    using handler_type = internal::dynamic_specs_handler<ParseContext>;
+    internal::specs_checker<handler_type> handler(handler_type(specs_, ctx),
+                                                  internal::type::string_type);
+    auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
+    internal::check_string_type_spec(specs_.type, ctx.error_handler());
+    return it;
+  }
+
+  template <typename FormatContext>
+  auto format(bytes b, FormatContext& ctx) -> decltype(ctx.out()) {
+    internal::handle_dynamic_spec<internal::width_checker>(
+        specs_.width, specs_.width_ref, ctx);
+    internal::handle_dynamic_spec<internal::precision_checker>(
+        specs_.precision, specs_.precision_ref, ctx);
+    using range_type =
+        internal::output_range<typename FormatContext::iterator, char>;
+    internal::basic_writer<range_type> writer(range_type(ctx.out()));
+    writer.write_bytes(b.data_, specs_);
+    return writer.out();
+  }
+
+ private:
+  internal::dynamic_format_specs<char> specs_;
+};
+
+template <typename It, typename Char> struct arg_join : internal::view {
+  It begin;
+  It end;
+  basic_string_view<Char> sep;
+
+  arg_join(It b, It e, basic_string_view<Char> s) : begin(b), end(e), sep(s) {}
+};
+
+template <typename It, typename Char>
+struct formatter<arg_join<It, Char>, Char>
+    : formatter<typename std::iterator_traits<It>::value_type, Char> {
+  template <typename FormatContext>
+  auto format(const arg_join<It, Char>& value, FormatContext& ctx)
+      -> decltype(ctx.out()) {
+    using base = formatter<typename std::iterator_traits<It>::value_type, Char>;
+    auto it = value.begin;
+    auto out = ctx.out();
+    if (it != value.end) {
+      out = base::format(*it++, ctx);
+      while (it != value.end) {
+        out = std::copy(value.sep.begin(), value.sep.end(), out);
+        ctx.advance_to(out);
+        out = base::format(*it++, ctx);
+      }
+    }
+    return out;
+  }
+};
+
+/**
+  Returns an object that formats the iterator range `[begin, end)` with elements
+  separated by `sep`.
+ */
+template <typename It>
+arg_join<It, char> join(It begin, It end, string_view sep) {
+  return {begin, end, sep};
+}
+
+template <typename It>
+arg_join<It, wchar_t> join(It begin, It end, wstring_view sep) {
+  return {begin, end, sep};
+}
+
+/**
+  \rst
+  Returns an object that formats `range` with elements separated by `sep`.
+
+  **Example**::
+
+    std::vector<int> v = {1, 2, 3};
+    fmt::print("{}", fmt::join(v, ", "));
+    // Output: "1, 2, 3"
+
+  ``fmt::join`` applies passed format specifiers to the range elements::
+
+    fmt::print("{:02}", fmt::join(v, ", "));
+    // Output: "01, 02, 03"
+  \endrst
+ */
+template <typename Range>
+arg_join<internal::iterator_t<const Range>, char> join(const Range& range,
+                                                       string_view sep) {
+  return join(std::begin(range), std::end(range), sep);
+}
+
+template <typename Range>
+arg_join<internal::iterator_t<const Range>, wchar_t> join(const Range& range,
+                                                          wstring_view sep) {
+  return join(std::begin(range), std::end(range), sep);
+}
+
+/**
+  \rst
+  Converts *value* to ``std::string`` using the default format for type *T*.
+
+  **Example**::
+
+    #include <fmt/format.h>
+
+    std::string answer = fmt::to_string(42);
+  \endrst
+ */
+template <typename T> inline std::string to_string(const T& value) {
+  return format("{}", value);
+}
+
+/**
+  Converts *value* to ``std::wstring`` using the default format for type *T*.
+ */
+template <typename T> inline std::wstring to_wstring(const T& value) {
+  return format(L"{}", value);
+}
+
+template <typename Char, std::size_t SIZE>
+std::basic_string<Char> to_string(const basic_memory_buffer<Char, SIZE>& buf) {
+  return std::basic_string<Char>(buf.data(), buf.size());
+}
+
+template <typename Char>
+typename buffer_context<Char>::iterator internal::vformat_to(
+    internal::buffer<Char>& buf, basic_string_view<Char> format_str,
+    basic_format_args<buffer_context<type_identity_t<Char>>> args) {
+  using range = buffer_range<Char>;
+  return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str),
+                                          args);
+}
+
+template <typename S, typename Char = char_t<S>,
+          FMT_ENABLE_IF(internal::is_string<S>::value)>
+inline typename buffer_context<Char>::iterator vformat_to(
+    internal::buffer<Char>& buf, const S& format_str,
+    basic_format_args<buffer_context<type_identity_t<Char>>> args) {
+  return internal::vformat_to(buf, to_string_view(format_str), args);
+}
+
+template <typename S, typename... Args, std::size_t SIZE = inline_buffer_size,
+          typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
+inline typename buffer_context<Char>::iterator format_to(
+    basic_memory_buffer<Char, SIZE>& buf, const S& format_str, Args&&... args) {
+  internal::check_format_string<Args...>(format_str);
+  using context = buffer_context<Char>;
+  return internal::vformat_to(buf, to_string_view(format_str),
+                              make_format_args<context>(args...));
+}
+
+template <typename OutputIt, typename Char = char>
+using format_context_t = basic_format_context<OutputIt, Char>;
+
+template <typename OutputIt, typename Char = char>
+using format_args_t = basic_format_args<format_context_t<OutputIt, Char>>;
+
+template <typename S, typename OutputIt, typename... Args,
+          FMT_ENABLE_IF(
+              internal::is_output_iterator<OutputIt>::value &&
+              !internal::is_contiguous_back_insert_iterator<OutputIt>::value)>
+inline OutputIt vformat_to(
+    OutputIt out, const S& format_str,
+    format_args_t<type_identity_t<OutputIt>, char_t<S>> args) {
+  using range = internal::output_range<OutputIt, char_t<S>>;
+  return vformat_to<arg_formatter<range>>(range(out),
+                                          to_string_view(format_str), args);
+}
+
+/**
+ \rst
+ Formats arguments, writes the result to the output iterator ``out`` and returns
+ the iterator past the end of the output range.
+
+ **Example**::
+
+   std::vector<char> out;
+   fmt::format_to(std::back_inserter(out), "{}", 42);
+ \endrst
+ */
+template <typename OutputIt, typename S, typename... Args,
+          FMT_ENABLE_IF(
+              internal::is_output_iterator<OutputIt>::value &&
+              !internal::is_contiguous_back_insert_iterator<OutputIt>::value &&
+              internal::is_string<S>::value)>
+inline OutputIt format_to(OutputIt out, const S& format_str, Args&&... args) {
+  internal::check_format_string<Args...>(format_str);
+  using context = format_context_t<OutputIt, char_t<S>>;
+  return vformat_to(out, to_string_view(format_str),
+                    make_format_args<context>(args...));
+}
+
+template <typename OutputIt> struct format_to_n_result {
+  /** Iterator past the end of the output range. */
+  OutputIt out;
+  /** Total (not truncated) output size. */
+  std::size_t size;
+};
+
+template <typename OutputIt, typename Char = typename OutputIt::value_type>
+using format_to_n_context =
+    format_context_t<internal::truncating_iterator<OutputIt>, Char>;
+
+template <typename OutputIt, typename Char = typename OutputIt::value_type>
+using format_to_n_args = basic_format_args<format_to_n_context<OutputIt, Char>>;
+
+template <typename OutputIt, typename Char, typename... Args>
+inline format_arg_store<format_to_n_context<OutputIt, Char>, Args...>
+make_format_to_n_args(const Args&... args) {
+  return format_arg_store<format_to_n_context<OutputIt, Char>, Args...>(
+      args...);
+}
+
+template <typename OutputIt, typename Char, typename... Args,
+          FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value)>
+inline format_to_n_result<OutputIt> vformat_to_n(
+    OutputIt out, std::size_t n, basic_string_view<Char> format_str,
+    format_to_n_args<type_identity_t<OutputIt>, type_identity_t<Char>> args) {
+  auto it = vformat_to(internal::truncating_iterator<OutputIt>(out, n),
+                       format_str, args);
+  return {it.base(), it.count()};
+}
+
+/**
+ \rst
+ Formats arguments, writes up to ``n`` characters of the result to the output
+ iterator ``out`` and returns the total output size and the iterator past the
+ end of the output range.
+ \endrst
+ */
+template <typename OutputIt, typename S, typename... Args,
+          FMT_ENABLE_IF(internal::is_string<S>::value&&
+                            internal::is_output_iterator<OutputIt>::value)>
+inline format_to_n_result<OutputIt> format_to_n(OutputIt out, std::size_t n,
+                                                const S& format_str,
+                                                const Args&... args) {
+  internal::check_format_string<Args...>(format_str);
+  using context = format_to_n_context<OutputIt, char_t<S>>;
+  return vformat_to_n(out, n, to_string_view(format_str),
+                      make_format_args<context>(args...));
+}
+
+template <typename Char>
+inline std::basic_string<Char> internal::vformat(
+    basic_string_view<Char> format_str,
+    basic_format_args<buffer_context<type_identity_t<Char>>> args) {
+  basic_memory_buffer<Char> buffer;
+  internal::vformat_to(buffer, format_str, args);
+  return to_string(buffer);
+}
+
+/**
+  Returns the number of characters in the output of
+  ``format(format_str, args...)``.
+ */
+template <typename... Args>
+inline std::size_t formatted_size(string_view format_str, const Args&... args) {
+  return format_to(internal::counting_iterator(), format_str, args...).count();
+}
+
+template <typename Char, FMT_ENABLE_IF(std::is_same<Char, wchar_t>::value)>
+void vprint(std::FILE* f, basic_string_view<Char> format_str,
+            wformat_args args) {
+  wmemory_buffer buffer;
+  internal::vformat_to(buffer, format_str, args);
+  buffer.push_back(L'\0');
+  if (std::fputws(buffer.data(), f) == -1)
+    FMT_THROW(system_error(errno, "cannot write to file"));
+}
+
+template <typename Char, FMT_ENABLE_IF(std::is_same<Char, wchar_t>::value)>
+void vprint(basic_string_view<Char> format_str, wformat_args args) {
+  vprint(stdout, format_str, args);
+}
+
+#if FMT_USE_USER_DEFINED_LITERALS
+namespace internal {
+
+#  if FMT_USE_UDL_TEMPLATE
+template <typename Char, Char... CHARS> class udl_formatter {
+ public:
+  template <typename... Args>
+  std::basic_string<Char> operator()(Args&&... args) const {
+    FMT_CONSTEXPR_DECL Char s[] = {CHARS..., '\0'};
+    FMT_CONSTEXPR_DECL bool invalid_format =
+        do_check_format_string<Char, error_handler, remove_cvref_t<Args>...>(
+            basic_string_view<Char>(s, sizeof...(CHARS)));
+    (void)invalid_format;
+    return format(s, std::forward<Args>(args)...);
+  }
+};
+#  else
+template <typename Char> struct udl_formatter {
+  basic_string_view<Char> str;
+
+  template <typename... Args>
+  std::basic_string<Char> operator()(Args&&... args) const {
+    return format(str, std::forward<Args>(args)...);
+  }
+};
+#  endif  // FMT_USE_UDL_TEMPLATE
+
+template <typename Char> struct udl_arg {
+  basic_string_view<Char> str;
+
+  template <typename T> named_arg<T, Char> operator=(T&& value) const {
+    return {str, std::forward<T>(value)};
+  }
+};
+
+// Converts string literals to basic_string_view.
+template <typename Char, size_t N>
+FMT_CONSTEXPR basic_string_view<Char> compile_string_to_view(
+    const Char (&s)[N]) {
+  // Remove trailing null character if needed. Won't be present if this is used
+  // with raw character array (i.e. not defined as a string).
+  return {s,
+          N - ((std::char_traits<Char>::to_int_type(s[N - 1]) == 0) ? 1 : 0)};
+}
+
+// Converts string_view to basic_string_view.
+template <typename Char>
+FMT_CONSTEXPR basic_string_view<Char> compile_string_to_view(
+    const std_string_view<Char>& s) {
+  return {s.data(), s.size()};
+}
+}  // namespace internal
+
+inline namespace literals {
+#  if FMT_USE_UDL_TEMPLATE
+#    pragma GCC diagnostic push
+#    if FMT_CLANG_VERSION
+#      pragma GCC diagnostic ignored "-Wgnu-string-literal-operator-template"
+#    endif
+template <typename Char, Char... CHARS>
+FMT_CONSTEXPR internal::udl_formatter<Char, CHARS...> operator""_format() {
+  return {};
+}
+#    pragma GCC diagnostic pop
+#  else
+/**
+  \rst
+  User-defined literal equivalent of :func:`fmt::format`.
+
+  **Example**::
+
+    using namespace fmt::literals;
+    std::string message = "The answer is {}"_format(42);
+  \endrst
+ */
+FMT_CONSTEXPR internal::udl_formatter<char> operator"" _format(const char* s,
+                                                               std::size_t n) {
+  return {{s, n}};
+}
+FMT_CONSTEXPR internal::udl_formatter<wchar_t> operator"" _format(
+    const wchar_t* s, std::size_t n) {
+  return {{s, n}};
+}
+#  endif  // FMT_USE_UDL_TEMPLATE
+
+/**
+  \rst
+  User-defined literal equivalent of :func:`fmt::arg`.
+
+  **Example**::
+
+    using namespace fmt::literals;
+    fmt::print("Elapsed time: {s:.2f} seconds", "s"_a=1.23);
+  \endrst
+ */
+FMT_CONSTEXPR internal::udl_arg<char> operator"" _a(const char* s,
+                                                    std::size_t n) {
+  return {{s, n}};
+}
+FMT_CONSTEXPR internal::udl_arg<wchar_t> operator"" _a(const wchar_t* s,
+                                                       std::size_t n) {
+  return {{s, n}};
+}
+}  // namespace literals
+#endif  // FMT_USE_USER_DEFINED_LITERALS
+FMT_END_NAMESPACE
+
+#define FMT_STRING_IMPL(s, ...)                                     \
+  [] {                                                              \
+    /* Use a macro-like name to avoid shadowing warnings. */        \
+    struct FMT_COMPILE_STRING : fmt::compile_string {               \
+      using char_type = fmt::remove_cvref_t<decltype(s[0])>;        \
+      FMT_MAYBE_UNUSED __VA_ARGS__ FMT_CONSTEXPR                    \
+      operator fmt::basic_string_view<char_type>() const {          \
+        return fmt::internal::compile_string_to_view<char_type>(s); \
+      }                                                             \
+    };                                                              \
+    return FMT_COMPILE_STRING();                                    \
+  }()
+
+/**
+  \rst
+  Constructs a compile-time format string from a string literal *s*.
+
+  **Example**::
+
+    // A compile-time error because 'd' is an invalid specifier for strings.
+    std::string s = format(FMT_STRING("{:d}"), "foo");
+  \endrst
+ */
+#define FMT_STRING(s) FMT_STRING_IMPL(s, )
+
+#if defined(FMT_STRING_ALIAS) && FMT_STRING_ALIAS
+#  define fmt(s) FMT_STRING_IMPL(s, [[deprecated]])
+#endif
+
+#ifdef FMT_HEADER_ONLY
+#  define FMT_FUNC inline
+#  include "format-inl.h"
+#else
+#  define FMT_FUNC
+#endif
+
+#endif  // FMT_FORMAT_H_
diff --git a/GeoModelDBManager/src/GMDBManager.cpp b/GeoModelDBManager/src/GMDBManager.cpp
index 3db7b6fc936f51048aa7ce0319bc6148e376162b..5189dff460f23ac05ba81e61f956a9ca75b8f609 100644
--- a/GeoModelDBManager/src/GMDBManager.cpp
+++ b/GeoModelDBManager/src/GMDBManager.cpp
@@ -1,73 +1,98 @@
 
-// author: Riccardo.Maria.Bianchi@cern.ch - 2017
-// major updates: Aug 2018
+/*
+* author: Riccardo.Maria.Bianchi@cern.ch - 2017
+* major updates:
+*  - Aug 2018, R.M.Bianchi
+*  - Jun 2020, R.M.Bianchi
+*/
 
 #include <GeoModelDBManager/GMDBManager.h>
 
-#include <QSqlQuery>
-#include <QSqlError>
-#include <QSqlRecord>
-#include <QSqlDriver>
-#include <QDebug>
+// include the 'fmt' library, which is hosted locally as header-only
+#define FMT_HEADER_ONLY 1 // to use 'fmt' header-only
+#include "fmt/format.h"
 
+// C++ includes
 #include <stdlib.h> /* exit, EXIT_FAILURE */
+#include <sstream>
 
 
-static std::string dbversion = "0.3.0"; // added GeoElement support (Sep 2019)
 
+static std::string dbversion = "0.4.0"; // removed "parent" info from [Full]PhysVols tables (May 2020)
 
-GMDBManager::GMDBManager(const QString &path) : m_dbpath(path), m_dbIsOK(false),  m_deepDebug(false)
+
+// FIXME: move this to utility class/file
+std::vector<std::string> toStdVectorStrings(QStringList qlist)
 {
-	// JFB commented: qDebug() << "GMDBManager: constructor";
+  std::vector<std::string> vec;
+  foreach(QString qstr, qlist) {
+    vec.push_back(qstr.toStdString());
+  }
+  return vec;
+}
 
-	#ifdef GEOREAD_DEEP_DEBUG
-	  m_deepDebug = true;
- 	#endif
+// FIXME: TODO: move to an utility class
+std::string getEnvVar( std::string const & key )
+{
+  char * val = std::getenv( key.c_str() );
+  return val == NULL ? std::string("") : std::string(val);
+}
 
+// FIXME: should go to an utility class
+std::string joinVectorStrings(std::vector<std::string> vec, std::string sep="") {
+  std::string s;
+  unsigned int ii = 0;
+  for (const auto &piece : vec) {
+    ++ii;
+    if (ii == vec.size()) {
+      s += (piece);
+    } else {
+      s += (piece + sep);
+    }
+  }
+  return s;
+}
 
-	m_dbIsOK = true;
 
-	m_db = QSqlDatabase::addDatabase("QSQLITE");
-	m_db.setDatabaseName(path);
+GMDBManager::GMDBManager(const std::string &path) : m_dbpath(path), m_dbSqlite(nullptr), m_SQLiteErrMsg(0), m_dbIsOK(false), m_debug(false)
+{
+  // Check if the user asked for running in serial or multi-threading mode
+  if ( "" != getEnvVar("GEOMODEL_ENV_IO_DBMANAGER_DEBUG")) {
+    m_debug = true;
+    std::cout << "You defined the GEOMODEL_IO_DEBUG variable, so you will see a verbose output." << std::endl;
+  }
 
-	if (!m_db.open())
-	{
-		qDebug() << "Error: connection with database failed!";
-		showError( m_db.lastError() );
-		m_dbIsOK = false;
-	}
-	else
-	{
-		// JFB commented: qDebug() << "Database: connection ok";
-		m_dbIsOK = true;
-	}
+  m_dbIsOK = false;
 
+  // FIXME: TODO: we should check the existence of the file, otherwise SQLite will create a new file from scratch
+  // Save the connection result
+  int exit = sqlite3_open(path.c_str(), &m_dbSqlite);
 
-	// check if DB has tables, if not create them
-	if (m_dbIsOK) {
-		if ( ! (initDB()) ) {
-			std::cout << "Error: database initialization failed" << std::endl;
-			m_dbIsOK = false;
-		}
-	}
+  // Test if there was an error
+  if (exit == SQLITE_OK) {
+    std::cout << "The Geometry Database '"<< path << "' has been opened successfully!" << std::endl;
+    m_dbIsOK = true;
+  } else {
+    std::cout << "DB Open Error: " << sqlite3_errmsg(m_dbSqlite) << std::endl;
+    m_dbIsOK = false;
+  }
 
-	// populate DB with fake data, only for debug
-	//loadTestData();
-}
+//  if (m_dbIsOK) {
+//    if ( ! (initDB()) ) {
+//      std::cout << "Error: database initialization failed" << std::endl;
+//      m_dbIsOK = false;
+//    }
+//  }
 
-GMDBManager::~GMDBManager()
-{
-	if (m_db.isOpen())
-	{
-		m_db.close();
-	}
+  // populate DB with fake data, only for debug
+  //loadTestData(); // TODO: we should move initDB() here, only for debug and Write
 }
 
-bool GMDBManager::isOpen() const
+
+GMDBManager::~GMDBManager()
 {
-	if (! m_dbIsOK )
-		return false;
-	return m_db.isOpen();
+  sqlite3_close(m_dbSqlite);
+  m_dbSqlite = nullptr;
 }
 
 
@@ -142,281 +167,169 @@ void GMDBManager::printDBVersion() const
 
 
 
-void GMDBManager::printAllRecords(QString tableName) const
+void GMDBManager::printAllRecords(const std::string &tableName) const
 {
-	std::cout << tableName.toStdString() << " in db:" << std::endl;
-
-	// QSqlQuery query("SELECT * FROM " + tableName);
-	QSqlQuery query = selectAllFromTable(tableName);
-
-	int nCols = (m_tableNames[tableName]).size();
-
-	// print table column names
-	std::cout << "- " << m_tableNames[tableName].join(", ").toStdString() << std::endl;
-	while (query.next())
-	{
-		std::cout << "* "; // TODO: move to a osstream: it's cleaner
-		for( int ii=0; ii<nCols; ++ii)
-		{
-			if (! (ii==0) )
-				std::cout << ", ";
-			std::string valueStr = query.value(ii).toString().toStdString();
-			// if (valueStr == "")
-			// 	std::cout << "NULL"; // removed because we want to save the NULL to the DB, for consistency
-			// else
-				std::cout << valueStr;
-		}
-		std::cout << std::endl;
-	}
-
+  // --- print table name
+  std::cout << tableName << " in db:" << std::endl;
+  // --- print table column names
+  std::cout << "- " <<  joinVectorStrings(m_tableNames.at(tableName), ", ") << std::endl;
+  // --- print records
+  std::vector<std::vector<std::string>> records;
+//  std::vector<std::string> nodeParams;
+  records = getTableRecords(tableName);
+  for( auto& row : records) {
+    std::cout << "* ";
+    for( auto& item : row) {
+      std::cout << item << ", ";
+    }
+    std::cout << std::endl;
+  }
+  std::cout << "---" << std::endl;
 	// TODO: I want to have a symbol like '---' to mean empty line when query gives 0 results.
-	// but I guess query.size() is not the right method...
-	//	qDebug() << "query size: " << query.size();
-	//	if (query.size() == 0 || query.size() == -1)
-	//		std::cout << "---" << std::endl;
-
 }
-QHash<unsigned int, QStringList> GMDBManager::getTableFromNodeType(QString nodeType)
-{
-
-	QString tableName = getTableNameFromNodeType(nodeType);
-
-	QHash<unsigned int, QStringList> records;
-	QStringList nodeParams;
-
-	int nCols = (m_tableNames[tableName]).size();
 
-	QSqlQuery query = selectAllFromTable(tableName);
-	while (query.next()) {
-		nodeParams.clear();
-		unsigned int nodeId = query.value(0).toUInt();
 
-		for( int ii=0; ii<nCols; ++ii) {
-			nodeParams << query.value(ii).toString();
-		}
-		records[nodeId] = nodeParams;
-	}
-return records;
-}
-
-std::vector<std::vector<std::string>> GMDBManager::getTableFromNodeTypeStd(std::string nodeType)
+// FIXME: TODO: we now return all records as text, but should we get double/int instead when appropriate? In that case, we should create dedicated methods for all tables, I guess.
+// TODO: fill a cache and returns that if asked a second time
+std::vector<std::vector<std::string>> GMDBManager::getTableRecords(std::string tableName) const
 {
-
-	QString tableName = getTableNameFromNodeType(QString::fromStdString(nodeType));
-
-	// QHash<unsigned int, QStringList> records;
-	// QStringList nodeParams;
+  // container to be returned
   std::vector<std::vector<std::string>> records;
-  std::vector<std::string> nodeParams;
-
-	int nCols = (m_tableNames[tableName]).size();
-
-	QSqlQuery query = selectAllFromTable(tableName); // sorted by ID
-	while (query.next()) {
-		nodeParams.clear();
-//    unsigned int nodeId = query.value(0).toUInt();
-
-		for( int ii=0; ii<nCols; ++ii) {
-			// nodeParams << query.value(ii).toString();
-			nodeParams.push_back( query.value(ii).toString().toStdString() );
-		}
-		// records[nodeId] = nodeParams;
-    records.push_back(nodeParams);
-	}
-return records;
-}
-
-
-void GMDBManager::showError(const QSqlError &err) const
-{
-	qWarning() << "Unable to initialize Database" << "Error initializing database: " + err.text();
-}
-
-QVariant GMDBManager::addPhysVol(const QVariant &logVolId, const QVariant &parentPhysVolId, bool isRootVolume)
-{
-	if (m_deepDebug) qDebug() << "GMDBManager::addPhysVol() - is root?" << isRootVolume;
-
-	QSqlQuery q;
-	if (!q.prepare(QLatin1String("insert into PhysVols(logvol, parent) values(?, ?)"))) {
-		showError(q.lastError());
-		return QVariant();
-	}
-
-	q.addBindValue(logVolId);
-	q.addBindValue(parentPhysVolId);
-	q.exec();
-
-	QVariant lastInserted = q.lastInsertId();
-
-	if (isRootVolume) {
-		storeRootVolume(lastInserted, "GeoPhysVol");
-	}
-
-	return lastInserted;
-
-}
-
-QVariant GMDBManager::addFullPhysVol(const QVariant &logVolId, const QVariant &parentPhysVolId, bool isRootVolume)
-{
-	if (m_deepDebug) qDebug() << "GMDBManager::addFullPhysVol() - is root?" << isRootVolume;
-
-	QSqlQuery q;
-	if (!q.prepare(QLatin1String("insert into FullPhysVols(logvol, parent) values(?, ?)"))) {
-		showError(q.lastError());
-		return QVariant();
-	}
-
-	q.addBindValue(logVolId);
-	q.addBindValue(parentPhysVolId);
-	q.exec();
-
-	QVariant lastInserted = q.lastInsertId();
-
-	if (isRootVolume) {
-		storeRootVolume(lastInserted, "GeoFullPhysVol");
-	}
-
-	return lastInserted;
-
+  // get the query statetement ready to be executed
+  sqlite3_stmt* stmt = nullptr;
+  if ("ChildrenPositions" == tableName) {
+    stmt = selectAllFromTableChildrenPositions();
+  }
+  else {
+    stmt = selectAllFromTable(tableName);
+  }
+  // execute the query and loop over all rows and all columuns
+  if ( stmt )
+  {
+    int ctotal = sqlite3_column_count(stmt); // Count the Number of Columns in the Table
+    int res = 0;
+    while ( 1 )
+    {
+      res = sqlite3_step(stmt); // Execute SQL Statement.
+      if ( res == SQLITE_ROW )
+      {
+        std::vector<std::string> nodeParams; // stores the data items contained in a single row
+        for ( int i = 0; i < ctotal; i++ )  // Loop times the number of columns in the table
+        {
+          std::string s = (char*)sqlite3_column_text(stmt, i);  // Read each Column in the row as text FIXME: is there a method to get the right type, e.g. double, instead of text?
+          nodeParams.push_back( s );
+        }
+        records.push_back(nodeParams);
+      }
+      
+      if ( res == SQLITE_DONE || res==SQLITE_ERROR)
+      {
+        if (res == SQLITE_ERROR) {
+          std::string errmsg(sqlite3_errmsg(m_dbSqlite));
+          sqlite3_finalize(stmt);
+          throw errmsg;
+        }
+        break;
+      }
+    }
+  }
+  // finalize
+  sqlite3_finalize(stmt);
+  return records;
 }
 
 
-
-QVariant GMDBManager::addLogVol(const QString &name, const QVariant &shapeId, const QVariant &materialId)
-{
-	if (m_deepDebug) qDebug() << "GMDBManager::addLogVol()";
-
-	QSqlQuery q;
-	if (!q.prepare(QLatin1String("insert into LogVols(name, shape, material) values(?, ?, ?)"))) {
-		showError(q.lastError());
-		return QVariant();
-	}
-
-	q.addBindValue(name);
-	q.addBindValue(shapeId);
-	q.addBindValue(materialId);
-	q.exec();
-	return q.lastInsertId();
-}
-
-QVariant GMDBManager::addMaterial(const QString &name, const QString &density, const QString &elements)
+std::vector<std::vector<std::string>> GMDBManager::getTableFromNodeType(std::string nodeType)
 {
-	if (m_deepDebug) qDebug() << "GMDBManager::addMaterial()";
 
-	QSqlQuery q;
-	if (!q.prepare(QLatin1String("insert into Materials(name, density, elements) values(?, ?, ?)"))) {
-		showError(q.lastError());
-		return QVariant();
-	}
-	q.addBindValue(name);
-	q.addBindValue(density);
-	q.addBindValue(elements);
-	q.exec();
-	return q.lastInsertId();
+  std::string tableName = getTableNameFromNodeType(nodeType);
+  return getTableRecords(tableName);
 }
 
+// TODO: simplify error reporting for SQLite
+//void GMDBManager::showError(const QSqlError &err) const
+//{
+//  qWarning() << "Unable to initialize Database" << "Error initializing database: " + err.text();
+//}
 
-QVariant GMDBManager::addElement(const QString &name, const QString &symbol, const QString &elZ, const QString &elA)
-{
-	if (m_deepDebug) qDebug() << "GMDBManager::addElement()";
-
-	QSqlQuery q;
-	if (!q.prepare(QLatin1String("insert into Elements(name, symbol, Z, A) values(?, ?, ?, ?)"))) {
-		showError(q.lastError());
-		return QVariant();
-	}
-	q.addBindValue(name);
-	q.addBindValue(symbol);
-	q.addBindValue(elZ);
-	q.addBindValue(elA);
-	q.exec();
-	return q.lastInsertId();
-}
 
-bool GMDBManager::addListOfChildrenPositions(const std::vector<QStringList> records)
+bool GMDBManager::addListOfChildrenPositions(const std::vector<std::vector<std::string>> &records)
 {
     // NOTE: Choose the right function for your version of SQLite!!
-	return addListOfRecordsToTable("ChildrenPositions", records); // newest SQLite versions
+	return addListOfRecordsToTable("ChildrenPositions", records); // needs SQLite >= 3.7.11
 	//return addListOfRecordsToTableOld("ChildrenPositions", records); // old SQLite versions
 }
 
-bool GMDBManager::addListOfRecords(const QString geoType, const std::vector<QStringList> records)
+
+bool GMDBManager::addListOfRecords(const std::string geoType, const std::vector<std::vector<std::string>> records)
 {
-	if (m_deepDebug) qDebug() << "GMDBManager::addListOfRecords():" << geoType;
+//  if (m_debug) qDebug() << "GMDBManager::addListOfRecords():" << geoType;
 
-    QString tableName = m_childType_tableName[geoType];
+  std::string tableName = m_childType_tableName[geoType];
 
-	if (tableName.isEmpty()) {
-        //qWarning() << "m_childType_tableName:" << m_childType_tableName;
-		qWarning() << "ERROR!! could not retrieve tableName for node type " << geoType << "!! Aborting...";
-		exit(1);
-	}
+  if (tableName.size() == 0) {
+    //qWarning() << "m_childType_tableName:" << m_childType_tableName;
+    std::cout << "ERROR!! could not retrieve tableName for node type '" << geoType << "'!! Aborting..." << std::endl;
+    exit(EXIT_FAILURE);
+  }
 
-	if (records.size() > 0 ) {
-        // NOTE: Choose the right function!!
-		return addListOfRecordsToTable(tableName, records); // newest SQLite versions
-		//return addListOfRecordsToTableOld(tableName, records); // old SQLite versions
-	}
-	else
-		qWarning() << "Info: no records to save for geoType '" << geoType << "'. Skipping...";
-	return true;
+  if (records.size() > 0 ) {
+    // NOTE: Choose the right function!!
+    return addListOfRecordsToTable(tableName, records); // better, but needs SQLite >= 3.7.11
+    //return addListOfRecordsToTableOld(tableName, records); // old SQLite versions
+  }
+  else {
+    if(m_debug) std::cout << "Info: no records to save for geoType '" << geoType << "'. Skipping..." << std::endl;
+  }
+  return true;
 }
 
+
 // ***Note***
-// the syntax built here below is more convenient
+// The syntax built here below is more convenient
 // but it is supported only in SQLite >= 3.7.11
-// ...but SLC6 only has 3.7.9!
-// here we build a query like this:
+// (Note: old SLC6-based Athena releases uses SQLite 3.7.9 from LCG)
+//
+// Here we build a query like this:
 // queryStr = QString("INSERT INTO Materials (id, name) VALUES  (1,'Air'), (2,'Silicon'), (368,'ShieldSteel');");
 //
-bool GMDBManager::addListOfRecordsToTable(const QString tableName, const std::vector<QStringList> records)
-{
-	// get table columns and format them for query
-	QString tableColString = "(" + m_tableNames[tableName].join(", ") + ")";
-
-    unsigned int nRecords = records.size();
-    qInfo() << "number of " << tableName << "records to insert into the DB:" << nRecords;
-
-	// preparing the SQL query
-	QString queryStr("INSERT INTO %1 %2 VALUES ");
-	queryStr = queryStr.arg(tableName); // insert table name
-	queryStr = queryStr.arg(tableColString); // insert table columns
-
-	unsigned int nMat = nRecords;
-	unsigned int id = 0;
-	foreach(QStringList rec, records) {
-        //qDebug() << "rec:" << rec;
-
-		++id;
-		QStringList items;
-
-		foreach (QString item, rec) {
-			items << '"' + item + '"';
-		}
-		QString values = items.join(",");
-		queryStr += " (" + QString::number(id) + "," + values + ")";
-		if (id != nMat)
-			queryStr += ",";
-		else
-			queryStr += ";";
-
-        }
-    // JFB commented: qDebug() << "queryStr:" << queryStr;
-
-	// executing the SQL query
-	QSqlQuery q;
-	if (!q.exec(queryStr)) {
-        qWarning() << "ERROR!!! SQL error:";
-		showError(q.lastError());
-		return false;
-	}
-
-	// JFB commented: qDebug() << "DONE. The list of records have been inserted into the DB.";
-	return true;
+bool GMDBManager::addListOfRecordsToTable(const std::string tableName, const std::vector<std::vector<std::string>> records)
+{
+  // get table columns and format them for query
+  std::string tableColString = "(" + joinVectorStrings(m_tableNames.at(tableName), ", ") + ")";
+  if(m_debug) std::cout << "tableColString:" << tableColString << std::endl;
+
+  unsigned int nRecords = records.size();
+  std::cout << "Info: number of " << tableName << " records to dump into the DB:" << nRecords << std::endl;
+
+  // preparing the SQL query
+  std::string sql = fmt::format("INSERT INTO {0} {1} VALUES ", tableName, tableColString);
+  unsigned int id = 0;
+  for( const std::vector<std::string>& rec : records) {
+    ++id;
+    std::vector<std::string> items;
+    for ( const std::string& item : rec) {
+      items.push_back("'" + item + "'");
+    }
+    std::string values = joinVectorStrings(items, ",");
+    sql += " (" + std::to_string(id) + "," + values + ")";
+    if (id != nRecords) {
+      sql += ",";
+    } else {
+      sql += ";";
+    }
+    
+  }
+  if(m_debug) std::cout << "Query string:" << sql << std::endl; // debug
+  
+  // executing the SQL query
+  if ( ! (execQuery(sql)) ) {
+    return false;
+  }
+  return true;
 }
 
-
-
+// TODO: this is for the old SQLite. Not needed anymore, I guess. ==> Just put a requirement on the newer version of SQLite3 in CMakeLists.txt. Perhaps, also check that GeoModelIO can run smoothly on older ATLAS releases, like 21.9 by taking a newer SQLite3 from LCG.
 // ***Note***
 // old syntax, for SQLite in SLC6
 // here below we build the syntax for multiple INSERT
@@ -426,21 +339,23 @@ bool GMDBManager::addListOfRecordsToTable(const QString tableName, const std::ve
 // we want to build a query like this:
 //  queryStr = QString("INSERT INTO Materials (id, name) SELECT 1 as id, 'Air' as name UNION ALL SELECT 2,'Silicon' UNION ALL SELECT 368,'ShieldSteel' ")
 //
+/*
 bool GMDBManager::addListOfRecordsToTableOld(const QString tableName, const std::vector<QStringList> records)
 {
 	// get table columns and format them for query
-	QString tableColString = "(" + m_tableNames[tableName].join(", ") + ")";
+  std::string tabColNames = joinVectorStrings(m_tableNames.at(tableName.toStdString()), ", ");
+  QString tableColString = "(" + QString::fromStdString(tabColNames) + ")";
 
 
 
-    QStringList colNames = m_tableNames[tableName];
+    QStringList colNames = m_tableNames.at(tableName);
     unsigned int nRecords = records.size();
     qInfo() << "number of " << tableName << "records to insert into the DB:" << nRecords;
 
-    /*
-     * SQLite has a limit on 'union' items, set at 500. So we have to split the items if we have more.
-     * See: stackoverflow.com/questions/9527851/
-     */
+
+     // SQLite has a limit on 'union' items, set at 500. So we have to split the items if we have more.
+     // See: stackoverflow.com/questions/9527851/
+
     unsigned int bunchSize = 500;
     if ( nRecords > bunchSize ) {
         qWarning() << "WARNING! " << nRecords << " records of type " << tableName << "to store in the DB in one call! Call limit is " << bunchSize << " --> We split them in bunches...";
@@ -464,14 +379,14 @@ bool GMDBManager::addListOfRecordsToTableOld(const QString tableName, const std:
 
         std::vector<QStringList> recordsBunch( first, last ); // we take the first 500 records
         recordsCopy.erase( first, last ); // we delete the first 500 records
-        if (m_deepDebug) qDebug() << "start:" << start << "recordsBunch size:" << recordsBunch.size() << "- recordsCopy size after removal:" << recordsCopy.size();
+        if (m_debug) qDebug() << "start:" << start << "recordsBunch size:" << recordsBunch.size() << "- recordsCopy size after removal:" << recordsCopy.size();
 
 	    // --- first record
         // outcome should be like: " 1 as id, 'Air' as name "
         unsigned int id = start+1; // set the first ID as 'start'
         QStringList recFirst = recordsBunch.front(); // access first record from vector
         recordsBunch.erase( recordsBunch.begin() ); // delete first record
-        if (m_deepDebug) qDebug() << "after taking first record - recordsBunch size:" << recordsBunch.size();
+        if (m_debug) qDebug() << "after taking first record - recordsBunch size:" << recordsBunch.size();
 
         // first item in the first record (it is the ID)
         QString firstCol = colNames[0];
@@ -488,7 +403,7 @@ bool GMDBManager::addListOfRecordsToTableOld(const QString tableName, const std:
             //qDebug() << "first element:" << ii << nRecs << queryStr;
             ++ii;
         }
-        if (m_deepDebug) qDebug() << "first element query:" << queryStr;
+        if (m_debug) qDebug() << "first element query:" << queryStr;
         // --- other records
         // outcome should be: " UNION ALL SELECT 2,'Silicon' "
 
@@ -595,1182 +510,748 @@ bool GMDBManager::addListOfRecordsToTableOld(const QString tableName, const std:
 	return true;
 
 }
+*/
 
 
-
-
-
-QVariant GMDBManager::addShape(const QString &type, const QString &parameters)
+bool GMDBManager::addRootVolume(const std::vector<std::string> &values)
 {
-	if (m_deepDebug) qDebug() << "GMDBManager::addShape()";
-
-	QSqlQuery q;
-	if (!q.prepare(QLatin1String("insert into Shapes(type, parameters) values(?, ?)"))) {
-		showError(q.lastError());
-		return QVariant();
+	if (values.size() > 0 ) {
+    const unsigned int volId = std::stoi(values[0]);
+    const std::string nodeType = values[1];
+		return storeRootVolume( volId, nodeType );
 	}
-
-	q.addBindValue(type);
-	q.addBindValue(parameters);
-	q.exec();
-	return q.lastInsertId();
+  else {
+    std::cout << "ERROR! No records to save for RootVolume! All GeoModel trees must have a 'World'/'Root' volume! Exiting..." << std::endl;
+    exit(EXIT_FAILURE);
+  }
 }
 
-QVariant GMDBManager::addSerialDenominator(const QString &baseName)
-{
-	if (m_deepDebug) qDebug() << "GMDBManager::addSerialDenominator("+baseName+")";
-
-	QSqlQuery q;
-	if (!q.prepare(QLatin1String("insert into SerialDenominators(baseName) values(?)"))) {
-		showError(q.lastError());
-		return QVariant();
-	}
 
-	q.addBindValue(baseName);
-	q.exec();
-	return q.lastInsertId();
+void GMDBManager::addDBversion(std::string version)
+{
+  checkIsDBOpen();
+  sqlite3_stmt * st = nullptr;
+  int rc = -1;
+  std::string sql = "INSERT INTO dbversion(version) VALUES(?)";
+  rc = sqlite3_prepare_v2( m_dbSqlite, sql.c_str(), -1, &st, NULL);
+  if (rc != SQLITE_OK) {
+    printf( "[SQLite ERR] (%s) : Error msg: %s\n", __func__, sqlite3_errmsg(m_dbSqlite) );
+    exit(EXIT_FAILURE);
+  }
+  rc = sqlite3_bind_text(st, 1, version.c_str(), version.length(), SQLITE_TRANSIENT);
+  rc = sqlite3_step( st );
+  if (rc != SQLITE_DONE) {
+    printf( "[SQLite ERR] (%s) : Error msg: %s\n", __func__, sqlite3_errmsg(m_dbSqlite) );
+    exit(EXIT_FAILURE);
+  }
+  // finalize
+  sqlite3_finalize(st);
+  return;
 }
 
-QVariant GMDBManager::addNameTag(const QString &name)
+bool GMDBManager::checkIsDBOpen() const
 {
-	if (m_deepDebug) qDebug() << "GMDBManager::addNameTag("+name+")";
-
-	QSqlQuery q;
-	if (!q.prepare(QLatin1String("insert into NameTags(name) values(?)"))) {
-		showError(q.lastError());
-		return QVariant();
-	}
-	q.addBindValue(name);
-	q.exec();
-	return q.lastInsertId();
+  if(m_dbSqlite != nullptr) {
+    return true;
+  } else {
+    std::cout << "ERROR! The SQLite DB is not accessible! Exiting..." << std::endl;
+    exit(EXIT_FAILURE);
+  }
 }
 
-QVariant GMDBManager::addFunction(const QString expression)
+
+std::vector<std::string> GMDBManager::getItemAndType(unsigned int tableId, unsigned int id)
 {
-	if (m_deepDebug) qDebug() << "GMDBManager::addFunction()";
+  std::vector<std::string> results;
+  std::string tableName = getTableNameFromTableId(tableId);
+  std::string nodeType  = getNodeTypeFromTableId(tableId);
+  std::vector<std::string> item  =  getItemFromTableName(tableName, id);
+  // add the type
+  results.push_back( nodeType );
+  // add the item
+  results.insert(results.end(), item.begin(), item.end());
+  return results;
+}
 
-	// TEST
-	std::cout << "Function - expression string len: " << expression.length();
-	std::cout << "Function - expression: " << expression.toStdString() << std::endl << std::endl;
-  //-----
 
-	// JFB commented: qDebug() << "expression:" << expression;
+std::vector<std::string> GMDBManager::getItemFromTableName(std::string tableName, unsigned int id)
+{
+  // FIXME: when you create caches for all tables, replace this method with a lookup action in the cache.
+  /*
+   * Get the object from DB.
+   */
+  std::vector<std::string> item;
+  // set a SQL command string with the right table name
+  std::string sql = fmt::format("SELECT * FROM {0} WHERE id = (?)", tableName);
+  // prepare the query
+  sqlite3_stmt * stmt = nullptr;
+  int rc = -1;
+  rc = sqlite3_prepare_v2( m_dbSqlite, sql.c_str(), -1, &stmt, NULL);
+  if (rc != SQLITE_OK) {
+    printf( "[SQLite ERR] 'prepare' (%s) : Error msg: %s\n", __func__, sqlite3_errmsg(m_dbSqlite) );
+    exit(EXIT_FAILURE);
+  }
+  // bind the parameters
+  rc = sqlite3_bind_int(stmt, 1, id);
+  // execute the query and loop over all rows and all columuns
+  if ( stmt )
+  {
+    int ctotal = sqlite3_column_count(stmt); // Count the Number of Columns in the Table
+    int res = 0;
+    while ( 1 )
+    {
+      res = sqlite3_step(stmt); // Execute SQL Statement.
+      if ( res == SQLITE_ROW )
+      {
+        for ( int i = 0; i < ctotal; i++ )  // Loop times the number of columns in the table
+        {
+          std::string s = (char*)sqlite3_column_text(stmt, i);  // Read each Column in the row as text FIXME: is there a method to get the right type, e.g. double, instead of text?
+          item.push_back( s );
+        }
+      }
+      if ( res == SQLITE_DONE || res==SQLITE_ERROR)
+      {
+        if (res == SQLITE_ERROR) {
+          std::string errmsg(sqlite3_errmsg(m_dbSqlite));
+          sqlite3_finalize(stmt);
+          throw errmsg;
+        }
+        break;
+      }
+    }
+  }
+  // TODO: do we need that error check here??
+//  if (rc != SQLITE_DONE) {
+//    printf( "[SQLite ERR] 'step' (%s) : Error msg: %s\n", __func__, sqlite3_errmsg(m_dbSqlite) );
+////    exit(EXIT_FAILURE);
+//  }
+  // finalize
+  sqlite3_finalize(stmt);
+    
+  if (item.size()==0) {
+    std::cout << "ERROR!!" << "Item with ID:'" << id << "' does not exist in table" << tableName << "! Exiting...";
+    exit(EXIT_FAILURE);
+  }
+    
+  return item;
+}
 
-	QSqlQuery q;
-	if (!q.prepare(QLatin1String("insert into Functions(expression) values(?)"))) {
-		showError(q.lastError());
-		return QVariant();
-	}
 
-	q.addBindValue(expression);
-	q.exec();
-	return q.lastInsertId();
+// Get all parent-children data from the database in one go
+std::vector<std::vector<std::string>> GMDBManager::getChildrenTable()
+{
+  return getTableRecords("ChildrenPositions");
+}
 
 
-}
 
-QVariant GMDBManager::addSerialTransformer(const QVariant &funcId, const QVariant &physvolId, const QString physvolType, const unsigned int &copies)
+unsigned int GMDBManager::getTableIdFromNodeType(const std::string &nodeType)
 {
-	if (m_deepDebug) qDebug() << "GMDBManager::addSerialTransformer()" << funcId << physvolId << copies;
+  return m_cache_nodeType_tableID[nodeType];
+}
 
-	QVariant volTableId = getTableIdFromNodeType(physvolType);
 
-	QSqlQuery q;
-	if (!q.prepare(QLatin1String("insert into SerialTransformers(funcId, volId, volTable, copies) values(?, ?, ?, ?)"))) {
-		showError(q.lastError());
-		return QVariant();
-	}
+std::string GMDBManager::getTableNameFromNodeType(const std::string &nodeType)
+{
+  return m_cache_nodeType_tableName.at(nodeType);
+}
 
-	q.addBindValue(funcId);
-	q.addBindValue(physvolId);
-	q.addBindValue(volTableId);
-	q.addBindValue(copies);
-	q.exec();
-	return q.lastInsertId();
+
+// build the GeoNodeTypes cache
+int GMDBManager::loadGeoNodeTypesAndBuildCache()
+{
+    checkIsDBOpen();
+    std::string queryStr;
+    sqlite3_stmt * st = nullptr;
+    int rc = -1;
+    //set the SQL query string
+    std::string sql = "SELECT * FROM GeoNodesTypes";
+    // declare the data we want to fetch
+    unsigned int id = 0;
+    std::string nodeType = "";
+    std::string tableName = "";
+    // prepare the query
+    rc = sqlite3_prepare_v2( m_dbSqlite, sql.c_str(), -1, &st, NULL);
+    if (rc != SQLITE_OK) {
+      printf( "[SQLite ERR] (%s) : Error msg: %s\n", __func__, sqlite3_errmsg(m_dbSqlite) );
+      exit(EXIT_FAILURE);
+    }
+    // execute the statement until all selected records are processed
+    while ((rc = sqlite3_step(st)) == SQLITE_ROW) {
+      // get the data
+      id = sqlite3_column_int(st, 0);
+      nodeType = std::string(reinterpret_cast<const char*>(sqlite3_column_text(st, 1)));
+      tableName = std::string(reinterpret_cast<const char*>(sqlite3_column_text(st, 2)));
+      if(m_debug) std::cout << "row: " << id << "," << nodeType << "," << tableName << std::endl;
+      // fill the caches
+      m_cache_tableId_tableName.insert( std::pair<unsigned int, std::string>(id, tableName));
+      m_cache_tableId_nodeType.insert( std::pair<unsigned int, std::string>(id, nodeType));
+      m_cache_nodeType_tableName.insert( std::pair<std::string, std::string>(nodeType, tableName));
+      m_cache_nodeType_tableID.insert( std::pair<std::string, unsigned int>(nodeType, id));
+    }
+    if (rc != SQLITE_DONE) {
+      std::string errmsg(sqlite3_errmsg(m_dbSqlite));
+      sqlite3_finalize(st);
+      throw errmsg;
+    }
+    // finalize
+    sqlite3_finalize(st);
+  return rc;
 }
 
-QVariant GMDBManager::addTransform(QVector<double> params)
+//FIXME: should throw an error if the method `loadGeoNodeTypesAndBuildCache()` was not called before calling this method
+std::string GMDBManager::getTableNameFromTableId(unsigned int tabId)
 {
-	if (m_deepDebug) qDebug() << "GMDBManager::addTransform()";
+  return m_cache_tableId_tableName[tabId];
+}
 
-	// get the 12 matrix elements
-	double xx = params[0];
-	double xy = params[1];
-	double xz = params[2];
+std::string GMDBManager::getNodeTypeFromTableId(unsigned int tabId)
+{
+  return m_cache_tableId_nodeType[tabId];
+}
 
-	double yx = params[3];
-	double yy = params[4];
-	double yz = params[5];
+std::unordered_map<unsigned int, std::string> GMDBManager::getAll_TableIDsNodeTypes()
+{
+  return m_cache_tableId_nodeType;
+}
 
-	double zx = params[6];
-	double zy = params[7];
-	double zz = params[8];
+std::unordered_map<std::string, unsigned int> GMDBManager::getAll_NodeTypesTableIDs()
+{
+  return m_cache_nodeType_tableID;
+}
 
-	double dx = params[9];
-	double dy = params[10];
-	double dz = params[11];
 
-	QSqlQuery q;
-	if (!q.prepare(QLatin1String("insert into Transforms(xx, xy, xz, yx, yy, yz, zx, zy, zz, dx, dy, dz) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"))) {
-		showError(q.lastError());
-		return QVariant();
-	}
 
-	q.addBindValue(xx);
-	q.addBindValue(xy);
-	q.addBindValue(xz);
-	q.addBindValue(yx);
-	q.addBindValue(yy);
-	q.addBindValue(yz);
-	q.addBindValue(zx);
-	q.addBindValue(zy);
-	q.addBindValue(zz);
-	q.addBindValue(dx);
-	q.addBindValue(dy);
-	q.addBindValue(dz);
-	q.exec();
-	return q.lastInsertId();
+sqlite3_stmt* GMDBManager::selectAllFromTable(std::string tableName) const
+{
+  return selectAllFromTableSortBy(tableName, "id");
 }
 
 
-QVariant GMDBManager::addAlignableTransform(QVector<double> params)
+sqlite3_stmt* GMDBManager::selectAllFromTableSortBy(std::string tableName, std::string sortColumn) const
 {
-	if (m_deepDebug) qDebug() << "GMDBManager::addAlignableTransform()";
-
-	// get the 12 matrix elements
-	double xx = params[0];
-	double xy = params[1];
-	double xz = params[2];
-
-	double yx = params[3];
-	double yy = params[4];
-	double yz = params[5];
-
-	double zx = params[6];
-	double zy = params[7];
-	double zz = params[8];
-
-	double dx = params[9];
-	double dy = params[10];
-	double dz = params[11];
+  checkIsDBOpen();
+  if ("" == sortColumn || 0 == sortColumn.size()) {
+    sortColumn = "id";
+  }
+  sqlite3_stmt * st = nullptr; // SQLite statement to be returned
+  int rc = -1; // SQLite return code
+  //set the SQL query string
+  std::string sql = fmt::format("SELECT * FROM {0} ORDER BY {1}", tableName, sortColumn);
+  // prepare the query
+  rc = sqlite3_prepare_v2( m_dbSqlite, sql.c_str(), -1, &st, NULL);
+  if (rc != SQLITE_OK) {
+    printf( "[SQLite ERR] (%s) : Error msg: %s\n", __func__, sqlite3_errmsg(m_dbSqlite) );
+    exit(EXIT_FAILURE);
+  }
+  return st;
+}
 
-	QSqlQuery q;
-	if (!q.prepare(QLatin1String("insert into AlignableTransforms(xx, xy, xz, yx, yy, yz, zx, zy, zz, dx, dy, dz) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"))) {
-		showError(q.lastError());
-		return QVariant();
-	}
 
-	q.addBindValue(xx);
-	q.addBindValue(xy);
-	q.addBindValue(xz);
-	q.addBindValue(yx);
-	q.addBindValue(yy);
-	q.addBindValue(yz);
-	q.addBindValue(zx);
-	q.addBindValue(zy);
-	q.addBindValue(zz);
-	q.addBindValue(dx);
-	q.addBindValue(dy);
-	q.addBindValue(dz);
-	q.exec();
-	return q.lastInsertId();
+sqlite3_stmt* GMDBManager::selectAllFromTableChildrenPositions() const
+{
+  checkIsDBOpen();
+  sqlite3_stmt * st = nullptr;
+  int rc = -1;
+  //set the SQL query string
+  std::string sql = "SELECT * FROM ChildrenPositions ORDER BY parentTable, parentId, parentCopyNumber, position";
+  // prepare the query
+  rc = sqlite3_prepare_v2( m_dbSqlite, sql.c_str(), -1, &st, NULL);
+  if (rc != SQLITE_OK) {
+    printf( "[SQLite ERR] (%s) : Error msg: %s\n", __func__, sqlite3_errmsg(m_dbSqlite) );
+    exit(EXIT_FAILURE);
+  }
+  return st;
 }
 
 
-bool GMDBManager::addRootVolume(const QStringList values)
+bool GMDBManager::initDB()
 {
-	if (values.size() > 0 ) {
-		QVariant volId = QVariant(values[0]);
-		QString nodeType = values[1];
-		return storeRootVolume( volId, nodeType );
-	}
-	else
-		qFatal("Info: no records to save for RootVolume!");
-	return false;
+  // create tables
+  bool tablesOK = createTables();
+	// store DB version
+  addDBversion(dbversion);
+	return tablesOK;
 }
 
-// insert a single parent-child relationship for a given parent volume
-void GMDBManager::addChildPosition(const QVariant parentId, const QString parentType, const QVariant childId, const unsigned int parentCopyNumber, const unsigned int childPos, const QString childType, const unsigned int childCopyN)
-{
-	if (m_deepDebug) qDebug() << "GMDBManager::addChildPosition(" << parentId << "," << parentType << "," << QString::number(parentCopyNumber) << "," <<childPos << "," << childType << ", " << childId << ", " << QString::number(childCopyN) << ")";
 
-	// get table name -- which the child ID refers to -- based on the child type
-	QVariant parentTableId = getTableIdFromNodeType(parentType);
-	QVariant childTableId = getTableIdFromNodeType(childType);
 
-	QString tableName = "ChildrenPositions";
-	QStringList cols = getTableColumnNames(tableName);
 
-	QString queryStr = QString("insert into %1(%2, %3, %4, %5, %6, %7, %8) values(?, ?, ?, ?, ?, ?, ?)").arg(tableName);
-	for (int i=0; i < cols.size(); ++i) {
-		if (i != 0) // skip the "id" column
-			queryStr = queryStr.arg( cols.at(i) );
-	}
-	if (m_deepDebug) qDebug() << "queryStr:" << queryStr;
-	QSqlQuery q;
-	if (!q.prepare(queryStr)) {
-		showError(q.lastError());
-		return;
-	}
-	// JFB commented: qDebug() << "parent:" << parentId;
-	q.addBindValue(parentId);
-	q.addBindValue(parentTableId);
-	q.addBindValue(parentCopyNumber);
-	q.addBindValue(childPos);
-	q.addBindValue(childTableId);
-	q.addBindValue(childId);
-	q.addBindValue(childCopyN);
-	q.exec();
-	return;
-}
 
-void GMDBManager::addDBversion(const QString version)
+void GMDBManager::printAllDBTables()
 {
-	QSqlQuery q;
-	if (!q.prepare(QLatin1String("insert into dbversion(version) values(?)"))) {
-		showError(q.lastError());
-		return;
-	}
-	q.addBindValue(version);
-	q.exec();
-	return;
+  if(m_cache_tables.size() == 0) {
+    getAllDBTables(); // load tables and build the cache
+  }
+  for( auto& str : m_cache_tables )
+    std::cout << str << std::endl;
 }
 
 
-QStringList GMDBManager::getItem(QString geoType, unsigned int id)
+void GMDBManager::getAllDBTables()
 {
-	if (m_deepDebug) qDebug() << "GMDBManager::getItem(geoType, id)"<< geoType << QString::number(id);
-
-	/* Get the right DB table */
-	QString tableName = getTableNameFromNodeType(geoType);
-	return getItemFromTableName(tableName, id);
-}
-
-
-QStringList GMDBManager::getItem(unsigned int tableId, unsigned int id)
-{
-	if (m_deepDebug) qDebug() << "GMDBManager::getItem(tableId, id)"<< QString::number(tableId) << QString::number(id);
-	/* Get the right DB table */
-	QString tableName = getTableNameFromTableId(tableId);
-	return getItemFromTableName(tableName, id);
-
+  std::string tableName;
+  std::vector<std::string> tables;
+  // define a query string containing the necessary SQL instructions
+  std::string queryStr = "SELECT name FROM sqlite_master WHERE type ='table' AND name NOT LIKE 'sqlite_%';";
+  // prepare the query with the query string
+  sqlite3_stmt *stmt;
+  int rc = sqlite3_prepare_v2(m_dbSqlite, queryStr.c_str(), -1, &stmt, NULL);
+  if (rc != SQLITE_OK) {
+    throw std::string(sqlite3_errmsg(m_dbSqlite));
+  }
+  // execute the statement until all selected records are processed
+  while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
+    tableName = std::string(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0)));
+    //std::cout << "tableName: " << tableName << std::endl; // debug
+    tables.push_back(tableName);
+  }
+  if (rc != SQLITE_DONE) {
+    std::string errmsg(sqlite3_errmsg(m_dbSqlite));
+    sqlite3_finalize(stmt);
+    throw errmsg;
+  }
+  // finalize
+  sqlite3_finalize(stmt);
+  
+  m_cache_tables = tables;
 }
 
-QStringList GMDBManager::getItemAndType(unsigned int tableId, unsigned int id)
-{
-	if (m_deepDebug) qDebug() << "GMDBManager::getItemAndType(tableId, id)"<< QString::number(tableId) << QString::number(id);
-	/* Get the right DB table */
-	QString tableName = getTableNameFromTableId(tableId);
-
-	QString nodeType = getNodeTypeFromTableId(tableId);
-
-	QStringList results;
-	QStringList item =  getItemFromTableName(tableName, id);
-
-	results << nodeType << item;
-
-	return results;
-
-}
-std::vector<std::string> GMDBManager::getItemAndTypeStd(unsigned int tableId, unsigned int id)
+bool GMDBManager::createTables()
 {
-  std::vector<std::string> results;
+  checkIsDBOpen();
   
-  QString tableName = getTableNameFromTableId(tableId);
-  std::string nodeType  = getNodeTypeFromTableIdStd(tableId);
-  std::vector<std::string> item  =  getItemFromTableNameStd(tableName, id);
+  int rc = -1; // sqlite return code
+  std::string queryStr;
 
-  // add the type
-  results.push_back( nodeType );
-  // add the item
-  results.insert(results.end(), item.begin(), item.end());
+  std::string geoNode;
+  std::string tableName;
+  std::vector<std::string> tab;
+
+  // create a table to store the relation between the types of GeoNodes and the name of the table
+  tableName = "dbversion";
+  tab.insert(tab.begin(), {tableName, "id", "version"});
+  storeTableColumnNames(tab);
+  queryStr = fmt::format("create table {0} ({1} integer primary key, {2} integer)", tab[0], tab[1], tab[2]);
+  rc = execQuery(queryStr);
+  tab.clear();
   
-  return results;
+  // create a table to store the relation between the types of GeoNodes and the name of the table
+  tableName = "GeoNodesTypes";
+  tab.insert(tab.begin(), {tableName, "id", "nodeType", "tableName"});
+  storeTableColumnNames(tab);
+  queryStr = fmt::format("create table {0}({1} integer primary key, {2} varchar, {3} varchar)", tab[0], tab[1], tab[2], tab[3]);
+  rc = execQuery(queryStr);
+  tab.clear();
   
-}
-
-
-QStringList GMDBManager::getItemFromTableName(QString tableName, unsigned int id)
-{
-	if (m_deepDebug) qDebug() << "GMDBManager::getItemFromTableName(tableName, id)"<< tableName << QString::number(id);
-	/*
-	 * 2. Get the object from DB
-	 */
-	// prepare a SQL string with the right table name
-	QString queryStr = QString("SELECT * FROM %1 WHERE id = (?)").arg(tableName);
-	// prepare the query
-	QSqlQuery q;
-	if (!q.prepare( queryStr )) {
-		showError(q.lastError());
-		return QStringList();
-	}
-	q.addBindValue(id);
-	q.exec();
-
-	QStringList params;
-
-	// get the number of columns of the DB table
-	int nCols = (m_tableNames[tableName]).size();
-
-	while (q.next()) {
-
-		for( int ii=0; ii<nCols; ++ii)
-			params << q.value(ii).toString();
-	}
-	if (params.length()==0) {
-		qWarning() << "WARNING!!" << "Item" << id << "does not exist in table" << tableName << "!!";
-	}
-	return params;
-}
-
-std::vector<std::string> GMDBManager::getItemFromTableNameStd(QString tableName, unsigned int id)
-{
-  if (m_deepDebug) qDebug() << "GMDBManager::getItemFromTableName(tableName, id)"<< tableName << QString::number(id);
-  /*
-   * 2. Get the object from DB
-   */
-  // prepare a SQL string with the right table name
-  QString queryStr = QString("SELECT * FROM %1 WHERE id = (?)").arg(tableName);
-  // prepare the query
-  QSqlQuery q;
-  if (!q.prepare( queryStr )) {
-    showError(q.lastError());
-//    return QStringList();
-    exit(EXIT_FAILURE);
-  }
-  q.addBindValue(id);
-  q.exec();
+  // create a table to store the mother-daughter relationships between nodes (notably, between the [Full]PhysVols as the parents and their children)
+  tableName = "ChildrenPositions";
+  tab.push_back(tableName);
+  tab.push_back("id");
+  tab.push_back("parentId");
+  tab.push_back("parentTable");
+  tab.push_back("parentCopyNumber");
+  tab.push_back("position");
+  tab.push_back("childTable");
+  tab.push_back("childId");
+  tab.push_back("childCopyNumber");
+  storeTableColumnNames(tab);
+  queryStr = fmt::format("create table {0}({1} integer primary key, {2} integer, {3} integer not null REFERENCES GeoNodesTypes(id), {4} integer, {5} integer, {6} integer not null REFERENCES GeoNodesTypes(id), {7} integer not null, {8} integer not null)", tab[0], tab[1], tab[2], tab[3], tab[4], tab[5], tab[6], tab[7], tab[8]);
+  rc = execQuery(queryStr);
+  tab.clear();
+
+  // create a table to store information about the 'root' volume (also known as the 'world' volume)
+  tableName = "RootVolume";
+  tab.push_back(tableName);
+  tab.push_back("id");
+  tab.push_back("volId");
+  tab.push_back("volTable");
+  storeTableColumnNames(tab);
+  queryStr = fmt::format("create table {0}({1} integer primary key, {2} integer not null, {3} integer not null REFERENCES GeoNodesTypes(id))", tab[0], tab[1], tab[2], tab[3]);
+  rc = execQuery(queryStr);
+  tab.clear();
   
-  std::vector<std::string> params;
+  // PhysVols table
+  geoNode = "GeoPhysVol";
+  tableName = "PhysVols";
+  m_childType_tableName[geoNode] = tableName;
+  tab.push_back(tableName);
+  tab.push_back("id");
+  tab.push_back("logvol");
+  storeTableColumnNames(tab);
+  queryStr = fmt::format("create table {0}({1} integer primary key, {2} integer not null)", tab[0], tab[1], tab[2]);
+  if ( 0==(rc = execQuery(queryStr)) ) {
+    storeNodeType(geoNode, tableName); }
+  tab.clear();
   
-  // get the number of columns of the DB table
-  int nCols = (m_tableNames[tableName]).size();
+  // FullPhysVols table
+  geoNode = "GeoFullPhysVol";
+  tableName = "FullPhysVols";
+  m_childType_tableName[geoNode] = tableName;
+  tab.push_back(tableName);
+  tab.push_back("id");
+  tab.push_back("logvol");
+  storeTableColumnNames(tab);
+  queryStr = fmt::format("create table {0}({1} integer primary key, {2} integer not null)", tab[0], tab[1], tab[2]);
+  if ( 0==(rc = execQuery(queryStr))) {
+    storeNodeType(geoNode, tableName); }
+  tab.clear();
   
-  while (q.next()) {
-    
-    for( int ii=0; ii<nCols; ++ii)
-      params.push_back( q.value(ii).toString().toStdString() );
-  }
-  if (params.size()==0) {
-    qWarning() << "WARNING!!" << "Item" << id << "does not exist in table" << tableName << "!!";
-  }
-  return params;
-}
-
-
-// get the list of children for a single GeoVPhysVol (i.e., GeoPhysVol or GeoFullPhysVol)
-QMap<unsigned int, QStringList> GMDBManager::getVPhysVolChildren(const unsigned int id, const QString nodeType, const unsigned int parentCopyNumber)
-{
-	if (m_deepDebug) qDebug() << "GMDBManager::getVPhysVolChildren() - id:" << QString::number(id) << "- type:" << nodeType << "- copyN:" << QString::number(parentCopyNumber);
-
-	QVariant tableId = getTableIdFromNodeType(nodeType);
-
-	// get children and their positions
-	QSqlQuery q;
-	if (!q.prepare(QLatin1String("SELECT * FROM ChildrenPositions WHERE parentId = (?) AND parentTable = (?) AND parentCopyNumber = (?)"))) {
-		showError(q.lastError());
-		return QMap<unsigned int, QStringList>();
-	}
-	q.addBindValue(id);
-	q.addBindValue(tableId);
-	q.addBindValue(parentCopyNumber);
-	q.exec();
-
-	QMap<unsigned int, QStringList> children;
-	QStringList childParams;
-
-	// get the number of columns of the DB table
-	int nCols = m_tableNames["ChildrenPositions"].size();
-
-	while (q.next()) {
-
-		childParams.clear();
-
-		unsigned int childPos = q.value(3).toUInt();
-
-		for( int ii=0; ii<nCols; ++ii)
-			childParams << q.value(ii).toString();
-
-		children[childPos] = childParams;
-	}
-
-	return children;
-}
-
-
-// Get all parent-children data from the database in one go
-QHash<QString, QMap<unsigned int, QStringList>> GMDBManager::getChildrenTable()
-{
-	// JFB commented: qDebug() << "GMDBManager::getChildrenTable()";
-
-	QSqlQuery q = selectAllFromTable("ChildrenPositions");
-
-	QHash< QString, QMap<unsigned int, QStringList> > all_children; // to store all children
-	// QMap<unsigned int, QStringList> children; // to temporarily store the children of one parent
-	QStringList childParams; // to temporarily store the children parameters
-
-	// get the number of columns of the DB table
-	int nCols = m_tableNames["ChildrenPositions"].size();
-        // JFB commented: qDebug() << "num of columns in childrenPos table" << nCols;
-
-    // loop over all children's positions stored in the DB
-	while (q.next()) {
-
-		childParams.clear();
-
-		QString parentId = q.value(1).toString();
-		QString parentTable = q.value(2).toString();
-		QString parentCopyNumber = q.value(3).toString();
-		unsigned int childPos = q.value(4).toUInt();
-
-		QString key = parentId + ":" + parentTable + ":" + parentCopyNumber;
-
-    for( int ii=0; ii<nCols; ++ii) {
-			childParams << q.value(ii).toString();
-    }
-
-		all_children[key][childPos] = childParams;
-	}
-	return all_children;
-}
-
-// Get all parent-children data from the database in one go
-std::vector<std::vector<std::string>> GMDBManager::getChildrenTableStd()
-{
-//  std::cout << "GMDBManager::getChildrenTableStd()\n"; // debug
+  // LogVols table
+  geoNode = "GeoLogVol";
+  tableName = "LogVols";
+  m_childType_tableName[geoNode] = tableName;
+  tab.push_back(tableName);
+  tab.push_back("id");
+  tab.push_back("name");
+  tab.push_back("shape");
+  tab.push_back("material");
+  storeTableColumnNames(tab);
+  queryStr = fmt::format("create table {0}({1} integer primary key, {2} varchar, {3} integer not null, {4} integer not null)", tab[0], tab[1], tab[2], tab[3], tab[4]);
+  if ( 0==(rc = execQuery(queryStr))) {
+    storeNodeType(geoNode, tableName); }
+  tab.clear();
   
-  QSqlQuery q;
-  QString queryStr = QString("SELECT * FROM ChildrenPositions ORDER BY parentTable, parentId, parentCopyNumber, position");
-  if (!q.prepare(queryStr)) {
-    showError(q.lastError());
-  }
-  q.exec();
+  // Materials table
+  geoNode = "GeoMaterial";
+  tableName = "Materials";
+  m_childType_tableName[geoNode] = tableName;
+  tab.push_back(tableName);
+  tab.push_back("id");
+  tab.push_back("name");
+  tab.push_back("density");
+  tab.push_back("elements");
+  storeTableColumnNames(tab);
+  queryStr = fmt::format("create table {0}({1} integer primary key, {2} varchar, {3} varchar, {4} varchar)", tab[0], tab[1], tab[2], tab[3], tab[4]);
+  if ( 0==(rc = execQuery(queryStr))) {
+    storeNodeType(geoNode, tableName); }
+  tab.clear();
   
-  std::vector<std::vector<std::string>> all_children; // to store all children
-  std::vector<std::string> childParams; // to temporarily store the children parameters
+  // Elements table
+  geoNode = "GeoElement";
+  tableName = "Elements";
+  m_childType_tableName[geoNode] = tableName;
+  tab.push_back(tableName);
+  tab.push_back("id");
+  tab.push_back("name");
+  tab.push_back("symbol");
+  tab.push_back("Z");
+  tab.push_back("A");
+  storeTableColumnNames(tab);
+  queryStr = fmt::format("create table {0}({1} integer primary key, {2} varchar, {3} varchar, {4} varchar, {5} varchar)", tab[0], tab[1], tab[2], tab[3], tab[4], tab[5]);
+  if ( 0==(rc = execQuery(queryStr))) {
+    storeNodeType(geoNode, tableName); }
+  tab.clear();
   
-  // get the number of columns of the DB table
-  int nCols = m_tableNames["ChildrenPositions"].size();
-  //  std::cout << "num of columns in childrenPos table" << nCols << std::endl; // debug
+  // Shapes table
+  geoNode = "GeoShape";
+  tableName = "Shapes";
+  m_childType_tableName[geoNode] = tableName;
+  tab.push_back(tableName);
+  tab.push_back("id");
+  tab.push_back("type");
+  tab.push_back("parameters");
+  storeTableColumnNames(tab);
+  queryStr = fmt::format("create table {0}({1} integer primary key, {2} varchar, {3} varchar)", tab[0], tab[1], tab[2], tab[3]);
+  if ( 0==(rc = execQuery(queryStr))) {
+    storeNodeType(geoNode, tableName); }
+  tab.clear();
   
-  // loop over all children's positions stored in the DB
-  while (q.next()) {
-    
-    childParams.clear();
-    
-    for( int ii=0; ii<nCols; ++ii) {
-      childParams.push_back( q.value(ii).toString().toStdString() );
-    }
-    
-//    // debug
-//    if(childParams[2]=="8920")
-//      std::cout << "CoolingTube parent:"; print childParams;
-    
-    all_children.push_back(childParams);
+  // SerialDenominators table
+  geoNode = "GeoSerialDenominator";
+  tableName = "SerialDenominators";
+  m_childType_tableName[geoNode] = tableName;
+  tab.push_back(tableName);
+  tab.push_back("id");
+  tab.push_back("baseName");
+  storeTableColumnNames(tab);
+  queryStr = fmt::format("create table {0}({1} integer primary key, {2} varchar)", tab[0], tab[1], tab[2]);
+  if ( 0==(rc = execQuery(queryStr))) {
+    storeNodeType(geoNode, tableName); }
+  tab.clear();
+
+  // Functions table
+  geoNode = "Function";
+  tableName = "Functions";
+  m_childType_tableName[geoNode] = tableName;
+  tab.push_back(tableName);
+  tab.push_back("id");
+  tab.push_back("expression");
+  storeTableColumnNames(tab);
+  queryStr = fmt::format("create table {0}({1} integer primary key, {2} varchar)", tab[0], tab[1], tab[2]);
+  if ( 0==(rc = execQuery(queryStr))) {
+    storeNodeType(geoNode, tableName); }
+  tab.clear();
+  
+  // SerialDenominators table
+  geoNode = "GeoSerialTransformer";
+  tableName = "SerialTransformers";
+  m_childType_tableName[geoNode] = tableName;
+  tab.push_back(tableName);
+  tab.push_back("id");
+  tab.push_back("funcId");
+  tab.push_back("volId");
+  tab.push_back("volTable");
+  tab.push_back("copies");
+  storeTableColumnNames(tab);
+  queryStr = fmt::format("create table {0}({1} integer primary key, {2} integer not null REFERENCES Functions(id), {3} integer not null, {4} integer not null REFERENCES GeoNodesTypes(id), {5} integer)", tab[0], tab[1], tab[2], tab[3], tab[4], tab[5]);
+  if ( 0==(rc = execQuery(queryStr))) {
+    storeNodeType(geoNode, tableName); }
+  tab.clear();
+  
+  // Transforms table
+  geoNode = "GeoTransform";
+  tableName = "Transforms";
+  m_childType_tableName[geoNode] = tableName;
+  tab.push_back(tableName);
+  tab.push_back("id");
+  tab.push_back("xx");
+  tab.push_back("xy");
+  tab.push_back("xz");
+  tab.push_back("yx");
+  tab.push_back("yy");
+  tab.push_back("yz");
+  tab.push_back("zx");
+  tab.push_back("zy");
+  tab.push_back("zz");
+  tab.push_back("dx");
+  tab.push_back("dy");
+  tab.push_back("dz");
+  storeTableColumnNames(tab);
+  queryStr = fmt::format("create table {0}({1} integer primary key, {2} real, {3} real, {4} real, {5} real, {6} real, {7} real, {8} real, {9} real, {10} real, {11} real, {12} real, {13} real)", tab[0], tab[1], tab[2], tab[3], tab[4], tab[5], tab[6], tab[7], tab[8], tab[9], tab[10], tab[11], tab[12], tab[13]);
+  if ( 0==(rc = execQuery(queryStr))) {
+    storeNodeType(geoNode, tableName); }
+  tab.clear();
+  
+  // Transforms table
+  geoNode = "GeoAlignableTransform";
+  tableName = "AlignableTransforms";
+  m_childType_tableName[geoNode] = tableName;
+  tab.push_back(tableName);
+  tab.push_back("id");
+  tab.push_back("xx");
+  tab.push_back("xy");
+  tab.push_back("xz");
+  tab.push_back("yx");
+  tab.push_back("yy");
+  tab.push_back("yz");
+  tab.push_back("zx");
+  tab.push_back("zy");
+  tab.push_back("zz");
+  tab.push_back("dx");
+  tab.push_back("dy");
+  tab.push_back("dz");
+  storeTableColumnNames(tab);
+  queryStr = fmt::format("create table {0}({1} integer primary key, {2} real, {3} real, {4} real, {5} real, {6} real, {7} real, {8} real, {9} real, {10} real, {11} real, {12} real, {13} real)", tab[0], tab[1], tab[2], tab[3], tab[4], tab[5], tab[6], tab[7], tab[8], tab[9], tab[10], tab[11], tab[12], tab[13]);
+  if ( 0==(rc = execQuery(queryStr))) {
+    storeNodeType(geoNode, tableName); }
+  tab.clear();
+  
+  // NameTags table
+  geoNode = "GeoNameTag";
+  tableName = "NameTags";
+  m_childType_tableName[geoNode] = tableName;
+  tab.push_back(tableName);
+  tab.push_back("id");
+  tab.push_back("name");
+  storeTableColumnNames(tab);
+  queryStr = fmt::format("create table {0}({1} integer primary key, {2} varchar)", tab[0], tab[1], tab[2]);
+  if ( 0==(rc = execQuery(queryStr))) {
+    storeNodeType(geoNode, tableName); }
+  tab.clear();
+  
+  if(m_debug) {
+    std::cout << "All these tables have been successfully created:" << std::endl; // debug
+    printAllDBTables();
   }
-  return all_children;
-}
-
-
-
-QVariant GMDBManager::getTableIdFromNodeType(QString nodeType)
-{
-	// JFB commented: qDebug() << "GMDBManager::getTableIdFromNodeType("<< nodeType <<")";
-	QSqlQuery q;
-	if (!q.prepare(QLatin1String("SELECT id FROM GeoNodesTypes WHERE nodeType = (?)"))) {
-		showError(q.lastError());
-		return QVariant();
-	}
-	q.addBindValue(nodeType);
-	q.exec();
-
-	QVariant id;
-	while (q.next()) {
-		id = q.value(0);
-		// JFB commented: qDebug() << "id:" << id;
-	}
-
-	return id;
-}
-
-QString GMDBManager::getTableNameFromNodeType(QString nodeType)
-{
-	// JFB commented: qDebug() << "GMDBManager::getTableNameFromNodeType("<< nodeType <<")";
-	QSqlQuery q;
-	if (!q.prepare(QLatin1String("SELECT tableName FROM GeoNodesTypes WHERE nodeType = (?)"))) {
-		showError(q.lastError());
-		return QString();
-	}
-	q.addBindValue(nodeType);
-	q.exec();
-
-	QString tableName;
-	while (q.next()) {
-		tableName = q.value(0).toString();
-		// JFB commented: qDebug() << "tableName:" << tableName;
-	}
-
-	return tableName;
-}
-
-// TODO: this and other methods could take data from in-memory maps, without asking to the DB all the times
-QString GMDBManager::getTableNameFromTableId(unsigned int tabId)
-{
-	QSqlQuery q;
-	if (!q.prepare(QLatin1String("SELECT tableName FROM GeoNodesTypes WHERE id = (?)"))) {
-		showError(q.lastError());
-		return QString();
-	}
-	q.addBindValue(tabId);
-	q.exec();
-
-	QString tableName;
-	while (q.next()) {
-		tableName = q.value(0).toString();
-		// JFB commented: qDebug() << "tableName:" << tableName;
-	}
 
-	return tableName;
-}
-std::string GMDBManager::getTableNameFromTableIdStd(unsigned int tabId)
-{
-  QSqlQuery q;
-  if (!q.prepare(QLatin1String("SELECT tableName FROM GeoNodesTypes WHERE id = (?)"))) {
-    showError(q.lastError());
-//    return QString();
-    exit(EXIT_FAILURE);
+  if(rc==0) {
+    return true;
   }
-  q.addBindValue(tabId);
-  q.exec();
-  
-  std::string tableName;
-  while (q.next()) {
-    tableName = q.value(0).toString().toStdString();
-  }
-  
-  return tableName;
+  return false;
 }
 
-QString GMDBManager::getNodeTypeFromTableId(unsigned int tabId)
-{
-	QSqlQuery q;
-	if (!q.prepare(QLatin1String("SELECT nodeType FROM GeoNodesTypes WHERE id = (?)"))) {
-		showError(q.lastError());
-		return QString();
-	}
-	q.addBindValue(tabId);
-	q.exec();
-
-	QString nodeType;
-	while (q.next()) {
-		nodeType = q.value(0).toString();
-	}
 
-	return nodeType;
-}
-std::string GMDBManager::getNodeTypeFromTableIdStd(unsigned int tabId)
+int GMDBManager::execQuery(std::string queryStr)
 {
-  QSqlQuery q;
-  if (!q.prepare(QLatin1String("SELECT nodeType FROM GeoNodesTypes WHERE id = (?)"))) {
-    showError(q.lastError());
-    // return QString();
-    exit(EXIT_FAILURE);
-  }
-  q.addBindValue(tabId);
-  q.exec();
-  
-  std::string nodeType;
-  while (q.next()) {
-    nodeType = q.value(0).toString().toStdString();
+  if(m_debug) std::cout << "queryStr to execute: " << queryStr << std::endl; // debug
+  checkIsDBOpen();
+  int result = -1;
+  if( (result = sqlite3_exec(m_dbSqlite, queryStr.c_str(), NULL, 0, &m_SQLiteErrMsg)) )
+  {
+    printf( "[ERR] : \t> CMD: %s , Error: %d\n" , queryStr.c_str() , result );
+    if ( m_SQLiteErrMsg )
+    {
+      printf( "[ERR] : Error msg: %s\n", m_SQLiteErrMsg );
+      sqlite3_free(m_SQLiteErrMsg);
+    }
   }
-  
-  return nodeType;
+  return result;
 }
 
-QHash<unsigned int, QString> GMDBManager::getAll_TableIDsNodeTypes()
-{
-	// JFB commented: qDebug() << "GMDBManager::getAll_TableIDsNodeTypes()";
 
-	QHash<unsigned int, QString> output;
 
-	QSqlQuery q = selectAllFromTable("GeoNodesTypes");
 
-	unsigned int id;
-	QString nodeType;
-	// QString tableName;
-	while (q.next()) {
-		id = q.value(0).toUInt();
-		nodeType = q.value(1).toString();
-		output[id] = nodeType;
-	}
-	return output;
-}
-
-std::unordered_map<unsigned int, std::string> GMDBManager::getAll_TableIDsNodeTypesStd()
+void GMDBManager::storeTableColumnNames(std::vector<std::string> input)
 {
-  // JFB commented: qDebug() << "GMDBManager::getAll_TableIDsNodeTypes()";
-  
-//  QHash<unsigned int, QString> output;
-  std::unordered_map<unsigned int, std::string> output;
-  
-  QSqlQuery q = selectAllFromTable("GeoNodesTypes");
-  
-  unsigned int id;
-  QString nodeType;
-  // QString tableName;
-  while (q.next()) {
-    id = q.value(0).toUInt();
-    nodeType = q.value(1).toString();
-    output[id] = nodeType.toStdString();
+  if (! (input.size() == 0) ) {
+    std::string tabName = input.at(0); // get the first element storing the name of the table
+    input.erase(input.begin()); // remove the first element storing the name of the table
+    m_tableNames[tabName] = input;
   }
-  return output;
 }
 
-QHash<QString, unsigned int> GMDBManager::getAll_NodeTypesTableIDs()
-{
-	// JFB commented: qDebug() << "GMDBManager::getAll_NodeTypesTableIDs()";
-
-	QHash<QString, unsigned int> output;
-
-	QSqlQuery q = selectAllFromTable("GeoNodesTypes");
 
-	unsigned int id;
-	QString nodeType;
-	// QString tableName;
-	while (q.next()) {
-		id = q.value(0).toUInt();
-		nodeType = q.value(1).toString();
-		output[nodeType] = id;
-	}
-	return output;
+std::vector<std::string> GMDBManager::getTableColumnNames(const std::string &tableName)
+{
+	return m_tableNames.at(tableName);
 }
 
-std::unordered_map<std::string, unsigned int> GMDBManager::getAll_NodeTypesTableIDsStd()
+void GMDBManager::storeNodeType(std::string nodeType, std::string tableName)
 {
-//  QHash<QString, unsigned int> output;
-  std::unordered_map<std::string, unsigned int> output;
-  
-  QSqlQuery q = selectAllFromTable("GeoNodesTypes");
-  
-  unsigned int id;
-  QString nodeType;
-  // QString tableName;
-  while (q.next()) {
-    id = q.value(0).toUInt();
-    nodeType = q.value(1).toString();
-    output[nodeType.toStdString()] = id;
+  checkIsDBOpen();
+  std::string queryStr;
+  sqlite3_stmt * st = nullptr;
+  int rc = -1;
+  // preparing the SQL query
+  std::string sql = "INSERT INTO GeoNodesTypes(nodeType, tableName) VALUES(?, ?)";
+  rc = sqlite3_prepare_v2( m_dbSqlite, sql.c_str(), -1, &st, NULL);
+  if (rc != SQLITE_OK) {
+    printf( "[SQLite ERR] (%s) : Error msg: %s\n", __func__, sqlite3_errmsg(m_dbSqlite) );
+    exit(EXIT_FAILURE);
+  }
+  if(m_debug) std::cout << "storeNodeType - Query string:" << sql << std::endl; // debug
+  // bind the parameters
+  rc = sqlite3_bind_text(st, 1, nodeType.c_str(), nodeType.length(), SQLITE_TRANSIENT);
+  rc = sqlite3_bind_text(st, 2, tableName.c_str(), tableName.length(), SQLITE_TRANSIENT);
+  // execute the query
+  rc = sqlite3_step( st );
+  if (rc != SQLITE_DONE) {
+    printf( "[SQLite ERR] (%s) : Error msg: %s\n", __func__, sqlite3_errmsg(m_dbSqlite) );
+    exit(EXIT_FAILURE);
   }
-  return output;
+  // finalize
+  sqlite3_finalize(st);
+  return;
 }
 
 
-QSqlQuery GMDBManager::selectAllFromTable(QString tableName) const
-{
+// TODO: move to sqlite3 library
+//void GMDBManager::loadTestData()
+//{
+//  // create test data
 //  QSqlQuery q;
+//  //
+//  //  qDebug() << "Loading Shapes...";
+//  //  if (!q.prepare(QLatin1String("insert into Shapes(name) values(?)"))) {
+//  //    showError(q.lastError());
+//  //    return;
+//  //  }
+//  QVariant boxId    = addShape(QLatin1String("Box"), "");
+//  QVariant coneId   = addShape(QLatin1String("Cone"), "");
+//  QVariant sphereId = addShape(QLatin1String("Sphere"), "");
 //
-//  QString queryStr = QString("SELECT * FROM %1 ORDER BY id");
-//  queryStr = queryStr.arg(tableName);
+//  qWarning() << "Sample GeoElement data --> to be implemented!";
 //
-//  if (!q.prepare(queryStr)) {
-//    showError(q.lastError());
-//    return QSqlQuery();
-//  }
-//  q.exec();
-//  return q;
-  
-  return selectAllFromTableSortBy(tableName, "id");
-}
+//  QVariant airId  = addMaterial(QLatin1String("Air"),QLatin1String("density"),QLatin1String("elements"));
+//  QVariant ironId = addMaterial(QLatin1String("Iron"),QLatin1String("density"),QLatin1String("elements"));
+//  QVariant leadId = addMaterial(QLatin1String("Lead"),QLatin1String("density"),QLatin1String("elements"));
+//
+//  QVariant worldLogId = addLogVol(QLatin1String("WorldLog"), boxId, airId);
+//  QVariant toyLogId  = addLogVol(QLatin1String("ToyLog"), coneId, leadId);
+//
+//  QVariant rootPhysId = addPhysVol(worldLogId, QVariant()); // no parent
+//  QVariant childPhysId = addPhysVol(toyLogId, rootPhysId);
+//}
+
 
-QSqlQuery GMDBManager::selectAllFromTableSortBy(QString tableName, std::string sortColumn) const
+
+bool GMDBManager::storeRootVolume(const unsigned int &id, const std::string &nodeType)
 {
-  QSqlQuery q;
-  
-  if ("" == sortColumn || 0 == sortColumn.size()) {
-    sortColumn = "id";
-  }
-  QString qsortColumn = QString::fromStdString(sortColumn);
+  checkIsDBOpen();
   
-  QStringList args;
-  args << tableName << qsortColumn;
-  
-  QString queryStr = QString("SELECT * FROM %1 ORDER BY %2");
-//  queryStr = queryStr.arg(tableName);
-  for (int i=0; i < args.size(); ++i) {
-    queryStr = queryStr.arg( args.at(i) );
+  std::string tableName = "RootVolume";
+	const unsigned int typeId = getTableIdFromNodeType(nodeType);
+  std::vector<std::string> cols = getTableColumnNames(tableName);
+
+  // set the SQL query
+  std::string queryStr;
+  std::string sql = fmt::format("INSERT INTO {0} ({1}, {2}) VALUES (?, ?)", tableName, cols[1], cols[2]);
+  // preparing the SQL query
+  sqlite3_stmt * st = nullptr;
+  int rc = -1;
+  rc = sqlite3_prepare_v2( m_dbSqlite, sql.c_str(), -1, &st, NULL);
+  if (rc != SQLITE_OK) {
+    printf( "[SQLite ERR] (%s) : Error msg: %s\n", __func__, sqlite3_errmsg(m_dbSqlite) ); // TODO: add __func__ to all error messages, as I did here
+    exit(EXIT_FAILURE);
   }
-  
-  if (!q.prepare(queryStr)) {
-    showError(q.lastError());
-    return QSqlQuery();
+  if(m_debug) std::cout << "Query string:" << sql << std::endl; // debug
+  // bind the parameters
+  rc = sqlite3_bind_int(st, 1, id);
+  rc = sqlite3_bind_int(st, 2, typeId);
+  // execute the query
+  rc = sqlite3_step( st );
+  if (rc != SQLITE_DONE) {
+    printf( "[SQLite ERR] (%s) : Error msg: %s\n", __func__, sqlite3_errmsg(m_dbSqlite) );
+    exit(EXIT_FAILURE);
   }
-  q.exec();
-  return q;
-}
-
-
-
-bool GMDBManager::initDB()
-{
-	// JFB commented: qDebug() << "GMDBManager::initDB()";
-
-
-	// check if DB is empty
-	// JFB commented: qDebug() << "checking existing tables...";
-	QSqlDatabase db = QSqlDatabase::database();
-	QStringList tables = db.tables();
-	if (   tables.contains("LogVols",   Qt::CaseInsensitive)
-			|| tables.contains("PhysVols",  Qt::CaseInsensitive)
-			|| tables.contains("Materials", Qt::CaseInsensitive)
-			|| tables.contains("Elements",  Qt::CaseInsensitive)
-			|| tables.contains("Shapes",    Qt::CaseInsensitive)
-	) {
-		// JFB commented: qDebug() << "tables are present already. Skipping tables creation. Loading tables...";
-		loadTableNamesFromDB();
-		return true;
-	}
-
-  // TODO: we should check if all needed tables are present; if not throw an error message and exit.
-
-  // TODO: we should create tables only if the DB is really completely empty!
-	// if DB is empty, then create tables
-	// JFB commented: qDebug() << "DB file is empty. Creating tables...";
-	bool tablesOK = createTables();
+  // finalize
+  sqlite3_finalize(st);
 
-	// store DB version
-	// JFB commented: qDebug() << "Storing DB version:" << QString::fromStdString(dbversion);
-	addDBversion(QString::fromStdString(dbversion));
-
-	return tablesOK;
-
-}
-
-bool GMDBManager::createTables()
-{
-	// JFB commented: qDebug() << "GMDBManager::createTables()";
-
-	QStringList tab;
-	QSqlQuery q;
-	QString geoNode;
-	QString tableName;
-	QString queryStr;
-
-	// create a table to store the relation between the types of GeoNodes and the name of the table
-	tableName = "dbversion";
-	tab << tableName << "id" << "version";
-	queryStr = QString("create table %1(%2 integer primary key, %3 integer)");
-	for (int i=0; i < tab.size(); ++i) {
-		queryStr = queryStr.arg( tab.at(i) );
-	}
-	//qDebug() << "query:" << queryStr;
-	if (!q.exec(queryStr)) {
-		showError(q.lastError());
-		return false;
-	}
-	storeTableColumnNames(tab);
-	tab.clear();
-
-	// create a table to store the relation between the types of GeoNodes and the name of the table
-	tableName = "GeoNodesTypes";
-	tab << tableName << "id" << "nodeType" << "tableName";
-	storeTableColumnNames(tab);
-	tab.clear();
-	if (!q.exec(QLatin1String("create table GeoNodesTypes(id integer primary key, nodeType varchar, tableName varchar)"))) {
-		showError(q.lastError());
-		return false;
-	}
-
-	// ChildrenPositions table
-	tableName = "ChildrenPositions";
-	tab << tableName << "id" << "parentId" << "parentTable" << "parentCopyNumber" << "position" << "childTable" << "childId" << "childCopyNumber";
-	queryStr = QString("create table %1(%2 integer primary key, %3 integer, %4 integer not null REFERENCES GeoNodesTypes(id), %5 integer, %6 integer, %7 integer not null REFERENCES GeoNodesTypes(id), %8 integer not null, %9 integer)"); // FIXME: add "NOT NULL" to copy numbers
-	for (int i=0; i < tab.size(); ++i) {
-		queryStr = queryStr.arg( tab.at(i) );
-	}
-	// qDebug() << "query:" << queryStr;
-	if (!q.exec(queryStr)) {
-		showError(q.lastError());
-		return false;
-	}
-	storeTableColumnNames(tab);
-	tab.clear();
-
-
-	// RootVolume table
-	tableName = "RootVolume";
-	tab << tableName << "id" << "volId" << "volTable";
-	queryStr = QString("create table %1(%2 integer primary key, %3 integer not null, %4 integer not null REFERENCES GeoNodesTypes(id))");
-	for (int i=0; i < tab.size(); ++i) {
-		queryStr = queryStr.arg( tab.at(i) );
-	}
-	//qDebug() << "query:" << queryStr;
-	if (!q.exec(queryStr)) {
-		showError(q.lastError());
-		return false;
-	}
-	storeTableColumnNames(tab);
-	tab.clear();
-
-
-	// PhysVols table
-	geoNode = "GeoPhysVol";
-	tableName = "PhysVols";
-	tab << tableName << "id" << "logvol" << "parent"; // FIXME: remove "parent" field, it is not used anymore and it's not reliable since it's not using the tableID.
-	storeTableColumnNames(tab);
-	tab.clear();
-	m_childType_tableName[geoNode] = tableName; // store type-table relation
-	if (!q.exec(QLatin1String("create table PhysVols(id integer primary key, logvol integer not null, parent integer)"))) {
-		showError(q.lastError());
-		return false;
-	} else {
-		storeNodeType(geoNode, tableName);
-	}
-
-	// FullPhysVols table
-	geoNode = "GeoFullPhysVol";
-	tableName = "FullPhysVols";
-	tab << tableName << "id" << "logvol" << "parent";
-	storeTableColumnNames(tab);
-	tab.clear();
-	m_childType_tableName[geoNode] = tableName; // store type-table relation
-	if (!q.exec(QLatin1String("create table FullPhysVols(id integer primary key, logvol integer not null, parent integer)"))) {
-		showError(q.lastError());
-		return false;
-	} else {
-		storeNodeType(geoNode, tableName);
-	}
-
-	// LogVols table
-	geoNode = "GeoLogVol";
-	tableName = "LogVols";
-	tab << tableName << "id" << "name" << "shape" << "material";
-	storeTableColumnNames(tab);
-	tab.clear();
-	m_childType_tableName[geoNode] = tableName; // store type-table relation
-	if (!q.exec(QLatin1String("create table LogVols(id integer primary key, name varchar, shape integer not null, material integer not null)"))) {
-		showError(q.lastError());
-		return false;
-	} else {
-		storeNodeType(geoNode, tableName);
-	}
-
-	// Materials table
-	geoNode = "GeoMaterial";
-	tableName = "Materials";
-	tab << tableName << "id" << "name" << "density" << "elements";
-	storeTableColumnNames(tab);
-	tab.clear();
-	m_childType_tableName[geoNode] = tableName; // store type-table relation
-	if (!q.exec(QLatin1String("create table Materials(id integer primary key, name varchar, density varchar, elements varchar)"))) {
-		showError(q.lastError());
-		return false;
-	} else {
-		storeNodeType(geoNode, tableName);
-	}
-
-	// Elements table
-	geoNode = "GeoElement";
-	tableName = "Elements";
-	tab << tableName << "id" << "name" << "symbol" << "Z" << "A";
-
-	storeTableColumnNames(tab);
-	tab.clear();
-	m_childType_tableName[geoNode] = tableName; // store type-table relation
-	if (!q.exec(QLatin1String("create table Elements(id integer primary key, name varchar, symbol varchar, Z varchar, A varchar)"))) {
-		showError(q.lastError());
-		return false;
-	} else {
-		storeNodeType(geoNode, tableName);
-	}
-
-	// Shapes table
-	geoNode = "GeoShape";
-	tableName = "Shapes";
-	tab << tableName << "id" << "type" << "parameters";
-	storeTableColumnNames(tab);
-	tab.clear();
-	m_childType_tableName[geoNode] = tableName; // store type-table relation
-	if (!q.exec(QLatin1String("create table Shapes(id integer primary key, type varchar, parameters varchar)"))) {
-		showError(q.lastError());
-		return false;
-	} else {
-		storeNodeType(geoNode, tableName);
-	}
-
-	// SerialDenominators table
-	geoNode = "GeoSerialDenominator";
-	tableName = "SerialDenominators";
-	tab << tableName << "id" << "baseName";
-	storeTableColumnNames(tab);
-	tab.clear();
-	m_childType_tableName[geoNode] = tableName; // store type-table relation
-	if (!q.exec(QLatin1String("create table SerialDenominators(id integer primary key, baseName varchar)"))) {
-		showError(q.lastError());
-		return false;
-	} else {
-		storeNodeType(geoNode, tableName);
-	}
-
-	// Functions table
-	geoNode = "Function";
-	tableName = "Functions";
-	tab << tableName << "id" << "expression";
-	storeTableColumnNames(tab);
-	tab.clear();
-	m_childType_tableName[geoNode] = tableName; // store type-table relation
-	if (!q.exec(QLatin1String("create table Functions(id integer primary key, expression varchar)"))) {
-		showError(q.lastError());
-		return false;
-	} else {
-		storeNodeType(geoNode, tableName);
-	}
-
-	// SerialDenominators table
-	geoNode = "GeoSerialTransformer";
-	tableName = "SerialTransformers";
-	m_childType_tableName[geoNode] = tableName; // store type-table relation
-	tab << tableName << "id" << "funcId" << "volId" << "volTable" << "copies";
-	storeTableColumnNames(tab);
-	queryStr = QString("create table %1(%2 integer primary key, %3 integer not null REFERENCES Functions(id), %4 integer not null, %5 integer not null REFERENCES GeoNodesTypes(id), %6 integer)");
-	for (int i=0; i < tab.size(); ++i) {
-		queryStr = queryStr.arg( tab.at(i) );
-	}
-	tab.clear();
-	// qDebug() << "query:" << queryStr;
-	if (!q.exec(queryStr)) {
-		showError(q.lastError());
-		return false;
-	} else {
-		storeNodeType(geoNode, tableName);
-	}
-
-
-	// Transforms table
-	geoNode = "GeoTransform";
-	tableName = "Transforms";
-	tab << tableName << "id" << "xx" << "xy" << "xz" << "yx" << "yy" << "yz" << "zx" << "zy" << "zz" << "dx" << "dy" << "dz";
-	storeTableColumnNames(tab);
-	tab.clear();
-	m_childType_tableName[geoNode] = tableName; // store type-table relation
-	if (!q.exec(QLatin1String("create table Transforms(id integer primary key, xx real, xy real, xz real, yx real, yy real, yz real, zx real, zy real, zz real, dx real, dy real, dz real)"))) {
-		showError(q.lastError());
-		return false;
-	} else {
-		storeNodeType(geoNode, tableName);
-	}
-
-	// AlignableTransforms table
-	geoNode = "GeoAlignableTransform";
-	tableName = "AlignableTransforms";
-	tab << tableName << "id" << "xx" << "xy" << "xz" << "yx" << "yy" << "yz" << "zx" << "zy" << "zz" << "dx" << "dy" << "dz";
-	storeTableColumnNames(tab);
-	tab.clear();
-	m_childType_tableName[geoNode] = tableName; // store type-table relation
-	if (!q.exec(QLatin1String("create table AlignableTransforms(id integer primary key, xx real, xy real, xz real, yx real, yy real, yz real, zx real, zy real, zz real, dx real, dy real, dz real)"))) {
-		showError(q.lastError());
-		return false;
-	} else {
-		storeNodeType(geoNode, tableName);
-	}
-
-	// NameTags table
-	geoNode = "GeoNameTag";
-	tableName = "NameTags";
-	m_childType_tableName[geoNode] = tableName; // store type-table relation
-	tab << tableName << "id" << "name";
-	storeTableColumnNames(tab);
-	queryStr = QString("create table %1(%2 integer primary key, %3 varchar)");
-	for (int i=0; i < tab.size(); ++i) {
-		queryStr = queryStr.arg( tab.at(i) );
-	}
-	tab.clear();
-	// qDebug() << "query:" << queryStr;
-	if (!q.exec(queryStr)) {
-		showError(q.lastError());
-		return false;
-	} else {
-		storeNodeType(geoNode, tableName);
-	}
-
-    // JFB commented: qDebug() << "m_childType_tableName:" << m_childType_tableName;
-	// JFB commented: qDebug() << "DONE. All tables created successfully.";
+  if (rc != 0) {
+    return false;
+  }
 	return true;
-
-}
-
-void GMDBManager::loadTableNamesFromDB()
-{
-	// JFB commented: qDebug() << "GMDBManager::loadTableNames()";
-	QSqlDatabase db = QSqlDatabase::database();
-	QStringList tables = db.tables();
-	foreach (QString tab, tables) {
-		QStringList tabColNames = getTableColNamesFromDB(tab);
-		storeTableColumnNames(tabColNames);
-	}
-	// qDebug() << "m_tableNames:" << m_tableNames;
-}
-
-void GMDBManager::storeTableColumnNames(QStringList input)
-{
-	//	qDebug() << "GMDBManager::storeTableColumnNames()";
-	if (! (input.isEmpty()) ) {
-		QString tabName = input.takeFirst();
-		m_tableNames[tabName] = input;
-	}
-}
-
-QStringList GMDBManager::getTableColumnNames(QString tableName)
-{
-	//	qDebug() << "GMDBManager::getTableColumnNames()";
-	return m_tableNames[tableName];
-}
-
-void GMDBManager::storeNodeType(QString nodeType, QString tableName)
-{
-	// JFB commented: qDebug() << "GMDBManager::storeNodeType()";
-
-	QSqlQuery q;
-	if (!q.prepare(QLatin1String("insert into GeoNodesTypes(nodeType, tableName) values(?, ?)"))) {
-		showError(q.lastError());
-		return;
-	}
-
-	q.addBindValue(nodeType);
-	q.addBindValue(tableName);
-	q.exec();
-	return;
 }
 
-void GMDBManager::loadTestData()
-{
-
-	// JFB commented: qDebug() << "GMDBManager::loadTestData()";
-
-	// create test data
-	QSqlQuery q;
-	//
-	//	qDebug() << "Loading Shapes...";
-	//	if (!q.prepare(QLatin1String("insert into Shapes(name) values(?)"))) {
-	//		showError(q.lastError());
-	//		return;
-	//	}
-	// JFB commented: qDebug() << "Loading Shapes...";
-	QVariant boxId    = addShape(QLatin1String("Box"), "");
-	QVariant coneId   = addShape(QLatin1String("Cone"), "");
-	QVariant sphereId = addShape(QLatin1String("Sphere"), "");
-
-	// JFB commented: qDebug() << "Loading Elements...";
-	qWarning() << "Sample GeoElement data --> to be implemented!";
-
-	// JFB commented: qDebug() << "Loading Materials...";
-	QVariant airId  = addMaterial(QLatin1String("Air"),QLatin1String("density"),QLatin1String("elements"));
-	QVariant ironId = addMaterial(QLatin1String("Iron"),QLatin1String("density"),QLatin1String("elements"));
-	QVariant leadId = addMaterial(QLatin1String("Lead"),QLatin1String("density"),QLatin1String("elements"));
-
-	// JFB commented: qDebug() << "Loading LogVols...";
-	QVariant worldLogId = addLogVol(QLatin1String("WorldLog"), boxId, airId);
-	QVariant toyLogId  = addLogVol(QLatin1String("ToyLog"), coneId, leadId);
-
-	// JFB commented: qDebug() << "Loading PhysVols...";
-	QVariant rootPhysId = addPhysVol(worldLogId, QVariant()); // no parent
-	QVariant childPhysId = addPhysVol(toyLogId, rootPhysId);
-
-	// JFB commented: qDebug() << "DONE. Created and loaded test data.";
-}
 
 
-QStringList GMDBManager::getTableColNamesFromDB(QString tableName) const
-{
-	QStringList colNames;
-	colNames << tableName;
-
-	// fetch the driver
-	QSqlDriver* driver = m_db.driver();
-	QSqlRecord record = driver->record(tableName);
-	int colN = record.count();
-	for (int i = 0; i < colN; ++i)
-		colNames << record.fieldName(i);
-	return colNames;
-}
-
-
-
-void GMDBManager::printTableColNamesFromDB(QString tableName) const
-{
-	// JFB commented: qDebug() << "GMDBManager::printTableColNames:" << tableName;
-
-	// fetch the driver
-	// JFB commented: qDebug() << getTableColNamesFromDB(tableName);
-}
-
-bool GMDBManager::storeRootVolume(QVariant id, QString nodeType)
-{
-	// JFB commented: qDebug() << "GMDBManager::storeRootVolume:" << id << nodeType;
-
-	QVariant typeId = getTableIdFromNodeType(nodeType);
-
-	QString tableName = "RootVolume";
-	QStringList cols = getTableColumnNames(tableName);
-
-	// prepare the query string
-	QString queryStr = QString("insert into %1(%2, %3) values(?, ?)").arg(tableName);
-	for (int i=0; i < cols.size(); ++i) {
-		if (i != 0) // skip the "id" column
-			queryStr = queryStr.arg( cols.at(i) );
-	}
-	//qDebug() << "queryStr:" << queryStr;
-	// prepare the query
-	QSqlQuery q;
-	if (!q.prepare(queryStr)) {
-		showError(q.lastError());
-		return false;
-	}
-	q.addBindValue(id);
-	q.addBindValue(typeId);
-	// run the query
-	q.exec();
-	return true;
-}
-
-QStringList GMDBManager::getRootPhysVol()
-{
-  //JFB commented: qDebug() << "GMDBManager::getRootPhysVol()";
-	// get the ID of the ROOT vol from the table "RootVolume"
-	// QSqlQuery query("SELECT * FROM RootVolume");
-	QSqlQuery query = selectAllFromTable("RootVolume");
-
-	QVariant id;
-	QVariant typeId;
-	while (query.next()) {
-		id = query.value(1);
-		typeId = query.value(2);
-	}
-	return getItemAndType(typeId.toUInt(), id.toUInt());
-}
-
-std::vector<std::string> GMDBManager::getRootPhysVolStd()
+std::vector<std::string> GMDBManager::getRootPhysVol()
 {
   // get the ID of the ROOT vol from the table "RootVolume"
-  QSqlQuery query = selectAllFromTable("RootVolume");
-  
+  sqlite3_stmt* stmt = selectAllFromTable("RootVolume");
+  // declare the data we want to fetch
   unsigned int id;
   unsigned int typeId;
-  while (query.next()) {
-    id = query.value(1).toUInt();
-    typeId = query.value(2).toUInt();
+  // execute the statement on all rows
+  int rc = -1;
+  while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
+    // get the data
+    id = sqlite3_column_int(stmt, 0);
+    typeId = sqlite3_column_int(stmt, 1);
+    // TODO: fill a cache
+  }
+  if (rc != SQLITE_DONE) {
+    std::string errmsg(sqlite3_errmsg(m_dbSqlite));
+    sqlite3_finalize(stmt);
+    throw errmsg;
   }
-  return getItemAndTypeStd(typeId, id);
+  // finalize
+  sqlite3_finalize(stmt);
+  
+  return getItemAndType(typeId, id);
 }
 
 
-QString GMDBManager::getDBFilePath()
+std::string GMDBManager::getDBFilePath()
 {
     return m_dbpath;
 }
 
+// FIXME: TODO: move to an utility class
+int lastIndexOf(std::vector<std::string> v, std::string str, int pos = 0) {
+  auto it = std::find(std::next(v.rbegin(), v.size() - pos), v.rend(), str);
+  if (it != v.rend())
+  {
+    auto idx = std::distance(v.begin(), it.base() - 1);
+//    std::cout << idx << std::endl;
+    return idx;
+  }
+  return -1; // no item matched
+}
 
-int GMDBManager::getTableColIndex(QString tableName, QString colName)
+int GMDBManager::getTableColIndex(const std::string &tableName, const std::string &colName)
 {
-	//qDebug() << "GMDBManager::getTableColIndex()";
-	QStringList colFields = m_tableNames[tableName];
-	return colFields.lastIndexOf(colName);
+  std::vector<std::string> colFields = m_tableNames.at(tableName);
+	return lastIndexOf(colFields, colName);
 }
diff --git a/GeoModelRead/src/ReadGeoModel.cpp b/GeoModelRead/src/ReadGeoModel.cpp
index 47c8868a028f08df133eee8ff466225a89f5ee9e..626bad84a2e69fd6e85059f2d7510be6b8d1bbac 100644
--- a/GeoModelRead/src/ReadGeoModel.cpp
+++ b/GeoModelRead/src/ReadGeoModel.cpp
@@ -92,50 +92,53 @@ ReadGeoModel::ReadGeoModel(GMDBManager* db, unsigned long* progress) : m_deepDeb
   m_debug(false), m_timing(false), m_runMultithreaded(false),
   m_runMultithreaded_nThreads(0), m_progress(nullptr)
 {
-  // Check if the user asked for running in serial or multi-threading mode
-  if ( "" != getEnvVar("GEOMODEL_ENV_IO_DEBUG")) {
+  // Check if the user asked for debug messages
+  if ( "" != getEnvVar("GEOMODEL_ENV_IO_READ_DEBUG")) {
     m_debug = true;
     std::cout << "You defined the GEOMODEL_IO_DEBUG variable, so you will see a verbose output." << std::endl;
   }
-  // Check if the user asked for running in serial or multi-threading mode
+  // Check if the user asked for verbose debug messages
   if ( "" != getEnvVar("GEOMODEL_ENV_IO_DEBUG_VERBOSE")) {
     m_deepDebug = true;
-    std::cout << "You defined the GEOMODEL_IO_DEBUG_VERBOSE variable, so you will see a verbose output." << std::endl;
+    std::cout << "You defined the GEOMODEL_IO_READ_DEBUG_VERBOSE variable, so you will see a verbose output." << std::endl;
   }
-  // Check if the user asked for running in serial or multi-threading mode
-  if ( "" != getEnvVar("GEOMODEL_ENV_IO_TIMING")) {
+  // Check if the user asked for timing output
+  if ( "" != getEnvVar("GEOMODEL_ENV_IO_READ_TIMING")) {
     m_timing = true;
-    std::cout << "You defined the GEOMODEL_IO_TIMING variable, so you will see a timing measurement in the output." << std::endl;
+    std::cout << "You defined the GEOMODEL_IO_READ_TIMING variable, so you will see a timing measurement in the output." << std::endl;
   }
 
 	if ( progress != nullptr) {
 	m_progress = progress;
 	}
 
-	// set the geometry file
+	// open the geometry file
 	m_dbManager = db;
-	if (m_dbManager->isOpen()) {
-    if (m_deepDebug) std::cout << "OK! Database is open!";
+	if (m_dbManager->checkIsDBOpen()) {
+    if (m_debug) std::cout << "OK! Database is open!";
 	}
 	else {
     std::cout << "ERROR!! Database is NOT open!";
 		return;
 	}
+  // build caches
+  m_dbManager->loadGeoNodeTypesAndBuildCache();
+  
 
   // Check if the user asked for running in serial or multi-threading mode
   if ( "" != getEnvVar("GEOMODEL_ENV_IO_NTHREADS"))
   {
     int nThreads = std::stoi(getEnvVar("GEOMODEL_ENV_IO_NTHREADS"));
     if (nThreads == 0) {
-      std::cout << "\nYou set the GEOMODEL_ENV_IO_NTHREADS to " << nThreads << "; thus, GeoModelIO will be run in serial mode." << std::endl;
+      std::cout << "Info: You set the GEOMODEL_ENV_IO_NTHREADS to '" << nThreads << "'; thus, GeoModelIO will be run in serial mode." << std::endl;
       m_runMultithreaded_nThreads = nThreads;
       m_runMultithreaded = false;
     } else if( nThreads > 0 ) {
-      std::cout << "\nYou set the GEOMODEL_ENV_IO_NTHREADS to " << nThreads << "; thus, GeoModelIO will use that number of worker threads." << std::endl;
+      std::cout << "Info: You set the GEOMODEL_ENV_IO_NTHREADS to '" << nThreads << "'; thus, GeoModelIO will use that number of worker threads." << std::endl;
       m_runMultithreaded_nThreads = nThreads;
       m_runMultithreaded = true;
     } else if (nThreads == -1) {
-      std::cout << "\nYou set the GEOMODEL_ENV_IO_NTHREADS to " << nThreads << "; thus, GeoModelIO will use the number of threads supported by the platform." << std::endl;
+      std::cout << "Info: You set the GEOMODEL_ENV_IO_NTHREADS to '" << nThreads << "'; thus, GeoModelIO will use the number of threads supported by the platform." << std::endl;
       m_runMultithreaded_nThreads = nThreads;
       m_runMultithreaded = true;
     }
@@ -151,7 +154,7 @@ ReadGeoModel::~ReadGeoModel() {
   // FIXME: some cleaning...??
 }
 
-  
+  // FIXME: TODO: move to an utility class
 std::string ReadGeoModel::getEnvVar( std::string const & key ) const
 {
     char * val = std::getenv( key.c_str() );
@@ -180,41 +183,41 @@ GeoPhysVol* ReadGeoModel::buildGeoModel()
 GeoPhysVol* ReadGeoModel::buildGeoModelPrivate()
 {
   // *** get all data from the DB ***
-
+  std::chrono::system_clock::time_point start = std::chrono::system_clock::now(); // timing: get start time
 	// get all GeoModel nodes from the DB
-	m_logVols = m_dbManager->getTableFromNodeTypeStd("GeoLogVol");
-	m_shapes = m_dbManager->getTableFromNodeTypeStd("GeoShape");
-	m_materials = m_dbManager->getTableFromNodeTypeStd("GeoMaterial");
-	m_elements = m_dbManager->getTableFromNodeTypeStd("GeoElement");
-	m_functions = m_dbManager->getTableFromNodeTypeStd("Function");
-  m_physVols = m_dbManager->getTableFromNodeTypeStd("GeoPhysVol");
-  m_fullPhysVols = m_dbManager->getTableFromNodeTypeStd("GeoFullPhysVol");
-  m_transforms = m_dbManager->getTableFromNodeTypeStd("GeoTransform");
-  m_alignableTransforms = m_dbManager->getTableFromNodeTypeStd("GeoAlignableTransform");
-  m_serialDenominators = m_dbManager->getTableFromNodeTypeStd("GeoSerialDenominator");
-  m_serialTransformers = m_dbManager->getTableFromNodeTypeStd("GeoSerialTransformer");
-  m_nameTags = m_dbManager->getTableFromNodeTypeStd("GeoNameTag");
-  
+	m_logVols = m_dbManager->getTableFromNodeType("GeoLogVol");
+	m_shapes = m_dbManager->getTableFromNodeType("GeoShape");
+	m_materials = m_dbManager->getTableFromNodeType("GeoMaterial");
+	m_elements = m_dbManager->getTableFromNodeType("GeoElement");
+	m_functions = m_dbManager->getTableFromNodeType("Function");
+  m_physVols = m_dbManager->getTableFromNodeType("GeoPhysVol");
+  m_fullPhysVols = m_dbManager->getTableFromNodeType("GeoFullPhysVol");
+  m_transforms = m_dbManager->getTableFromNodeType("GeoTransform");
+  m_alignableTransforms = m_dbManager->getTableFromNodeType("GeoAlignableTransform");
+  m_serialDenominators = m_dbManager->getTableFromNodeType("GeoSerialDenominator");
+  m_serialTransformers = m_dbManager->getTableFromNodeType("GeoSerialTransformer");
+  m_nameTags = m_dbManager->getTableFromNodeType("GeoNameTag");
   // get the children table from DB
-  m_allchildren = m_dbManager->getChildrenTableStd();
-  
+  m_allchildren = m_dbManager->getChildrenTable();
 	// get the root volume data
-//  m_root_vol_data = m_dbManager->getRootPhysVol();
-  m_root_vol_data = m_dbManager->getRootPhysVolStd();
-
+  m_root_vol_data = m_dbManager->getRootPhysVol();
   // get DB metadata
-  m_tableID_toTableName = m_dbManager->getAll_TableIDsNodeTypesStd();
-  m_tableName_toTableID = m_dbManager->getAll_NodeTypesTableIDsStd();
-
+  m_tableID_toTableName = m_dbManager->getAll_TableIDsNodeTypes();
+  m_tableName_toTableID = m_dbManager->getAll_NodeTypesTableIDs();
+  
+  auto end = std::chrono::system_clock::now(); // timing: get end time
+  auto diff = std::chrono::duration_cast < std::chrono::seconds > (end - start).count();
+  if (m_timing || m_debug || m_deepDebug) {
+    std::cout << "*** Time taken to fetch GeoModel data from the database: " << diff << " [s]" << std::endl;
+  }
 
   // *** build all nodes ***
-
+  start = std::chrono::system_clock::now(); // timing: get start time
   // parallel mode:
-  
   if (m_runMultithreaded) {
   std::cout << "Building nodes concurrently..." << std::endl;
   std::thread t2(&ReadGeoModel::buildAllElements, this);
-//  std::thread t7(&ReadGeoModel::buildAllFunctions, this);
+    //  std::thread t7(&ReadGeoModel::buildAllFunctions, this); // FIXME: implement cache for Functions
   
   std::thread t8(&ReadGeoModel::buildAllTransforms, this);
   std::thread t9(&ReadGeoModel::buildAllAlignableTransforms, this);
@@ -266,7 +269,21 @@ GeoPhysVol* ReadGeoModel::buildGeoModelPrivate()
     buildAllFullPhysVols();
     buildAllSerialTransformers();
   }
+  end = std::chrono::system_clock::now(); // timing: get end time
+  diff = std::chrono::duration_cast < std::chrono::seconds > (end - start).count();
+  if (m_timing || m_debug || m_deepDebug) {
+    std::cout << "*** Time taken to build all GeoModel nodes: " << diff << " [s]" << std::endl;
+  }
+  
+  // *** recreate all mother-daughter relatioships between nodes ***
+  start = std::chrono::system_clock::now(); // timing: get start time
   loopOverAllChildrenInBunches();
+  end = std::chrono::system_clock::now(); // timing: get end time
+  diff = std::chrono::duration_cast < std::chrono::seconds > (end - start).count();
+  if (m_timing || m_debug || m_deepDebug) {
+    std::cout << "*** Time taken to recreate all mother-daughter relationships between nodes of the GeoModel tree: " << diff << " [s]" << std::endl;
+  }
+  
 	return getRootVolume();
 }
 
@@ -284,22 +301,22 @@ GeoPhysVol* ReadGeoModel::buildGeoModelPrivate()
     muxCout.unlock();
   }
   
-  // Get Start Time
-  std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
+//  // Get Start Time
+//  std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
   
   for ( auto& record : records ) {
     processParentChild( record );
   }
   
-  // Get End Time
-  auto end = std::chrono::system_clock::now();
-  auto diff = std::chrono::duration_cast < std::chrono::seconds > (end - start).count();
-  
-  if (m_timing || m_debug || m_deepDebug) {
-    muxCout.lock();
-    std::cout << "Time Taken to process " << nChildrenRecords << " parent-child relationships = " << diff << " Seconds" << std::endl;
-    muxCout.unlock();
-  }
+//  // Get End Time
+//  auto end = std::chrono::system_clock::now();
+//  auto diff = std::chrono::duration_cast < std::chrono::seconds > (end - start).count();
+//
+//  if (m_timing || m_debug || m_deepDebug) {
+//    muxCout.lock();
+//    std::cout << "Time Taken to process " << nChildrenRecords << " parent-child relationships = " << diff << " Seconds" << std::endl;
+//    muxCout.unlock();
+//  }
 }
 
 
@@ -470,6 +487,7 @@ void ReadGeoModel::buildAllSerialTransformers()
   if (nSize>0) std::cout << "All " << nSize << " SerialTransformers have been built!\n";
 }
   
+  // FIXME: implement build function and cache for Functions
 // //! Iterate over the list of nodes, build them all, and store their pointers
 // void ReadGeoModel::buildAllFunctions()
 // {
@@ -599,7 +617,7 @@ void ReadGeoModel::loopOverAllChildrenInBunches()
     const unsigned int parentCopyN = std::stoi(parentchild[3]);
 
     // get the child's position in the parent's children list
-    const unsigned int position = std::stoi(parentchild[4]);
+    //const unsigned int position = std::stoi(parentchild[4]); // unused
     
     // get the child's details
     const unsigned int childTableId = std::stoi(parentchild[5]);
@@ -815,7 +833,7 @@ GeoPhysVol* ReadGeoModel::getRootVolume()
     std::cout << "ReadGeoModel::getRootVolume()" << std::endl;
     muxCout.unlock();
   }
-  const unsigned int id = std::stoi(m_root_vol_data[1]); // TODO: GetRoot() should return integers instead of strings...
+  const unsigned int id = std::stoi(m_root_vol_data[1]); // TODO: GeoModel GetRoot() should return integers instead of strings...
   const unsigned int tableId = std::stoi(m_root_vol_data[2]);
 	const unsigned int copyNumber = 1; // the Root volume has only one copy by definition
   GeoPhysVol* root = dynamic_cast<GeoPhysVol*>(buildVPhysVolInstance(id, tableId, copyNumber));
@@ -837,11 +855,6 @@ GeoMaterial* ReadGeoModel::buildMaterial(const unsigned int id)
   }
   std::vector<std::string> values = m_materials[id-1];
 
-//  if (m_deepDebug) {
-//    muxCout.lock();
-//    std::cout << "mat values=" << values << std::endl;
-//    muxCout.unlock();
-//  }
   const unsigned int matId = std::stoi(values[0]);
   const std::string matName = values[1];
   double matDensity = std::stod(values[2]);
@@ -936,7 +949,7 @@ std::string ReadGeoModel::getShapeType(const unsigned int shapeId)
 
   
 
-
+  //TODO: move shapes in different files, so code here is more managable
 /// Recursive function, to build GeoShape nodes
 GeoShape* ReadGeoModel::buildShape(const unsigned int shapeId, type_shapes_boolean_info* shapes_info_sub)
 {
@@ -1118,7 +1131,8 @@ GeoShape* ReadGeoModel::buildShape(const unsigned int shapeId, type_shapes_boole
 
 					if(error) {
             muxCout.lock();
-            // FIXME:           std::cout << "ERROR! GeoPcon 'ZRmin' and 'ZRmax' values are not at the right place! --> " << shapePars << std::endl;
+            std::cout << "ERROR! GeoPcon 'ZRmin' and 'ZRmax' values are not at the right place! --> ";
+            printStdVectorStrings(shapePars);
             muxCout.unlock();
           }
 
@@ -1127,7 +1141,8 @@ GeoShape* ReadGeoModel::buildShape(const unsigned int shapeId, type_shapes_boole
 				} else {
 					error = 1;
           muxCout.lock();
-          // FIXME: std::cout << "ERROR! GeoPcon 'ZPos' value is not at the right place! --> " << shapePars << std::endl;
+          std::cout << "ERROR! GeoPcon 'ZPos' value is not at the right place! --> ";
+          printStdVectorStrings(shapePars);
           muxCout.unlock();
 				}
 			}
@@ -1857,7 +1872,7 @@ GeoShape* ReadGeoModel::buildShape(const unsigned int shapeId, type_shapes_boole
     // otherwise, build the operands
     else {
 
-      // TODO: IMPORTANT!!! --> check how the transf used in shift are saved into the DB, because they are bare transf and not GeoTransform nodes... I fear thay are wrongly stored...
+      // TODO: IMPORTANT!!! --> check how the transf used in shift are saved into the DB, because they are bare transf and not GeoTransform nodes...
 
       // first, check if the transform is built;
       // if so, use that;
@@ -2305,6 +2320,9 @@ std::vector<std::string> ReadGeoModel::splitString(const std::string& s, const c
 //  }
 //  return vec;
 //}
+  
+  
+  
 // TODO: move this to utility class/file
 void ReadGeoModel::printStdVectorStrings(std::vector<std::string> vec)
 {
@@ -2404,21 +2422,12 @@ GeoLogVol* ReadGeoModel::buildLogVol(const unsigned int id)
 
 	// get logVol properties from the DB
   std::vector<std::string> values = m_logVols[id-1];
-	if (m_deepDebug) {
-    muxCout.lock();
-    //FIXME: std::cout << "buildLogVol() - params:" << values << std::endl;
-    muxCout.unlock();
-  }
 
 	// get the parameters to build the GeoLogVol node
   std::string logVolName = values[1];
 
 	// build the referenced GeoShape node
   const unsigned int shapeId = std::stoi(values[2]);
-//  type_shapes_boolean_info shapes_info_sub; // tuple to store the boolean shapes to complete at a second stage
-//  GeoShape* shape = buildShape(shapeId, &shapes_info_sub);
-  // now, create the missing operand shapes for boolean/operator shapes
-//  createBooleanShapeOperands(&shapes_info_sub);
   GeoShape* shape = getBuiltShape(shapeId);
   if(!shape) {
     std::cout << "ERROR!! While building a LogVol, Shape is NULL! Exiting..." <<std::endl;
@@ -2432,7 +2441,6 @@ GeoLogVol* ReadGeoModel::buildLogVol(const unsigned int id)
     std::cout << "buildLogVol() - material Id:" << matId << std::endl;
     muxCout.unlock();
   }
-//  GeoMaterial* mat = buildMaterial(matId);
   GeoMaterial* mat = getBuiltMaterial(matId);
   if(!mat) {
     std::cout << "ERROR!! While building a LogVol, Material is NULL! Exiting..." <<std::endl;
diff --git a/GeoModelWrite/GeoModelWrite/WriteGeoModel.h b/GeoModelWrite/GeoModelWrite/WriteGeoModel.h
index f101a7a2cc0ea657ad22a8e70d1f64acc7013700..5f0d7f411ebb2efd767f6fb18a6027e41bf717c5 100644
--- a/GeoModelWrite/GeoModelWrite/WriteGeoModel.h
+++ b/GeoModelWrite/GeoModelWrite/WriteGeoModel.h
@@ -1,8 +1,10 @@
 /*
  * author: Riccardo.Maria.Bianchi@cern.ch, 2017
  *
- * major updates: Aug 2018 rbianchi
- *                Feb 2019 rbianchi
+ * major updates:
+ * - Aug 2018 - R.M.Bianchi
+ * - Feb 2019 - R.M.Bianchi
+ * - May 2020 - R.M.Bianchi
  */
 
 #ifndef GeoModelWrite_WriteGeoModel_H
@@ -20,14 +22,12 @@
 #include "GeoModelKernel/GeoLogVol.h"
 #include "GeoModelKernel/GeoXF.h"
 #include "GeoModelKernel/GeoAlignableTransform.h"
-#include "GeoModelKernel/GeoDefinitions.h"
+#include "GeoModelKernel/GeoDefinitions.h" 
 
-// Qt includes
-//#include <QSqlDatabase>
-#include <QStringList>
-#include <QVariant>
-#include <QString>
-#include <QMap>
+// C++ includes
+#include <vector>
+#include <string>
+#include <unordered_map>
 
 
 namespace GeoModelIO {
@@ -77,81 +77,82 @@ private:
 
 	void showMemoryMap();
 
-	QStringList getParentNode();
-
-	QVariant storeShape(const GeoShape* shape);
-	QVariant storeMaterial(const GeoMaterial* mat);
-	QVariant storeElement(const GeoElement* el);
-	QVariant storeTranform(const GeoTransform* node);
-
-	QVariant storeObj(const GeoMaterial* pointer, const QString name, const QString density, const QString elements);
-	QVariant storeObj(const GeoElement* pointer, const QString name, const QString symbol, const QString elZ, const QString elA);
-	QVariant storeObj(const GeoShape* pointer, const QString type, const QString parameters);
-	QVariant storeObj(const GeoLogVol* pointer, const QString name, const QVariant shapeId, const QVariant materialId);
-	QVariant storeObj(const GeoPhysVol* pointer, const QVariant logvolId, const QVariant parentId = QVariant(), bool isRootVolume = false );
-	QVariant storeObj(const GeoFullPhysVol* pointer, const QVariant logvolId, const QVariant parentId = QVariant(), bool isRootVolume = false );
-	QVariant storeObj(const GeoSerialDenominator* pointer, const QString baseName);
-	QVariant storeObj(const GeoSerialTransformer* pointer, const QVariant functionId, const QVariant volId, const QString volType, const unsigned int copies);
-	QVariant storeObj(const GeoXF::Function* pointer, const QString expression);
-	QVariant storeObj(const GeoTransform* pointer, const std::vector<double> parameters);
-	QVariant storeObj(const GeoAlignableTransform* pointer, const std::vector<double> parameters);
-	QVariant storeObj(const GeoNameTag* pointer, const QString name);
-
-	unsigned int addRecord(std::vector<QStringList>* container, const QStringList values) const;
-	QVariant addMaterial(const QString name, const QString density, const QString elements);
-	QVariant addElement(const QString name, const QString symbol, const QString elZ, const QString elA);
-	QVariant addNameTag(const QString name);
-	QVariant addAlignableTransform(const std::vector<double> params);
-	QVariant addTransform(const std::vector<double> params);
-	QVariant addFunction(const QString expression);
-	QVariant addSerialTransformer(const QVariant &funcId, const QVariant &physvolId, const QString volType, const unsigned int &copies);
-	QVariant addShape(const QString &type, const QString &parameters);
-	QVariant addSerialDenominator(const QString &baseName);
-	QVariant addPhysVol(const QVariant &logVolId, const QVariant &parentPhysVolId, bool isRootVolume = false);
-	QVariant addFullPhysVol(const QVariant &logVolId, const QVariant &parentPhysVolId, bool isRootVolume = false);
-	QVariant addLogVol(const QString &name, const QVariant &shapeId, const QVariant &materialId);
-	void addChildPosition(const QVariant parentId, const QString parentType, const QVariant childId, const unsigned int parentCopyNumber, const unsigned int childPos, const QString childType, const unsigned int childCopyN);
-
-	unsigned int getChildPosition(QString parentId, QString parentType, unsigned int copyN);
-
-	unsigned int setVolumeCopyNumber(QString volId, QString volType);
-	unsigned int getLatestParentCopyNumber(QString parentId, QString parentType);
-
-	void storeChildPosition(const QVariant parentId, const QString parentType, const QVariant childVol, const unsigned int parentCopyNumber, const unsigned int childPos, const QString childType, const unsigned int childCopyN);
-
-	bool isAddressStored(const QString address);
-	void storeAddress(const QString address, QVariant id);
-
-	QVariant getStoredIdFromAddress(QString address);
-
-	QString getAddressStringFromPointer(const GeoMaterial* pointer);
-	QString getAddressStringFromPointer(const GeoElement* pointer);
-	QString getAddressStringFromPointer(const GeoShape* pointer);
-	QString getAddressStringFromPointer(const GeoLogVol* pointer);
-	QString getAddressStringFromPointer(const GeoPhysVol* pointer);
-	QString getAddressStringFromPointer(const GeoVPhysVol* pointer);
-	QString getAddressStringFromPointer(const GeoSerialDenominator* pointer);
-	QString getAddressStringFromPointer(const GeoSerialTransformer* pointer);
-	QString getAddressStringFromPointer(const GeoXF::Function* pointer);
-	QString getAddressStringFromPointer(const GeoTransform* pointer);
-	QString getAddressStringFromPointer(const GeoNameTag* pointer);
-
-	QString getQStringFromOss(std::ostringstream &oss);
+  std::vector<std::string> getParentNode();
+
+	unsigned int storeShape(const GeoShape* shape);
+	unsigned int storeMaterial(const GeoMaterial* mat);
+	unsigned int storeElement(const GeoElement* el);
+	unsigned int storeTranform(const GeoTransform* node);
+
+	unsigned int storeObj(const GeoMaterial* pointer, const std::string &name, const double &density, const std::string &elements);
+  unsigned int storeObj(const GeoElement* pointer, const std::string &name, const std::string &symbol, const double &elZ, const double &elA);
+  unsigned int storeObj(const GeoShape* pointer, const std::string &type, const std::string &parameters);
+  unsigned int storeObj(const GeoLogVol* pointer, const std::string &name, const unsigned int &shapeId, const unsigned int &materialId);
+	unsigned int storeObj(const GeoPhysVol* pointer, const unsigned int &logvolId, const unsigned int parentId = 0, const bool isRootVolume = false );
+	unsigned int storeObj(const GeoFullPhysVol* pointer, const unsigned int &logvolId, const unsigned int parentId = 0, const bool isRootVolume = false );
+  unsigned int storeObj(const GeoSerialDenominator* pointer, const std::string &baseName);
+  unsigned int storeObj(const GeoSerialTransformer* pointer, const unsigned int &functionId, const unsigned int &volId, const std::string &volType, const unsigned int &copies);
+  unsigned int storeObj(const GeoXF::Function* pointer, const std::string &expression);
+	unsigned int storeObj(const GeoTransform* pointer, const std::vector<double> &parameters);
+	unsigned int storeObj(const GeoAlignableTransform* pointer, const std::vector<double> &parameters);
+  unsigned int storeObj(const GeoNameTag* pointer, const std::string &name);
+
+  unsigned int addRecord(std::vector<std::vector<std::string>>* container, const std::vector<std::string> values) const;
+  
+  unsigned int addMaterial(const std::string &name, const double &density, const std::string &elements);
+  unsigned int addElement(const std::string &name, const std::string &symbol, const double &elZ, const double &elA);
+  unsigned int addNameTag(const std::string &name);
+	unsigned int addAlignableTransform(const std::vector<double> &params);
+	unsigned int addTransform(const std::vector<double> &params);
+  unsigned int addFunction(const std::string &expression);
+  unsigned int addSerialTransformer(const unsigned int &funcId, const unsigned int &physvolId, const std::string volType, const unsigned int &copies);
+  unsigned int addShape(const std::string &type, const std::string &parameters);
+  unsigned int addSerialDenominator(const std::string &baseName);
+	unsigned int addPhysVol(const unsigned int &logVolId, const unsigned int &parentPhysVolId, const bool &isRootVolume);
+	unsigned int addFullPhysVol(const unsigned int &logVolId, const unsigned int &parentPhysVolId, const bool &isRootVolume);
+  unsigned int addLogVol(const std::string &name, const unsigned int &shapeId, const unsigned int &materialId);
+  void addChildPosition(const unsigned int &parentId, const std::string &parentType, const unsigned int &childId, const unsigned int &parentCopyNumber, const unsigned int &childPos, const std::string &childType, const unsigned int &childCopyN);
+
+	unsigned int getChildPosition(const unsigned int &parentId, const std::string &parentType, const unsigned int &copyN);
+
+  unsigned int setVolumeCopyNumber(const unsigned int& volId, const std::string& volType);
+  unsigned int getLatestParentCopyNumber(const unsigned int& parentId, const std::string& parentType);
+
+	void storeChildPosition(const unsigned int& parentId, const std::string& parentType, const unsigned int& childVol, const unsigned int& parentCopyNumber, const unsigned int& childPos, const std::string& childType, const unsigned int& childCopyN);
+
+	bool isAddressStored(const std::string &address);
+	void storeAddress(const std::string &address, const unsigned int &id);
+
+  unsigned int getStoredIdFromAddress(const std::string &address);
+
+	std::string getAddressStringFromPointer(const GeoMaterial* pointer);
+	std::string getAddressStringFromPointer(const GeoElement* pointer);
+	std::string getAddressStringFromPointer(const GeoShape* pointer);
+	std::string getAddressStringFromPointer(const GeoLogVol* pointer);
+	std::string getAddressStringFromPointer(const GeoPhysVol* pointer);
+	std::string getAddressStringFromPointer(const GeoVPhysVol* pointer);
+	std::string getAddressStringFromPointer(const GeoSerialDenominator* pointer);
+	std::string getAddressStringFromPointer(const GeoSerialTransformer* pointer);
+	std::string getAddressStringFromPointer(const GeoXF::Function* pointer);
+	std::string getAddressStringFromPointer(const GeoTransform* pointer);
+	std::string getAddressStringFromPointer(const GeoNameTag* pointer);
+
+	std::string getQStringFromOss(std::ostringstream &oss);
 
 	std::vector<double> getTransformParameters(GeoTrf::Transform3D); // TODO: to be moved to Eigen (GeoTrf) and to be moved to an Utility class, so we can use it from TransFunctionRecorder as well.
-	QString getShapeParameters(const GeoShape*);
+  std::string getShapeParameters(const GeoShape*);
 
-	QString getGeoTypeFromVPhysVol(const GeoVPhysVol* vol);
+  std::string getGeoTypeFromVPhysVol(const GeoVPhysVol* vol);
 
-	QString getIdFromNodeType(QString nodeType);
+  unsigned int getIdFromNodeType(const std::string &nodeType);
 
-	QString m_dbpath;
+  std::string m_dbpath;
 	GMDBManager* m_dbManager;
 
-	QMap<QString, QVariant> m_memMap; // TODO: maybe move to QHash??
-	QHash<QString, unsigned int> m_memMap_Tables;
-	QMap<QString, unsigned int> m_parentChildrenMap2; // TODO: clean name!
-	QMap<QString, unsigned int> m_volumeCopiesMap;
+  std::unordered_map<std::string, unsigned int> m_parentChildrenMap;
+  std::unordered_map<std::string, unsigned int> m_volumeCopiesMap;
+  std::unordered_map<std::string, unsigned int> m_memMap;
+  std::unordered_map<std::string, unsigned int> m_memMap_Tables;
 
 	// keep track of the number of visited tree nodes
 	unsigned int m_len;
@@ -160,23 +161,22 @@ private:
 	bool m_rootVolumeFound;
 	bool m_unconnectedTree;
 
-	std::vector<QStringList> m_logVols;
-	std::vector<QStringList> m_physVols;
-	std::vector<QStringList> m_fullPhysVols;
-	std::vector<QStringList> m_shapes;
-	std::vector<QStringList> m_materials;
-	std::vector<QStringList> m_elements;
-	std::vector<QStringList> m_transforms;
-	std::vector<QStringList> m_alignableTransforms;
-	std::vector<QStringList> m_serialDenominators;
-	std::vector<QStringList> m_serialTransformers;
-	std::vector<QStringList> m_functions;
-	std::vector<QStringList> m_nameTags;
-
-	std::vector<QStringList> m_childrenPositions;
-	QStringList m_rootVolume;
-
-	QStringList m_objectsNotPersistified;
+  std::vector<std::vector<std::string>> m_logVols;
+	std::vector<std::vector<std::string>> m_physVols;
+	std::vector<std::vector<std::string>> m_fullPhysVols;
+	std::vector<std::vector<std::string>> m_materials;
+	std::vector<std::vector<std::string>> m_elements;
+	std::vector<std::vector<std::string>> m_transforms;
+	std::vector<std::vector<std::string>> m_alignableTransforms;
+	std::vector<std::vector<std::string>> m_serialDenominators;
+	std::vector<std::vector<std::string>> m_serialTransformers;
+	std::vector<std::vector<std::string>> m_functions;
+	std::vector<std::vector<std::string>> m_nameTags;
+	std::vector<std::vector<std::string>> m_childrenPositions;
+  std::vector<std::vector<std::string>> m_shapes;
+  std::vector<std::string> m_rootVolume;
+
+  std::vector<std::string> m_objectsNotPersistified;
 
 };
 
diff --git a/GeoModelWrite/scripts/xcode_test_clean_folder.sh b/GeoModelWrite/scripts/xcode_test_clean_folder.sh
new file mode 100755
index 0000000000000000000000000000000000000000..b41e36c7d2dc12c87d15786d538ed692daa97d13
--- /dev/null
+++ b/GeoModelWrite/scripts/xcode_test_clean_folder.sh
@@ -0,0 +1,17 @@
+# A script to be used as "Run Script" in Xcode.
+# This is specific to test the target GeoModelWrite with the
+# GeoModelExamples/HelloGeoWrite example.
+# The script removes the test file geometry.db before running the example.
+#
+# To use the script, you should add a "Run Script" entry in the target's
+# "Build Phases" (the executable, i.e. the test, not the library)
+# and add this single line, which will run this script:
+#
+# $SRCROOT/GeoModelWrite/xcode_test_clean_folder.sh
+#
+# See: https://www.mokacoding.com/blog/better-build-phase-scripts/
+#
+#
+set -x
+ls $TARGET_BUILD_DIR
+rm -f $TARGET_BUILD_DIR/geometry.db
diff --git a/GeoModelWrite/src/WriteGeoModel.cpp b/GeoModelWrite/src/WriteGeoModel.cpp
index 3fbf52f155ec9a87c37c0d17f963068feb8486f7..2454c0fd7eadefc47a860d58678261a8c60ba4da 100644
--- a/GeoModelWrite/src/WriteGeoModel.cpp
+++ b/GeoModelWrite/src/WriteGeoModel.cpp
@@ -1,5 +1,8 @@
 // author: Riccardo.Maria.Bianchi@cern.ch, 2017
-// major updates: Aug 2018, Feb 2019
+// major updates:
+// - Aug 2018 - Riccardo Maria Bianchi
+// - Feb 2019 - Riccardo Maria Bianchi
+// - May 2020 - Riccardo Maria Bianchi
 
 // local includes
 #include "GeoModelWrite/WriteGeoModel.h"
@@ -34,95 +37,101 @@
 
 #include "GeoModelKernel/GeoUnidentifiedShape.h"
 
-// Qt includes
-#include <QSqlQuery>
-#include <QSqlError>
-#include <QSqlRecord>
-#include <QSqlDriver>
-#include <QVector>
-#include <QDebug>
-
 // C++ includes
 #include <sstream>
 
 
 namespace GeoModelIO {
 
-
+  // TODO: should go to an utility class
+  // FIXME: should go to an utility class
+  std::string joinVectorStrings(std::vector<std::string> vec, std::string sep="") {
+    std::string s;
+    unsigned int ii = 0;
+    for (const auto &piece : vec) {
+      ++ii;
+      if (ii == vec.size()) {
+        s += (piece);
+      } else {
+        s += (piece + sep);
+      }
+    }
+    return s;
+  }
+  
+  // TODO: move this to utility class/file
+  void printStdVectorStrings(std::vector<std::string> vec)
+  {
+    for ( const auto& str : vec) {
+      std::cout << str << " ";
+    }
+    std::cout << std::endl;
+    return;
+  }
+  
 /// Get next child position available, given the parent type, id and copy number
-unsigned int WriteGeoModel::getChildPosition(const QString parentId, const QString parentType, const unsigned int copyN)
-{
-	//JFB Commented out: qDebug() << "WriteGeoModel::getChildPosition()";
-	QString tableId = getIdFromNodeType(parentType);
-	QString key = tableId + ":" + parentId + ":" + QString::number(copyN);
-
-	if ( ! (m_parentChildrenMap2.contains(key)) ) {
-		m_parentChildrenMap2[key] = 1;
+  unsigned int WriteGeoModel::getChildPosition(const unsigned int &parentId, const std::string &parentType, const unsigned int &copyN)
+{
+  unsigned int tableId = getIdFromNodeType(parentType);
+  std::string key = std::to_string(tableId) + ":" + std::to_string(parentId) + ":" + std::to_string(copyN);
+  
+  std::unordered_map<std::string, unsigned int>::iterator it = m_parentChildrenMap.find(key);
+	if ( it == m_parentChildrenMap.end() ) {
+		m_parentChildrenMap[key] = 1; // if item is not present, create an entry
 	} else {
-	    ++m_parentChildrenMap2[key];
+	    ++m_parentChildrenMap[key]; // if present already, then increment its entry
     }
-
-    //JFB Commented out: qDebug() << "parent key:" << key << " [tableId:parentId:copyN] - pos: " <<  m_parentChildrenMap2[key];
-	return m_parentChildrenMap2[key];
+	return m_parentChildrenMap[key];
 }
 
-unsigned int WriteGeoModel::setVolumeCopyNumber(QString volId, QString volType)
+  unsigned int WriteGeoModel::setVolumeCopyNumber(const unsigned int& volId, const std::string& volType)
 {
 	//JFB Commented out: qDebug() << "WriteGeoModel::setVolumeCopyNumber()";
-	QString tableId = getIdFromNodeType(volType);
-	QString key = tableId + ":" + volId;
+	const unsigned int tableId = getIdFromNodeType(volType);
+  std::string key = std::to_string(tableId) + ":" + std::to_string(volId);
 
-	if ( ! (m_volumeCopiesMap.contains(key)) ) {
+  std::unordered_map<std::string, unsigned int>::iterator it = m_volumeCopiesMap.find(key);
+  if ( it == m_volumeCopiesMap.end() ) {
 		m_volumeCopiesMap[key] = 1;
 	} else {
     	++m_volumeCopiesMap[key];
     }
-
-    //JFB Commented out: qDebug() << "volume key:" << key << " [tableId:volumeId] - copy number: " <<  m_volumeCopiesMap[key];
 	return m_volumeCopiesMap[key];
 }
 
 
-unsigned int WriteGeoModel::getLatestParentCopyNumber(QString parentId, QString parentType)
+  unsigned int WriteGeoModel::getLatestParentCopyNumber(const unsigned int &parentId, const std::string &parentType)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::getLatestParentCopyNumber()";
-	QString tableId = getIdFromNodeType(parentType);
-	QString key = tableId + ":" + parentId;
-    //JFB Commented out: qDebug() << "key:" << key;
+  const unsigned int tableId = getIdFromNodeType(parentType);
+  std::string key = std::to_string(tableId) + ":" + std::to_string(parentId);
 
-	if ( ! (m_volumeCopiesMap.contains(key)) ) {
-		qFatal("ERROR!!! Something's wrong in storing the number of copies!");
+  std::unordered_map<std::string, unsigned int>::iterator it = m_volumeCopiesMap.find(key);
+  if ( it == m_volumeCopiesMap.end() ) {
+    std::cout << "ERROR!!! Something's wrong in storing the number of copies!" << std::endl;
 	}
-
-    //JFB Commented out: qDebug() << "get latest parent copy number:" << key << " [tableId:parentId] - copy number: " <<  m_volumeCopiesMap[key];
 	return m_volumeCopiesMap[key];
 }
 
 
 void WriteGeoModel::handlePhysVol (const GeoPhysVol *vol)
 {
-	//JFB Commented out: qDebug() << "\nWriteGeoModel::handlePhysVol(GeoPhysVol*)";
 	handleVPhysVolObjects( vol );
 }
 
 
 void WriteGeoModel::handleFullPhysVol (const GeoFullPhysVol *vol)
 {
-	//JFB Commented out: qDebug() << "\nWriteGeoModel::handleFullPhysVol( GeoFullPhysVol* )";
 	handleVPhysVolObjects( vol );
 }
 
 
 void WriteGeoModel::handleVPhysVolObjects(const GeoVPhysVol* vol)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::handleVPhysVolObjects( GeoVPhysVol* )";
-
-
 	// get the address string for the current volume
-	QString address = getAddressStringFromPointer( vol );
+  std::string address = getAddressStringFromPointer( vol );
 
 	// variables used to persistify the object
-	QVariant physId;
+	unsigned int physId;
 
 	// check the volume position in the geometry tree
 	GeoNodePath* path = getPath();
@@ -149,8 +158,7 @@ void WriteGeoModel::handleVPhysVolObjects(const GeoVPhysVol* vol)
 		if (len > 1) {
 			doGetParentNode = true; // TODO: is that needed????
 		} else{
-			//JFB Commented out: qDebug() << "This is the Root volume!";
-            isRootVolume = true;
+      isRootVolume = true;
 			m_rootVolumeFound = true;
 			storeRootVolume = true;
 			doGetParentNode = false;
@@ -164,11 +172,11 @@ void WriteGeoModel::handleVPhysVolObjects(const GeoVPhysVol* vol)
 	// If we do, the ID of the parent of the SerialTransformer is returned, which is wrong.
 	if (m_unconnectedTree) {
 		doGetParentNode = false;
-		//JFB Commented out: qDebug() << "Handling an unconnected tree: we skip the parent-finding step...";
+		//qDebug() << "Handling an unconnected tree: we skip the parent-finding step...";
 		// now, we reset the status,
 		// otherwise all the children of the first referenced, unconnected volume
 		// will be treated as unconnected as well
-		//JFB Commented out: qDebug() << "setting 'unconnected' to: false";
+		//qDebug() << "setting 'unconnected' to: false";
 		m_unconnectedTree = false;
 	}
 
@@ -180,23 +188,23 @@ void WriteGeoModel::handleVPhysVolObjects(const GeoVPhysVol* vol)
 
 	// get the parent volume, if this is not the Root volume or an unconnected sub-tree
 	const GeoVPhysVol* parentNode = nullptr;
-	QVariant parentId = "NULL";
+	unsigned int parentId = 0;
 
-    QString volTypeStr = "NNN";
+  std::string volTypeStr = "0";
 
 	if (doGetParentNode) {
 
 //        bool isShared = vol->isShared();
-        //JFB Commented out: qDebug() << "is this node shared?" << isShared;
+        //qDebug() << "is this node shared?" << isShared;
 
         //if (isShared)
             parentNode = upperVol;
         //else
 		//    parentNode = dynamic_cast<const GeoVPhysVol*>( &(*(vol->getParent() ))); // this cannot be trust for shared nodes
-        //JFB Commented out: qDebug() << "parentNode address" << parentNode;
+        //qDebug() << "parentNode address" << parentNode;
 
 		if (parentNode) {
-			QString parentAddress = getAddressStringFromPointer(parentNode);
+      std::string parentAddress = getAddressStringFromPointer(parentNode);
 			//JFB Commented out: qDebug() << "==> parent's address:" << parentNode;
 
 			if (isAddressStored(parentAddress))
@@ -204,24 +212,24 @@ void WriteGeoModel::handleVPhysVolObjects(const GeoVPhysVol* vol)
 			//		else
 			//			qFatal("FATAL ERROR!!! - The parent node of this child should has been stored in the DB already, but it was not found!!");
 
-			//JFB Commented out: qDebug() << "==> parent's LogVol name:" << QString::fromStdString(parentNode->getLogVol()->getName());
+			//qDebug() << "==> parent's LogVol name:" << QString::fromStdString(parentNode->getLogVol()->getName());
 		}
 		else {
-			//JFB Commented out: qDebug() << "NULL parent node!! --> this node appeared unconnected.";
+			//qDebug() << "NULL parent node!! --> this node appeared unconnected.";
 		}
 	}
 
 	// counting children
 //  unsigned int nChildren = vol->getNChildVols();
-	//JFB Commented out: qDebug() << "number of child physical volumes:" << nChildren;
-	//JFB Commented out: qDebug() << "[number of PhysVol and SerialTransformer child nodes:" << vol->getNChildVolAndST() << "]";
+	//qDebug() << "number of child physical volumes:" << nChildren;
+	//qDebug() << "[number of PhysVol and SerialTransformer child nodes:" << vol->getNChildVolAndST() << "]";
 
 	//// for debug
 	// GeoCountVolAction cv;
 	// cv.setDepthLimit(1);
 	// vol->exec(&cv);
 	// int nChildCount = cv.getCount();
-	// //JFB Commented out: qDebug() << "number of child volumes:" << nChildCount;
+	// qDebug() << "number of child volumes:" << nChildCount;
 
 
 
@@ -241,105 +249,84 @@ void WriteGeoModel::handleVPhysVolObjects(const GeoVPhysVol* vol)
 
 		// LOGVOL
 		const GeoLogVol* logVol = vol->getLogVol();
-		const QString logName = QString::fromStdString(logVol->getName());
-		//JFB Commented out: qDebug() << "LogVol name:"  << logName;
-
+    const std::string logName = logVol->getName();
 
 		// MATERIAL
 		const GeoMaterial * mat = vol->getLogVol()->getMaterial();
-		// const QString matName = QString::fromStdString(mat->getName());
-		// qDebug() << "material name:" << matName << ", address:" << mat;
-
 
 		// SHAPE
 		const GeoShape * shape = vol->getLogVol()->getShape();
-		// const QString shapeType = QString::fromStdString(shape->type());
-		// qDebug() << "shape name:" << shapeType  << ", address:" << shape;
-		// // get shape parameters
-		// QString shapePars = getShapeParameters(shape);
-
 
 		/*
 		 * STORE THE OBJECTS IN THE DB
 		 */
 
 		// store/get the Material object into/from the DB
-		QVariant matId;
+		unsigned int matId;
 		// matId = storeObj(mat, matName);
 		matId = storeMaterial(mat);
 
 		// store/get the Shape object into/from the DB
-		QVariant shapeId;
+		unsigned int shapeId;
 		shapeId = storeShape(shape);
 		// shapeId = storeObj(shape, shapeType, shapePars);
 
 		// store/get the LogVol object into/from the DB
-		QVariant logvolId;
+		unsigned int logvolId;
 		logvolId = storeObj(logVol, logName, shapeId, matId);
 
 		if (dynamic_cast<const GeoPhysVol*>(vol)) {
-			//JFB Commented out: qDebug() << "New PhysVol, storing it...";
 			const GeoPhysVol* physVol = dynamic_cast<const GeoPhysVol*>(vol);
 			// store the PhysVol volume into the DB
 			physId = storeObj(physVol, logvolId, parentId, storeRootVolume); // with parent info
-            volTypeStr = "GeoPhysVol";
-			//JFB Commented out: qDebug() << "PhysVol stored. Id:" << physId.toString();
+      volTypeStr = "GeoPhysVol";
 		}
 		else if (dynamic_cast<const GeoFullPhysVol*>(vol)) {
-			//JFB Commented out: qDebug() << "New FullPhysVol, storing it...";
 			const GeoFullPhysVol* fullVol = dynamic_cast<const GeoFullPhysVol*>(vol);
-			// store the FullPhysVol volume into the DB
 			physId = storeObj(fullVol, logvolId, parentId, storeRootVolume); // with parent info
-            volTypeStr = "GeoFullPhysVol";
-			//JFB Commented out: qDebug() << "FullPhysVol stored. Id:" << physId.toString();
+      volTypeStr = "GeoFullPhysVol";
 		} else {
-			qWarning() << "WARNING!! Unknown GeoVPhysVol type!!";
+      std::cout << "WARNING!! Unknown GeoVPhysVol type!! Exiting..." << std::endl;
+      exit(EXIT_FAILURE);
 		}
 
 	} else {
-		//	qDebug() << "Volume stored already. It is a shared volume. Taking ID from memory map and moving to its physical children...";
-		//JFB Commented out: qDebug() << "Volume stored already. It is a shared volume. Now, we are handling a 'copy' of it. We take the ID from memory map...";
 		physId = getStoredIdFromAddress(address);
-        volTypeStr = getGeoTypeFromVPhysVol(vol);
+    volTypeStr = getGeoTypeFromVPhysVol(vol);
 	}
 
     // Now we get the 'copy number' for this volume,
     // to distinguish this volume from the other volumes created from the same shared node (if any)
     if (volTypeStr == "NULL") qFatal("ERROR!! volTypeStr is 'NULL'!!!");
-    const unsigned int volCopyN = setVolumeCopyNumber(physId.toString(), volTypeStr);
+    const unsigned int volCopyN = setVolumeCopyNumber(physId, volTypeStr);
     //JFB Commented out: qDebug() << "physId: " << physId << "- volume copy number: " << volCopyN;
 
-    if ( isRootVolume || parentId == "NULL") {
-        //JFB Commented out: qDebug() << "This is the RootVolume or the volume has 'NULL' parent (unconnected subtree?) - So, we do not store the child position for this volume!";
+    if ( isRootVolume || parentId == 0) {
+        //qDebug() << "This is the RootVolume or the volume has 'NULL' parent (unconnected subtree?) - So, we do not store the child position for this volume!";
     } else {
 	    // store the parent-child relationship in the DB
-	    QString parentType = getGeoTypeFromVPhysVol(parentNode);
-        // get the copy number of the parent
-        const unsigned int parentCopyN = getLatestParentCopyNumber(parentId.toString(), parentType);
-	    //JFB Commented out: qDebug() << "PhysVol Id:" << physId << " - copyNumber:" << QString::number(parentCopyN);
-
-	    QString childType  = getGeoTypeFromVPhysVol(vol);
-
-	    storeChildPosition(parentId, parentType, physId, parentCopyN, getChildPosition( parentId.toString(), parentType, parentCopyN), childType, volCopyN);
+      std::string parentType = getGeoTypeFromVPhysVol(parentNode);
+      // get the copy number of the parent
+      const unsigned int parentCopyN = getLatestParentCopyNumber(parentId, parentType);
+      std::string childType  = getGeoTypeFromVPhysVol(vol);
+	    storeChildPosition(parentId, parentType, physId, parentCopyN, getChildPosition( parentId, parentType, parentCopyN), childType, volCopyN);
     }
 }
 
 
-QString WriteGeoModel::getGeoTypeFromVPhysVol(const GeoVPhysVol* vol)
+  std::string WriteGeoModel::getGeoTypeFromVPhysVol(const GeoVPhysVol* vol)
 {
 	if (!vol)
-		return QString("NULL");
+		return "NULL";
 
-	QString geoType;
+  std::string geoType;
 	if (dynamic_cast<const GeoPhysVol*>(vol)) {
-		//JFB Commented out: qDebug() << "GeoType: GeoPhysVol";
 		geoType = "GeoPhysVol";
 	}
 	else if (dynamic_cast<const GeoFullPhysVol*>(vol)) {
-		//JFB Commented out: qDebug() << "GeoType: GeoFullPhysVol";
 		geoType = "GeoFullPhysVol";
 	} else {
-		qWarning() << "WARNING!! Unknown GeoVPhysVol type!!";
+    std::cout << "WARNING!! Unknown GeoVPhysVol type!!" << std::endl;
 	}
 	return geoType;
 }
@@ -347,35 +334,25 @@ QString WriteGeoModel::getGeoTypeFromVPhysVol(const GeoVPhysVol* vol)
 
 void WriteGeoModel::handleSerialDenominator (const GeoSerialDenominator *node)
 {
-	//JFB Commented out: qDebug() << "\nWriteGeoModel::handleSerialDenominator(GeoSerialDenominator*)";
-
-	QString address = getAddressStringFromPointer( node );
-
-	std::string baseNameStr = node->getBaseName();
-	QString baseName = QString::fromStdString(baseNameStr);
-	//JFB Commented out: qDebug() << "base name:" << baseName << "address:" << address;
+  std::string address = getAddressStringFromPointer( node );
+  std::string baseName = node->getBaseName();
 
 	// variables used to persistify the object
-	QVariant sdId;
+	unsigned int sdId;
 
 	// get the parent volume
-	QStringList parentList = getParentNode();
-	QString parentId = parentList[0];
-	QString parentType = parentList[1];
-	unsigned int parentCopyN = getLatestParentCopyNumber(parentId, parentType);
+  const std::vector<std::string> parentList = getParentNode();
+  const unsigned int parentId = std::stoi(parentList[0]);
+  const std::string parentType = parentList[1];
+	const unsigned int parentCopyN = getLatestParentCopyNumber(parentId, parentType);
 
 	// check if this object has been stored already
 	if (! isAddressStored(address)) {
-
-		//JFB Commented out: qDebug() << "New SerialDenominator, storing it...";
-
 		/* STORE THE OBJECT IN THE DB */
 		sdId = storeObj(node, baseName);
-		//JFB Commented out: qDebug() << "SerialDenominator stored. Id:" << sdId.toString();
-
 	} else {
+    /* GET THE OBJECT FROM THE DB */
 		sdId = getStoredIdFromAddress(address);
-		//JFB Commented out: qDebug() << "SerialDenominator already stored in the DB. Id:" << sdId.toString();
 	}
 
 	storeChildPosition(parentId, parentType, sdId, parentCopyN, getChildPosition( parentId, parentType, parentCopyN ), "GeoSerialDenominator", 0); // TODO: Check if the copyN=0 at the end is OK for nodes as Transforms, which cannot be used as parents, only as children!
@@ -385,29 +362,24 @@ void WriteGeoModel::handleSerialDenominator (const GeoSerialDenominator *node)
 
 void WriteGeoModel::handleSerialTransformer (const GeoSerialTransformer *node)
 {
-	//JFB Commented out: qDebug() << "\nWriteGeoModel::handleSerialTransformer(GeoSerialTransformer*)";
-
-	QString address = getAddressStringFromPointer( node );
+  std::string address = getAddressStringFromPointer( node );
 
 	// variables used to persistify the object
-	QVariant functionId;
-	QVariant physvolId;
-	QVariant physvolTable;
+	unsigned int functionId;
+	unsigned int physvolId;
+//  unsigned int physvolTable;
 	unsigned int nCopies;
-	QVariant stId;
+	unsigned int stId;
 
 	// get the parent volume
-	QStringList parentList = getParentNode();
-	QString parentId = parentList[0];
-	QString parentType = parentList[1];
+  const std::vector<std::string> parentList = getParentNode();
+  const unsigned int parentId = std::stoi(parentList[0]);
+  const std::string parentType = parentList[1];
 	unsigned int parentCopyN = getLatestParentCopyNumber(parentId, parentType);
 
 
 	// check if this object has been stored already
 	if (! isAddressStored(address)) {
-
-		//JFB Commented out: qDebug() << "New SerialTransformer, storing it...";
-
 		/*
 		 * Get Node characteristics
 		 *
@@ -421,18 +393,17 @@ void WriteGeoModel::handleSerialTransformer (const GeoSerialTransformer *node)
 		// get linked function and number of copies
 		const GeoXF::Function * func = node->getFunction();
 		nCopies =  node->getNCopies();
-		//JFB Commented out: qDebug() << "n. of copies:" << QString::number(nCopies);
 
 		// get linked VPhysVol volume
 		const GeoVPhysVol *vol = &( *( node->getVolume() ) );
 		// const GeoPhysVol* vol = dynamic_cast<const GeoPhysVol*>(volV);
-		QString volType;
+    std::string volType;
 		if (dynamic_cast<const GeoPhysVol*>(vol)) {
 			volType = "GeoPhysVol";
 		} else if (dynamic_cast<const GeoFullPhysVol*>(vol)) {
 			volType = "GeoFullPhysVol";
 		} else {
-			qWarning() << "ERROR!!! Unknown VPhysVol type!!";
+      std::cout << "ERROR!!! Unknown VPhysVol type!!" << std::endl;
 		}
 
 		/*
@@ -444,8 +415,7 @@ void WriteGeoModel::handleSerialTransformer (const GeoSerialTransformer *node)
         } catch (const std::runtime_error & error) {
             std::cout << "SEVERE WARNING!! Handling std::runtime_error! -->" << error.what() << std::endl;
         }
-		QString expression = QString::fromStdString( persistifier.getCodedString() );
-		//JFB Commented out: qDebug() << "FUNCTION:" << expression;
+    std::string expression = persistifier.getCodedString();
 
 		if (expression.size() == 0) {
 				qFatal("FATAL ERROR!! Function expression is empty!! Aborting...");
@@ -471,11 +441,9 @@ void WriteGeoModel::handleSerialTransformer (const GeoSerialTransformer *node)
 		 * ==> So, we need to persitify it as a new tree, to get all its children visited and persistified
 		 *
 		 */
-		//JFB Commented out: qDebug() << "Handling the referenced VPhysVol, going into the sub-tree, if any...";
 		handleReferencedVPhysVol(vol);
 
-		//JFB Commented out: qDebug() << "Storing the referenced VPhysVol...";
-		QString physvolAddress = getAddressStringFromPointer(vol);
+    std::string physvolAddress = getAddressStringFromPointer(vol);
 		physvolId = getStoredIdFromAddress(physvolAddress);
 
 
@@ -484,14 +452,11 @@ void WriteGeoModel::handleSerialTransformer (const GeoSerialTransformer *node)
 		 */
 		// store the SerialTransformer volume in the DB
 		stId = storeObj(node, functionId, physvolId, volType, nCopies);
-		//JFB Commented out: qDebug() << "SerialTransformer stored. Id:" << stId.toString();
-
-	} else {
+	}
+  else {
 		stId = getStoredIdFromAddress(address);
-		//JFB Commented out: qDebug() << "SerialTransformer already stored in the DB. Id:" << stId.toString();
 	}
 
-	//JFB Commented out: qDebug() << "Storing:" << parentId << parentType << stId;
 	storeChildPosition(parentId, parentType, stId, parentCopyN, getChildPosition( parentId, parentType, parentCopyN ), "GeoSerialTransformer", 0); // TODO: Check if the copyN=0 at the end is OK for nodes as Transforms, which cannot be used as parents, only as children!
 }
 
@@ -500,14 +465,12 @@ void WriteGeoModel::handleSerialTransformer (const GeoSerialTransformer *node)
 
 void WriteGeoModel::handleTransform(const GeoTransform* node)
 {
-	//JFB Commented out: qDebug() << "\nWriteGeoModel::handleTransform(GeoTransform*)";
-
-	QString address = getAddressStringFromPointer( node );
+  std::string address = getAddressStringFromPointer( node );
 
 	// get the parent volume
-	QStringList parentList = getParentNode();
-	QString parentId = parentList[0];
-	QString parentType = parentList[1];
+  const std::vector<std::string> parentList = getParentNode();
+  const unsigned int parentId = std::stoi(parentList[0]);
+  const std::string parentType = parentList[1];
 
 	unsigned int parentCopyN = getLatestParentCopyNumber(parentId, parentType);
 
@@ -516,20 +479,19 @@ void WriteGeoModel::handleTransform(const GeoTransform* node)
 	 */
 
 	 // store the transformation in the DB
-	 QVariant trId = storeTranform(node);
+	 unsigned int trId = storeTranform(node);
 
 	// Store the child-parent relationship
 
 	if ( dynamic_cast<const GeoAlignableTransform*>(node) ) {
-		//JFB Commented out: qDebug() << "Storing a GeoAlignableTransform...";
 		storeChildPosition(parentId, parentType, trId, parentCopyN, getChildPosition( parentId, parentType, parentCopyN ), "GeoAlignableTransform", 0); // TODO: Check if the copyN=0 at the end is OK for nodes as Transforms, which cannot be used as parents, only as children!
 	}
 	else if ( dynamic_cast<const GeoTransform*>(node) ) {
-		//JFB Commented out: qDebug() << "Storing a GeoTransform...";
 		storeChildPosition(parentId, parentType, trId, parentCopyN, getChildPosition( parentId, parentType, parentCopyN ), "GeoTransform", 0); // TODO: Check if the copyN=0 at the end is OK for nodes as Transforms, which cannot be used as parents, only as children!
 	}
 	else {
-		qWarning("Not implemented yet!!!! ");
+    std::cout << "ERROR!! Unknwon Transform type! Exiting..." << std::endl;
+    exit(EXIT_FAILURE);
 	}
 
 }
@@ -537,28 +499,22 @@ void WriteGeoModel::handleTransform(const GeoTransform* node)
 
 void WriteGeoModel::handleNameTag(const GeoNameTag* node)
 {
-	//JFB Commented out: qDebug() << "\nWriteGeoModel::handleNameTag(GeoNameTag*)";
-
-	std::string nameStr = node->getName();
-	QString name = QString::fromStdString(nameStr);
-	//JFB Commented out: qDebug() << "name:" << name;
-
-	QString address = getAddressStringFromPointer( node );
-
+  std::string name = node->getName();
+  const std::string address = getAddressStringFromPointer( node );
 	// get the parent volume
-	QStringList parentList = getParentNode();
-	QString parentId = parentList[0];
-	QString parentType = parentList[1];
-    unsigned int parentCopyN = getLatestParentCopyNumber(parentId, parentType);
+  const std::vector<std::string> parentList = getParentNode();
+  const unsigned int parentId = std::stoi(parentList[0]);
+  const std::string parentType = parentList[1];
+  unsigned int parentCopyN = getLatestParentCopyNumber(parentId, parentType);
 
-    // TODO: add "if stored"...
+  // FIXME: TODO: add "if stored"...
 
 	/*
 	 * STORE THE OBJECT IN THE DB AND ITS POSITION WITHIN THE TREE
 	 */
 
 	 // store the name tag in the DB
-	 QVariant nameId = storeObj(node, name);
+	 unsigned int nameId = storeObj(node, name);
 
 	 // Store the child-parent relationship
 	 storeChildPosition(parentId, parentType, nameId, parentCopyN, getChildPosition( parentId, parentType, parentCopyN ), "GeoNameTag", 0);
@@ -567,14 +523,11 @@ void WriteGeoModel::handleNameTag(const GeoNameTag* node)
 
 
 //__________________________________________________
-QStringList WriteGeoModel::getParentNode()
+  std::vector<std::string> WriteGeoModel::getParentNode()
 {
-    //JFB Commented out: qDebug() << "WriteGeoModel::getParentNode()";
-
 	// check the current volume position in the geometry tree
 	GeoNodePath* path = getPath();
 	unsigned int len = path->getLength();
-	//JFB Commented out: qDebug() << "length: " << len;
 
 	// reset the number of visited node, if len is different than before
 	if (len > m_len) {
@@ -588,8 +541,8 @@ QStringList WriteGeoModel::getParentNode()
 
 	// get the parent volume, if this is not the Root volume
 	const GeoVPhysVol* parentNode = nullptr;
-	QVariant parentId = "NULL";
-	QString parentType = "NULL";
+	unsigned int parentId = 0;
+  std::string parentType = "NULL";
 
 		if (len >= 1)
 		{
@@ -602,43 +555,39 @@ QStringList WriteGeoModel::getParentNode()
 				parentType = getGeoTypeFromVPhysVol(parentNode);
 
 				// get the parent memory address
-				QString parentAddress = getAddressStringFromPointer(parentNode);
-				//JFB Commented out: qDebug() << "-- parent's address:" << parentNode;
+        std::string parentAddress = getAddressStringFromPointer(parentNode);
 
 				// get the id of the parent node, which should be stored already in the DB
 				if (isAddressStored(parentAddress)) {
 					parentId = getStoredIdFromAddress(parentAddress);
 				}
 				else {
-					// qFatal("FATAL ERROR!!! - The parent node of this child should has been stored in the DB already, but it was not found!!");
-					qWarning() << "The parent node of this child node seems to not having be stored in the DB yet! [It is normal if it is the root volume or a transformation node used for example only in the definition of a 'GeoShapeShift' instance]";
+          std::cout << "The parent node of this child node seems to not having be stored in the DB yet! [It is normal if it is the root volume or a transformation node used for example only in the definition of a 'GeoShapeShift' instance]" << std::endl;
 				}
-				//JFB Commented out: qDebug() << "-- parent's LogVol name:" << QString::fromStdString(parentNode->getLogVol()->getName());
 			}
 		}
 		else{
-			// qWarning() << "ERROR!! Len == 1, but this cannot be the Root volume!";
-			qWarning() << "WARNING!! Len == 0, but this cannot be the Root volume!";
+      std::cout << "WARNING!! Len == 0, but this cannot be the Root volume!" << std::endl;
 		}
 
-		QStringList parentList;
-		parentList << parentId.toString() << parentType;
+  std::vector<std::string> parentList;
+  parentList.insert(parentList.begin(), {std::to_string(parentId), parentType});
 
 		return  parentList;
 }
 
 
 //__________________________________________________________________
-QVariant WriteGeoModel::storeShape(const GeoShape* shape)
+unsigned int WriteGeoModel::storeShape(const GeoShape* shape)
 {
-	QString shapeType = QString::fromStdString(shape->type());
-	//JFB Commented out: qDebug() << "storeShape() - shape name:" << shapeType  << ", address:" << shape;
-	
-    // LArCustomShape is deprecated.  Write it out as a GeoUnidentifiedShape;
-    if (shapeType=="CustomShape") shapeType="UnidentifiedShape";
+//  QString shapeType = QString::fromStdString(shape->type());
+  std::string shapeType = shape->type();
+  
+  // LArCustomShape is deprecated.  Write it out as a GeoUnidentifiedShape;
+  if (shapeType=="CustomShape") shapeType="UnidentifiedShape";
     
-    // get shape parameters
-	QString shapePars = getShapeParameters(shape);
+  // get shape parameters
+  std::string shapePars = getShapeParameters(shape);
 	
 	// store the shape in the DB and returns the ID
 	return storeObj(shape, shapeType, shapePars);
@@ -646,47 +595,30 @@ QVariant WriteGeoModel::storeShape(const GeoShape* shape)
 
 
 //______________________________________________________________________
-QVariant WriteGeoModel::storeMaterial(const GeoMaterial* mat)
+unsigned int WriteGeoModel::storeMaterial(const GeoMaterial* mat)
 {
-	const QString matName = QString::fromStdString(mat->getName());   //The name of the material.
-	const QString matID = QString::number(mat->getID());              //Gives an integral identifier for the material.For convenience.
-	const QString matDensity = QString::number(mat->getDensity());	  //The density of the material.
+  const std::string matName = mat->getName();   //The name of the material.
+	const double matDensity = mat->getDensity();	  //The density of the material.
 	const unsigned int numElements = mat->getNumElements();
 
-	const QString matNumElements = QString::number(numElements);
-	//JFB Commented out: qDebug() << "storeMaterial() - material name:" << matName
-	//		<< ", address:" << mat
-	//		<< ", matID:" << matID
-	//		<< ", matDensity:" << matDensity
-	//		<< ", matNumElements:" << matNumElements;
-
 	// loop over the elements composing the material
-	QString matElements;
-	QStringList matElementsList;
+  std::string matElements;
+  std::vector<std::string> matElementsList;
 	for (unsigned int i=0; i < numElements; i++) {
 
 		//Gets the i-th element.
 		const GeoElement* element = mat->getElement(i);
 		std::string elName = element->getName();
-		/*
-		  std::cout << "\t element n. " << i << std::endl;
-		  std::cout << "\t element address: " << element << std::endl;
-		  std::cout << "\t element name: " << elName << std::endl;
-		 */
 
 		// Store the element and get its DataBase ID
-		QVariant elementId = storeElement(element);
+		unsigned int elementId = storeElement(element);
 
 		//Gets the fraction by weight of the i-th element
-		const QString elementFraction = QString::number( mat->getFraction(i) );
+    const std::string elementFraction = std::to_string( mat->getFraction(i) );
 
-		//JFB Commented out: qDebug() << "\t--> element ID: " << elementId.toString() << " - getFraction:" << elementFraction;
-
-		matElementsList << elementId.toString() + ":" + elementFraction;
+    matElementsList.push_back( std::to_string(elementId) + ":" + elementFraction );
 	}
-	matElements = matElementsList.join(";");
-
-	//JFB Commented out: qDebug() << "\t==> material's elements:" << matElements;
+	matElements = joinVectorStrings(matElementsList, ";");
 
 	// store the material in the DB and returns the ID
 	return storeObj(mat, matName, matDensity, matElements);
@@ -694,19 +626,19 @@ QVariant WriteGeoModel::storeMaterial(const GeoMaterial* mat)
 
 
 //_______________________________________________________________________
-QVariant WriteGeoModel::storeElement(const GeoElement* el)
+unsigned int WriteGeoModel::storeElement(const GeoElement* el)
 {
 	//	The name of the element, e.g. "Carbon".
-	const QString elName = QString::fromStdString(el->getName());
+  const std::string elName = el->getName();
 
 	//	The chemical symbol for the element, e.g. C, O, S, Na....
-	const QString elSymbol = QString::fromStdString(el->getSymbol());
+  const std::string elSymbol = el->getSymbol();
 
 	//	The atomic number Z for the material.
-	const QString elZ = QString::number(el->getZ());
+  const double elZ = el->getZ();
 
 	//	The average atomic mass for the element.
-	const QString elA = QString::number(el->getA());
+  const double elA = el->getA();
 
 	// store the material in the DB and returns the ID
 	return storeObj(el, elName, elSymbol, elZ, elA);
@@ -714,22 +646,19 @@ QVariant WriteGeoModel::storeElement(const GeoElement* el)
 
 
 //_______________________________________________________________________
-QVariant WriteGeoModel::storeTranform(const GeoTransform* node)
+unsigned int WriteGeoModel::storeTranform(const GeoTransform* node)
 {
 	/*
 	 * STORE THE OBJECT IN THE DB
 	 */
 
-	 QString address = getAddressStringFromPointer( node );
+  std::string address = getAddressStringFromPointer( node );
 
-	 QVariant trId;
+	 unsigned int trId = 0;
 
 	// check if this object has been stored already
 	if (! isAddressStored(address)) {
 
-		//JFB Commented out: qDebug() << "New Transform, storing it...";
-
-
 				// TODO: simplify and put common code in a separate class
 
 				/*
@@ -782,7 +711,6 @@ QVariant WriteGeoModel::storeTranform(const GeoTransform* node)
 				tr(3,3)=1;
 
 		std::vector<double> params = getTransformParameters(tr);
-		//JFB Commented out: qDebug() << "Transform parameters:" << QVector<double>::fromStdVector(params);
 
 		/*
 		 * STORE THE OBJECT IN THE DB
@@ -791,37 +719,35 @@ QVariant WriteGeoModel::storeTranform(const GeoTransform* node)
 		// store the object in the DB
 		if ( dynamic_cast<const GeoAlignableTransform*>(node) ) {
 			trId = storeObj(dynamic_cast<const GeoAlignableTransform*>(node), params);
-			//JFB Commented out: qDebug() << "AlignableTransform stored. Id:" << trId.toString();
 		}
 		else if ( dynamic_cast<const GeoTransform*>(node) ) {
 			trId = storeObj(dynamic_cast<const GeoTransform*>(node), params);
-			//JFB Commented out: qDebug() << "Transform stored. Id:" << trId.toString();
 		}
 		else {
-			std::cout << "WARNING!!! - This type of transformation still needs to be persistified!!" << std::endl;
+			std::cout << "ERROR!!! - Unknown type of transformation! Exiting..." << std::endl;
+      exit(EXIT_FAILURE);
 		}
-
 	} else {
 		trId = getStoredIdFromAddress(address);
-		//JFB Commented out: qDebug() << "Transform already stored in the DB. Id:" << trId.toString();
 	}
-
+  
+  if(!trId) {
+    std::cout << "ERROR!!! - Error while handling a Transform node! Exiting..." << std::endl;
+    exit(EXIT_FAILURE);
+  }
 	return trId;
 }
 
 
 void WriteGeoModel::handleReferencedVPhysVol (const GeoVPhysVol *vol)
 {
-	//JFB Commented out: qDebug() << "\nWriteGeoModel::handleReferencedVPhysVol(GeoVPhysVol*)";
-
 	// qDebug() << "PhysVol's LogVol name:" << QString::fromStdString(vol->getLogVol()->getName());
 
 	// get the address string for the current volume
-	QString address = getAddressStringFromPointer( vol );
+  std::string address = getAddressStringFromPointer( vol );
 
-	QVariant parentId = "NULL";
+	unsigned int parentId = 0;
 
-	//JFB Commented out: qDebug() << "setting 'unconnected' to: false";
 	m_unconnectedTree = false;
 
 	// get the parent volume, if this is not the Root volume
@@ -832,19 +758,18 @@ void WriteGeoModel::handleReferencedVPhysVol (const GeoVPhysVol *vol)
 	const GeoVPhysVol* parentNode = p ? dynamic_cast<const GeoVPhysVol*>( &(*(vol->getParent() ))) : nullptr;
 	
 	if (parentNode) {
-		QString parentAddress = getAddressStringFromPointer(parentNode);
-		//JFB Commented out: qDebug() << "--> parent's address:" << parentNode;
+    std::string parentAddress = getAddressStringFromPointer(parentNode);
 
 		if (isAddressStored(parentAddress))
 			parentId = getStoredIdFromAddress(parentAddress);
 		//		else
 		//			qFatal("FATAL ERROR!!! - The parent node of this child should has been stored in the DB already, but it was not found!!");
 
-		//JFB Commented out: qDebug() << "--> parent's LogVol name:" << QString::fromStdString(parentNode->getLogVol()->getName());
+		//qDebug() << "--> parent's LogVol name:" << QString::fromStdString(parentNode->getLogVol()->getName());
 	}
 	else {
-		//JFB Commented out: qDebug() << "NULL parent node!! --> it seems to be an unconnected subtree.";
-		//JFB Commented out: qDebug() << "setting 'unconnected' to: true";
+		//qDebug() << "NULL parent node!! --> it seems to be an unconnected subtree.";
+		//qDebug() << "setting 'unconnected' to: true";
 		m_unconnectedTree = true;
 	}
 
@@ -865,47 +790,43 @@ void WriteGeoModel::handleReferencedVPhysVol (const GeoVPhysVol *vol)
 	// check if this object has been stored already
 	if (! isAddressStored(address)) {
 
-		//JFB Commented out: qDebug() << "This is a new root PhysVol node of an 'unconnected' tree, so we start another action on it to dump it into the DB...";
+		//qDebug() << "This is a new root PhysVol node of an 'unconnected' tree, so we start another action on it to dump it into the DB...";
 
 		// Dump the tree volumes into the DB
 		vol->exec(this); // TODO: check if the new action overwrites the id of the volumes already in the DB...!!!
 
 	} else {
-		//JFB Commented out: qDebug() << "The referenced volume has been stored already. Skipping...";
+		//qDebug() << "The referenced volume has been stored already. Skipping...";
 	}
 	// at the end, we make sure we reset the status
-	//JFB Commented out: qDebug() << "setting 'unconnected' to: false";
+	//qDebug() << "setting 'unconnected' to: false";
 	m_unconnectedTree = false;
 }
 
 
 // Get shape parameters
-QString WriteGeoModel::getShapeParameters(const GeoShape* shape)
+std::string WriteGeoModel::getShapeParameters(const GeoShape* shape)
 {
-	const QString shapeType = QString::fromStdString(shape->type());
-	//JFB Commented out: qDebug() << "shapeType:" << shapeType;
+  const std::string shapeType = shape->type();
 
-	QString shapePars = "";
+  std::string shapePars = "";
+  std::vector<std::string> pars;
 
 	if (shapeType == "Box") {
-		//JFB Commented out: qDebug() << "get GeoBox parameters";
-		QStringList pars;
 		const GeoBox* box = dynamic_cast<const GeoBox*>(shape);
-		pars << "XHalfLength=" + QString::number(box->getXHalfLength()) ;
-		pars << "YHalfLength=" + QString::number(box->getYHalfLength()) ;
-		pars << "ZHalfLength=" + QString::number(box->getZHalfLength()) ;
-		shapePars = pars.join(";");
+		pars.push_back("XHalfLength=" + std::to_string(box->getXHalfLength())) ;
+		pars.push_back("YHalfLength=" + std::to_string(box->getYHalfLength())) ;
+		pars.push_back("ZHalfLength=" + std::to_string(box->getZHalfLength())) ;
+		shapePars = joinVectorStrings(pars,";");
 	} else if (shapeType == "Cons") {
-		QStringList pars;
 		const GeoCons* shapeIn = dynamic_cast<const GeoCons*>(shape);
-		pars << "RMin1=" + QString::number(shapeIn->getRMin1()) ;
-		pars << "RMin2=" + QString::number(shapeIn->getRMin2()) ;
-		pars << "RMax1=" + QString::number(shapeIn->getRMax1()) ;
-		pars << "RMax2=" + QString::number(shapeIn->getRMax2()) ;
-		pars << "DZ=" + QString::number(shapeIn->getDZ()) ;
-		pars << "SPhi=" + QString::number(shapeIn->getSPhi()) ;
-		pars << "DPhi=" + QString::number(shapeIn->getDPhi()) ;
-		shapePars = pars.join(";");
+		pars.push_back("RMin1=" + std::to_string(shapeIn->getRMin1())) ;
+		pars.push_back("RMin2=" + std::to_string(shapeIn->getRMin2())) ;
+		pars.push_back("RMax1=" + std::to_string(shapeIn->getRMax1())) ;
+		pars.push_back("RMax2=" + std::to_string(shapeIn->getRMax2())) ;
+		pars.push_back("DZ=" + std::to_string(shapeIn->getDZ())) ;
+		pars.push_back("SPhi=" + std::to_string(shapeIn->getSPhi())) ;
+		pars.push_back("DPhi=" + std::to_string(shapeIn->getDPhi())) ;
 	} else if (shapeType == "Torus") {
 		// Member Data:
 		// * Rmax - outside radius of the torus tube
@@ -915,209 +836,181 @@ QString WriteGeoModel::getShapeParameters(const GeoShape* shape)
 		// * SPhi - starting angle of the segment in radians
 		// * DPhi - delta angle of the segment in radians
 		//
-		QStringList pars;
 		const GeoTorus* shapeIn = dynamic_cast<const GeoTorus*>(shape);
-		pars << "Rmin=" + QString::number(shapeIn->getRMin()) ;
-		pars << "Rmax=" + QString::number(shapeIn->getRMax()) ;
-		pars << "Rtor=" + QString::number(shapeIn->getRTor()) ;
-		pars << "SPhi=" + QString::number(shapeIn->getSPhi()) ;
-		pars << "DPhi=" + QString::number(shapeIn->getDPhi()) ;
-		shapePars = pars.join(";");
-	} else if (shapeType == "Para") {
-		QStringList pars;
+		pars.push_back("Rmin=" + std::to_string(shapeIn->getRMin())) ;
+		pars.push_back("Rmax=" + std::to_string(shapeIn->getRMax())) ;
+		pars.push_back("Rtor=" + std::to_string(shapeIn->getRTor())) ;
+		pars.push_back("SPhi=" + std::to_string(shapeIn->getSPhi())) ;
+		pars.push_back("DPhi=" + std::to_string(shapeIn->getDPhi())) ;
+	}
+  else if (shapeType == "Para") {
 		const GeoPara* shapeIn = dynamic_cast<const GeoPara*>(shape);
-		pars << "XHalfLength=" + QString::number(shapeIn->getXHalfLength()) ;
-		pars << "YHalfLength=" + QString::number(shapeIn->getYHalfLength()) ;
-		pars << "ZHalfLength=" + QString::number(shapeIn->getZHalfLength()) ;
-		pars << "Alpha=" + QString::number(shapeIn->getAlpha()) ;
-		pars << "Theta=" + QString::number(shapeIn->getTheta()) ;
-		pars << "Phi=" + QString::number(shapeIn->getPhi()) ;
-		shapePars = pars.join(";");
-	} else if (shapeType == "Pcon") {
-		QStringList pars;
+		pars.push_back("XHalfLength=" + std::to_string(shapeIn->getXHalfLength())) ;
+		pars.push_back("YHalfLength=" + std::to_string(shapeIn->getYHalfLength())) ;
+		pars.push_back("ZHalfLength=" + std::to_string(shapeIn->getZHalfLength())) ;
+		pars.push_back("Alpha=" + std::to_string(shapeIn->getAlpha())) ;
+		pars.push_back("Theta=" + std::to_string(shapeIn->getTheta())) ;
+		pars.push_back("Phi=" + std::to_string(shapeIn->getPhi())) ;
+	}
+  else if (shapeType == "Pcon") {
 		const GeoPcon* shapeIn = dynamic_cast<const GeoPcon*>(shape);
-		pars << "SPhi=" + QString::number(shapeIn->getSPhi());
-		pars << "DPhi=" + QString::number(shapeIn->getDPhi());
+		pars.push_back("SPhi=" + std::to_string(shapeIn->getSPhi()));
+		pars.push_back("DPhi=" + std::to_string(shapeIn->getDPhi()));
 		// get number of Z planes and loop over them
 		const int nZplanes = shapeIn->getNPlanes();
-		pars << "NZPlanes=" + QString::number(nZplanes);
+		pars.push_back("NZPlanes=" + std::to_string(nZplanes));
 		for (int i=0; i<nZplanes; ++i) {
-			pars << "ZPos=" + QString::number(shapeIn->getZPlane(i));
-			pars << "ZRmin=" + QString::number(shapeIn->getRMinPlane(i));
-			pars << "ZRmax=" + QString::number(shapeIn->getRMaxPlane(i));
+			pars.push_back("ZPos=" + std::to_string(shapeIn->getZPlane(i)));
+			pars.push_back("ZRmin=" + std::to_string(shapeIn->getRMinPlane(i)));
+			pars.push_back("ZRmax=" + std::to_string(shapeIn->getRMaxPlane(i)));
 		}
-		shapePars = pars.join(";");
-	} else if (shapeType == "Pgon") {
-		QStringList pars;
+	}
+  else if (shapeType == "Pgon") {
 		const GeoPgon* shapeIn = dynamic_cast<const GeoPgon*>(shape);
-		pars << "SPhi=" + QString::number(shapeIn->getSPhi()) ;
-		pars << "DPhi=" + QString::number(shapeIn->getDPhi()) ;
-		pars << "NSides=" + QString::number(shapeIn->getNSides()) ;
+		pars.push_back("SPhi=" + std::to_string(shapeIn->getSPhi())) ;
+		pars.push_back("DPhi=" + std::to_string(shapeIn->getDPhi())) ;
+		pars.push_back("NSides=" + std::to_string(shapeIn->getNSides())) ;
 		// get number of Z planes and loop over them
 		const int nZplanes = shapeIn->getNPlanes();
-		pars << "NZPlanes=" + QString::number(nZplanes);
+		pars.push_back("NZPlanes=" + std::to_string(nZplanes));
 		for (int i=0; i<nZplanes; ++i) {
-			pars << "ZPos=" + QString::number(shapeIn->getZPlane(i));
-			pars << "ZRmin=" + QString::number(shapeIn->getRMinPlane(i));
-			pars << "ZRmax=" + QString::number(shapeIn->getRMaxPlane(i));
+			pars.push_back("ZPos=" + std::to_string(shapeIn->getZPlane(i)));
+			pars.push_back("ZRmin=" + std::to_string(shapeIn->getRMinPlane(i)));
+			pars.push_back("ZRmax=" + std::to_string(shapeIn->getRMaxPlane(i)));
 		}
-		shapePars = pars.join(";");
-	} else if (shapeType == "SimplePolygonBrep") {
-		QStringList pars;
+	}
+  else if (shapeType == "SimplePolygonBrep") {
 		const GeoSimplePolygonBrep* shapeIn = dynamic_cast<const GeoSimplePolygonBrep*>(shape);
-		pars << "DZ=" + QString::number(shapeIn->getDZ()) ;
+		pars.push_back("DZ=" + std::to_string(shapeIn->getDZ())) ;
 		// get number of vertices and loop over them
 		const int nVertices = shapeIn->getNVertices();
-		pars << "NVertices=" + QString::number(nVertices);
+		pars.push_back("NVertices=" + std::to_string(nVertices));
 		for (int i=0; i<nVertices; ++i) {
-			pars << "xV=" + QString::number(shapeIn->getXVertex(i));
-			pars << "yV=" + QString::number(shapeIn->getYVertex(i));
+			pars.push_back("xV=" + std::to_string(shapeIn->getXVertex(i)));
+			pars.push_back("yV=" + std::to_string(shapeIn->getYVertex(i)));
 		}
-		shapePars = pars.join(";");
-	} else if (shapeType == "Trap") {
-		QStringList pars;
+	}
+  else if (shapeType == "Trap") {
 		const GeoTrap* shapeIn = dynamic_cast<const GeoTrap*>(shape);
-		pars << "ZHalfLength=" + QString::number(shapeIn->getZHalfLength()) ;
-		pars << "Theta=" + QString::number(shapeIn->getTheta()) ;
-		pars << "Phi=" + QString::number(shapeIn->getPhi()) ;
-		pars << "Dydzn=" + QString::number(shapeIn->getDydzn()) ;
-		pars << "Dxdyndzn=" + QString::number(shapeIn->getDxdyndzn()) ;
-		pars << "Dxdypdzn=" + QString::number(shapeIn->getDxdypdzn()) ;
-		pars << "Angleydzn=" + QString::number(shapeIn->getAngleydzn()) ;
-		pars << "Dydzp=" + QString::number(shapeIn->getDydzp()) ;
-		pars << "Dxdyndzp=" + QString::number(shapeIn->getDxdyndzp()) ;
-		pars << "Dxdypdzp=" + QString::number(shapeIn->getDxdypdzp()) ;
-		pars << "Angleydzp=" + QString::number(shapeIn->getAngleydzp()) ;
-		shapePars = pars.join(";");
-	} else if (shapeType == "Trd") {
-		QStringList pars;
+		pars.push_back("ZHalfLength=" + std::to_string(shapeIn->getZHalfLength())) ;
+		pars.push_back("Theta=" + std::to_string(shapeIn->getTheta())) ;
+		pars.push_back("Phi=" + std::to_string(shapeIn->getPhi())) ;
+		pars.push_back("Dydzn=" + std::to_string(shapeIn->getDydzn())) ;
+		pars.push_back("Dxdyndzn=" + std::to_string(shapeIn->getDxdyndzn())) ;
+		pars.push_back("Dxdypdzn=" + std::to_string(shapeIn->getDxdypdzn())) ;
+		pars.push_back("Angleydzn=" + std::to_string(shapeIn->getAngleydzn())) ;
+		pars.push_back("Dydzp=" + std::to_string(shapeIn->getDydzp())) ;
+		pars.push_back("Dxdyndzp=" + std::to_string(shapeIn->getDxdyndzp())) ;
+		pars.push_back("Dxdypdzp=" + std::to_string(shapeIn->getDxdypdzp())) ;
+		pars.push_back("Angleydzp=" + std::to_string(shapeIn->getAngleydzp())) ;
+	}
+  else if (shapeType == "Trd") {
 		const GeoTrd* shapeIn = dynamic_cast<const GeoTrd*>(shape);
-		pars << "XHalfLength1=" + QString::number(shapeIn->getXHalfLength1()) ;
-		pars << "XHalfLength2=" + QString::number(shapeIn->getXHalfLength2()) ;
-		pars << "YHalfLength1=" + QString::number(shapeIn->getYHalfLength1()) ;
-		pars << "YHalfLength2=" + QString::number(shapeIn->getYHalfLength2()) ;
-		pars << "ZHalfLength=" + QString::number(shapeIn->getZHalfLength()) ;
-		shapePars = pars.join(";");
-	} else if (shapeType == "Tube") {
-		QStringList pars;
+		pars.push_back("XHalfLength1=" + std::to_string(shapeIn->getXHalfLength1())) ;
+		pars.push_back("XHalfLength2=" + std::to_string(shapeIn->getXHalfLength2())) ;
+		pars.push_back("YHalfLength1=" + std::to_string(shapeIn->getYHalfLength1())) ;
+		pars.push_back("YHalfLength2=" + std::to_string(shapeIn->getYHalfLength2())) ;
+		pars.push_back("ZHalfLength=" + std::to_string(shapeIn->getZHalfLength())) ;
+	}
+  else if (shapeType == "Tube") {
 		const GeoTube* tube = dynamic_cast<const GeoTube*>(shape);
-		pars << "RMin=" + QString::number(tube->getRMin()) ;
-		pars << "RMax=" + QString::number(tube->getRMax()) ;
-		pars << "ZHalfLength=" + QString::number(tube->getZHalfLength()) ;
-		shapePars = pars.join(";");
-	} else if (shapeType == "Tubs") {
-		QStringList pars;
+		pars.push_back("RMin=" + std::to_string(tube->getRMin())) ;
+		pars.push_back("RMax=" + std::to_string(tube->getRMax())) ;
+		pars.push_back("ZHalfLength=" + std::to_string(tube->getZHalfLength())) ;
+	}
+  else if (shapeType == "Tubs") {
 		const GeoTubs* shapeIn = dynamic_cast<const GeoTubs*>(shape);
-		pars << "RMin=" + QString::number(shapeIn->getRMin()) ;
-		pars << "RMax=" + QString::number(shapeIn->getRMax()) ;
-		pars << "ZHalfLength=" + QString::number(shapeIn->getZHalfLength()) ;
-		pars << "SPhi=" + QString::number(shapeIn->getSPhi()) ;
-		pars << "DPhi=" + QString::number(shapeIn->getDPhi()) ;
-		shapePars = pars.join(";");
-	} else if (shapeType == "TessellatedSolid") {
-		QStringList pars;
+		pars.push_back("RMin=" + std::to_string(shapeIn->getRMin())) ;
+		pars.push_back("RMax=" + std::to_string(shapeIn->getRMax())) ;
+		pars.push_back("ZHalfLength=" + std::to_string(shapeIn->getZHalfLength())) ;
+		pars.push_back("SPhi=" + std::to_string(shapeIn->getSPhi())) ;
+		pars.push_back("DPhi=" + std::to_string(shapeIn->getDPhi())) ;
+	}
+  else if (shapeType == "TessellatedSolid") {
 		const GeoTessellatedSolid* shapeIn = dynamic_cast<const GeoTessellatedSolid*>(shape);
 		// get number of facets
 		const size_t nFacets = shapeIn->getNumberOfFacets();
-		pars << "nFacets=" + QString::number(nFacets);
+		pars.push_back("nFacets=" + std::to_string(nFacets));
 		// loop over the facets
 		for (size_t i=0; i<nFacets; ++i) {
 			GeoFacet* facet = shapeIn->getFacet(i);
 			// get GeoFacet actual implementation
-			if (dynamic_cast<GeoTriangularFacet*>(facet))        pars << "TRI";
-			else if (dynamic_cast<GeoQuadrangularFacet*>(facet)) pars << "QUAD";
+			if (dynamic_cast<GeoTriangularFacet*>(facet))        pars.push_back("TRI");
+			else if (dynamic_cast<GeoQuadrangularFacet*>(facet)) pars.push_back("QUAD");
 			// get vertex type (ABSOLUTE/RELATIVE)
 			GeoFacet::GeoFacetVertexType facetVertexType = facet->getVertexType();
-			if (facetVertexType == GeoFacet::ABSOLUTE) pars << "vT=ABSOLUTE";
-			if (facetVertexType == GeoFacet::RELATIVE) pars << "vT=RELATIVE";
+			if (facetVertexType == GeoFacet::ABSOLUTE) pars.push_back("vT=ABSOLUTE");
+			if (facetVertexType == GeoFacet::RELATIVE) pars.push_back("vT=RELATIVE");
 			// get number of vertices and loop over them
 			const size_t nVertices = facet->getNumberOfVertices();
-			pars << "nV=" + QString::number(nVertices);
+			pars.push_back("nV=" + std::to_string(nVertices));
 			for (size_t i=0; i<nVertices; ++i) {
 				GeoFacetVertex facetVertex = facet->getVertex(i);
-				pars << "xV=" + QString::number( facetVertex[0] );
-				pars << "yV=" + QString::number( facetVertex[1] );
-				pars << "zV=" + QString::number( facetVertex[2] );
+				pars.push_back("xV=" + std::to_string( facetVertex[0] ));
+				pars.push_back("yV=" + std::to_string( facetVertex[1] ));
+				pars.push_back("zV=" + std::to_string( facetVertex[2] ));
 			}
 		}
-		shapePars = pars.join(";");
-		//qDebug() << "Tessellated pars:" << shapePars; // debug
 	}
 	else if (shapeType == "Intersection") {
-		//JFB Commented out: qDebug() << "get GeoShapeIntersection parameters";
-		QStringList pars;
 		const GeoShapeIntersection* shapeIn = dynamic_cast<const GeoShapeIntersection*>(shape);
 		// get the referenced Shape used in the 'union' operation, store it in the DB
 		const GeoShape* shapeOpA = shapeIn->getOpA();
-		QVariant shapeIdA = storeShape(shapeOpA);
+		const unsigned int shapeIdA = storeShape(shapeOpA);
 		const GeoShape* shapeOpB = shapeIn->getOpB();
-		QVariant shapeIdB = storeShape(shapeOpB);
-		pars << "opA=" + QString::number( shapeIdA.toUInt() ) ;
-		pars << "opB=" + QString::number( shapeIdB.toUInt() ) ;
-		shapePars = pars.join(";");
+		const unsigned int shapeIdB = storeShape(shapeOpB);
+		pars.push_back("opA=" + std::to_string( shapeIdA )) ;
+		pars.push_back("opB=" + std::to_string( shapeIdB )) ;
 	}
 	else if (shapeType == "Shift") {
-		//JFB Commented out: qDebug() << "get GeoShapeShift parameters";
-		QStringList pars;
 		const GeoShapeShift* shapeIn = dynamic_cast<const GeoShapeShift*>(shape);
 
 		// get the referenced Shape used in the 'shift' operation, store it in the DB
 		const GeoShape* shapeOp = shapeIn->getOp();
-		QVariant shapeId = storeShape(shapeOp);
+		const unsigned int shapeId = storeShape(shapeOp);
 
 		// get the Transformation, store it in the DB
 		GeoTransform* transf = new GeoTransform( shapeIn->getX() );
-		QVariant trId = storeTranform(transf);
+		const unsigned int trId = storeTranform(transf);
 
-		pars << "A=" + QString::number( shapeId.toUInt() ) ;
-		pars << "X=" + QString::number( trId.toUInt() ) ;
-		shapePars = pars.join(";");
+		pars.push_back("A=" + std::to_string( shapeId ));
+		pars.push_back("X=" + std::to_string( trId ));
 	}
 	else if (shapeType == "Subtraction") {
-		//JFB Commented out: qDebug() << "get GeoShapeSubtraction parameters";
-		QStringList pars;
 		const GeoShapeSubtraction* shapeIn = dynamic_cast<const GeoShapeSubtraction*>(shape);
 		// get the referenced Shape used in the 'union' operation, store it in the DB
 		const GeoShape* shapeOpA = shapeIn->getOpA();
-		QVariant shapeIdA = storeShape(shapeOpA);
+		const unsigned int shapeIdA = storeShape(shapeOpA);
 		const GeoShape* shapeOpB = shapeIn->getOpB();
-		QVariant shapeIdB = storeShape(shapeOpB);
-		pars << "opA=" + QString::number( shapeIdA.toUInt() ) ;
-		pars << "opB=" + QString::number( shapeIdB.toUInt() ) ;
-		shapePars = pars.join(";");
+		const unsigned int shapeIdB = storeShape(shapeOpB);
+		pars.push_back("opA=" + std::to_string( shapeIdA ));
+		pars.push_back("opB=" + std::to_string( shapeIdB ));
 	}
 	else if (shapeType == "Union") {
-		//JFB Commented out: qDebug() << "get GeoShapeUnion parameters";
-		QStringList pars;
 		const GeoShapeUnion* shapeIn = dynamic_cast<const GeoShapeUnion*>(shape);
 
 		// get the referenced Shape used in the 'union' operation, store it in the DB
 		const GeoShape* shapeOpA = shapeIn->getOpA();
-		QVariant shapeIdA = storeShape(shapeOpA);
+		unsigned int shapeIdA = storeShape(shapeOpA);
 		const GeoShape* shapeOpB = shapeIn->getOpB();
-		QVariant shapeIdB = storeShape(shapeOpB);
+		unsigned int shapeIdB = storeShape(shapeOpB);
 
-		pars << "opA=" + QString::number( shapeIdA.toUInt() ) ;
-		pars << "opB=" + QString::number( shapeIdB.toUInt() ) ;
-		shapePars = pars.join(";");
+		pars.push_back("opA=" + std::to_string( shapeIdA )) ;
+		pars.push_back("opB=" + std::to_string( shapeIdB )) ;
 	}
 	else if (shapeType=="GenericTrap") {
-	  QStringList pars;
 	  const GeoGenericTrap * shapeIn = dynamic_cast<const GeoGenericTrap*>(shape);
-	  pars << "ZHalfLength=" + QString::number(shapeIn->getZHalfLength());
-	  pars << "NVertices="   + QString::number(shapeIn->getVertices().size());
+	  pars.push_back("ZHalfLength=" + std::to_string(shapeIn->getZHalfLength()));
+	  pars.push_back("NVertices="   + std::to_string(shapeIn->getVertices().size()));
 	  for (int i=0; i<shapeIn->getVertices().size(); ++i) {
-	    pars << "X=" + QString::number(shapeIn->getVertices()[i](0));
-	    pars << "Y=" + QString::number(shapeIn->getVertices()[i](1));
+	    pars.push_back("X=" + std::to_string(shapeIn->getVertices()[i](0)));
+	    pars.push_back("Y=" + std::to_string(shapeIn->getVertices()[i](1)));
 	  }
-	  shapePars = pars.join(";");
 	}
 	else if (shapeType=="UnidentifiedShape") {
 	  const GeoUnidentifiedShape *shapeIn=dynamic_cast<const GeoUnidentifiedShape *> (shape);
-	  QStringList pars;
-	  pars << "name="+QString::fromStdString(shapeIn->name());
-	  pars << "asciiData="+QString::fromStdString(shapeIn->asciiData());
-	  shapePars=pars.join(";");
+	  pars.push_back("name="+shapeIn->name());
+	  pars.push_back("asciiData="+shapeIn->asciiData());
   }
   //LAr custom shape
   // else if(shapeType == "CustomShape") {
@@ -1129,9 +1022,12 @@ QString WriteGeoModel::getShapeParameters(const GeoShape* shape)
   //   shapePars=pars.join(";");
   // }
   else {
-    std::cout << "\n\tWARNING!!! - Shape '" << shapeType.toStdString() << "' needs to be persistified!!\n\n";
-    m_objectsNotPersistified << shapeType;
+    std::cout << "\n\tWARNING!!! - Shape '" << shapeType << "' needs to be persistified!!\n\n";
+    printStdVectorStrings(m_objectsNotPersistified);
   }
+                     
+
+  shapePars = joinVectorStrings(pars,";");
 
   return shapePars;
 
@@ -1166,29 +1062,29 @@ std::vector<double> WriteGeoModel::getTransformParameters(GeoTrf::Transform3D tr
 
 WriteGeoModel::WriteGeoModel(GMDBManager &db)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel: constructor";
-
 	// init variables
 	m_len = 0;
 	m_len_nChild = 0;
 	m_rootVolumeFound = false;
 	m_unconnectedTree = false;
 
-	// init anche check the database handle
+	// get a handle to the database and check it
 	m_dbManager = &db;
-	if (m_dbManager->isOpen()) {
-		//JFB Commented out: qDebug() << "OK! Database is open!";
+	if (m_dbManager->checkIsDBOpen()) { 
+//    if(m_debug) std::cout << "OK! Database is open!" << std::endl;
 	}
 	else {
-		//JFB Commented out: qDebug() << "Database is not open!";
+		//qDebug() << "Database is not open!";
 	}
-
-    m_dbpath = m_dbManager->getDBFilePath();
+  // initialize the database (create tables, ...)
+  m_dbManager->initDB();
+  // build caches
+  m_dbManager->loadGeoNodeTypesAndBuildCache();
+  // get the database file path
+  m_dbpath = m_dbManager->getDBFilePath();
 
 	// get DB metadata
 	m_memMap_Tables = m_dbManager->getAll_NodeTypesTableIDs();
-	// qDebug() << "m_memMap_Tables:" << m_memMap_Tables;
-
 }
 
 WriteGeoModel::~WriteGeoModel()
@@ -1199,469 +1095,386 @@ WriteGeoModel::~WriteGeoModel()
 
 void WriteGeoModel::showMemoryMap()
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::showMemoryMap()";
-
-	QMap<QString, QVariant>::const_iterator it = m_memMap.constBegin();
-	while (it != m_memMap.constEnd()) {
-		std::cout << it.key().toStdString() << ": " << it.value().toString().toStdString() << std::endl;
+  std::unordered_map<std::string, unsigned int>::const_iterator it = m_memMap.begin();
+	while (it != m_memMap.end()) {
+		std::cout << it->first << ": " << it->second << std::endl;
 		++it;
 	}
 }
 
 
 
-QVariant WriteGeoModel::storeObj(const GeoMaterial* pointer, const QString name, const QString density, const QString elements)
+  unsigned int WriteGeoModel::storeObj(const GeoMaterial* pointer, const std::string &name, const double &density, const std::string &elements)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::storeObj(GeoMaterial*) - name:" << name << "- address:" << pointer << "- density:" << density << "- elements:" << elements;
-
-	QString address = getAddressStringFromPointer( pointer );
-	QVariant materialId;
+  std::string address = getAddressStringFromPointer( pointer );
+	unsigned int materialId;
 
 	if (! isAddressStored(address)) {
-		//JFB Commented out: qDebug() << "New Material! Storing it...";
-
 		materialId = addMaterial(name, density, elements);
-
 		storeAddress( address, materialId );
 	}
 	else {
-		//JFB Commented out: qDebug() << "Material node stored already. Getting ID from the memory map...";
 		materialId = getStoredIdFromAddress(address);
 	}
-	//JFB Commented out: qDebug() << "materialId:" << materialId;
 	return materialId;
 }
 
 
-QVariant WriteGeoModel::storeObj(const GeoElement* pointer, const QString name, const QString symbol, const QString elZ, const QString elA)
+  unsigned int WriteGeoModel::storeObj(const GeoElement* pointer, const std::string &name, const std::string &symbol, const double &elZ, const double &elA)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::storeObj(GeoElement*) - name:" << name << "address:" << pointer << " - symbol: " << symbol << " - elZ: " << elZ << " - elA: " << elA;
-
-	QString address = getAddressStringFromPointer( pointer );
-	QVariant elementId;
+  std::string address = getAddressStringFromPointer( pointer );
+	unsigned int elementId;
 
 	if (! isAddressStored(address)) {
-		//JFB Commented out: qDebug() << "New Element! Storing it...";
-
-		elementId = addElement(name, symbol, elZ, elA);
-
+    elementId = addElement(name, symbol, elZ, elA);
 		storeAddress( address, elementId );
 	}
 	else {
-		//JFB Commented out: qDebug() << "Element node stored already. Getting ID from the memory map...";
 		elementId = getStoredIdFromAddress(address);
 	}
-	//JFB Commented out: qDebug() << "elementId:" << elementId;
 	return elementId;
 }
 
 
-QVariant WriteGeoModel::storeObj(const GeoShape* pointer, const QString name, const QString parameters)
+  unsigned int WriteGeoModel::storeObj(const GeoShape* pointer, const std::string &name, const std::string &parameters)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::storeObj(GeoShape*) - name:" << name << "address:" << pointer;
-
-	QString address = getAddressStringFromPointer( pointer );
+  std::string address = getAddressStringFromPointer( pointer );
 
-	QVariant shapeId;
+	unsigned int shapeId;
 	if (! isAddressStored(address)) {
-		//JFB Commented out: qDebug() << "New Shape! Storing it...";
-
-		// shapeId = m_dbManager->addShape(name, parameters);
 		shapeId = addShape(name, parameters);
-
 		storeAddress( address, shapeId);
 	}
 	else {
-		//JFB Commented out: qDebug() << "Shape node stored already. Getting ID from the memory map...";
 		shapeId = getStoredIdFromAddress(address);
 	}
-	//JFB Commented out: qDebug() << "shapeId:" << shapeId;
 	return shapeId;
 }
 
-QVariant WriteGeoModel::storeObj(const GeoLogVol* pointer, const QString name, const QVariant shapeId, const QVariant materialId)
+unsigned int WriteGeoModel::storeObj(const GeoLogVol* pointer, const std::string &name, const unsigned int &shapeId, const unsigned int &materialId)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::storeObj(GeoLogVol*) - name:" << name << "address:" << pointer;
-
-	QString address = getAddressStringFromPointer( pointer );
+  std::string address = getAddressStringFromPointer( pointer );
 
-	QVariant logvolId;
+	unsigned int logvolId;
 	if (! isAddressStored(address)) {
-		//JFB Commented out: qDebug() << "New LogVol! Storing it...";
-
-		// logvolId = m_dbManager->addLogVol(name, shapeId, materialId);
 		logvolId = addLogVol(name, shapeId, materialId);
-
 		storeAddress( address, logvolId );
 	}
 	else {
-		//JFB Commented out: qDebug() << "LogVol node stored already. Getting ID from the memory map...";
 		logvolId = getStoredIdFromAddress(address);
 	}
-	//JFB Commented out: qDebug() << "logvolId:" << logvolId;
 	return logvolId;
 }
 
 
-QVariant WriteGeoModel::storeObj(const GeoPhysVol* pointer, const QVariant logvolId, const QVariant parentId, bool isRootVolume)
+unsigned int WriteGeoModel::storeObj(const GeoPhysVol* pointer, const unsigned int &logvolId, const unsigned int parentId, const bool isRootVolume)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::storeObj(GeoPhysVol*) - address:" << pointer << "- is root volume?" << isRootVolume;
+  std::string address = getAddressStringFromPointer( pointer );
 
-	QString address = getAddressStringFromPointer( pointer );
-
-	QVariant physvolId;
+	unsigned int physvolId;
 	if (! isAddressStored(address)) {
-		//JFB Commented out: qDebug() << "New PhysVol! Storing it...";
-
-		// physvolId = m_dbManager->addPhysVol(logvolId, parentId, isRootVolume);
-		physvolId = addPhysVol(logvolId, parentId, isRootVolume);
-
+    physvolId = addPhysVol(logvolId, parentId, isRootVolume); // FIXME: remove parentInfo
+    
 		storeAddress( address, physvolId );
 	}
 	else {
-		//JFB Commented out: qDebug() << "PhysVol node stored already. Getting ID from the memory map...";
 		physvolId = getStoredIdFromAddress(address);
 	}
-	//JFB Commented out: qDebug() << "physvolId:" << physvolId;
 	return physvolId;
 }
 
-QVariant WriteGeoModel::storeObj(const GeoFullPhysVol* pointer, const QVariant logvolId, const QVariant parentId, bool isRootVolume)
+unsigned int WriteGeoModel::storeObj(const GeoFullPhysVol* pointer, const unsigned int &logvolId, const unsigned int parentId, const bool isRootVolume)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::storeObj(GeoFullPhysVol*) - address:" << pointer << "- is root volume?" << isRootVolume;
-
-	QString address = getAddressStringFromPointer( pointer );
+  std::string address = getAddressStringFromPointer( pointer );
 
-	QVariant physvolId;
+	unsigned int physvolId;
 	if (! isAddressStored(address)) {
-		//JFB Commented out: qDebug() << "New FullPhysVol! Storing it...";
-
-		// physvolId = m_dbManager->addFullPhysVol(logvolId, parentId, isRootVolume);
-		physvolId = addFullPhysVol(logvolId, parentId, isRootVolume);
+    physvolId = addFullPhysVol(logvolId, parentId, isRootVolume); // FIXME: remove parent info!
 
 		storeAddress( address, physvolId );
 	}
 	else {
-		//JFB Commented out: qDebug() << "FullPhysVol node stored already. Getting ID from the memory map...";
 		physvolId = getStoredIdFromAddress(address);
 	}
-	//JFB Commented out: qDebug() << "fullphysvolId:" << physvolId;
 	return physvolId;
 }
 
-QVariant WriteGeoModel::storeObj(const GeoSerialDenominator* pointer, const QString baseName)
+  unsigned int WriteGeoModel::storeObj(const GeoSerialDenominator* pointer, const std::string &baseName)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::storeObj(GeoSerialDenominator*) - baseName:" << baseName << "address:" << pointer;
-
-	QString address = getAddressStringFromPointer( pointer );
-	QVariant id;
+  const std::string address = getAddressStringFromPointer( pointer );
+	unsigned int id;
 
 	if (! isAddressStored(address)) {
-		//JFB Commented out: qDebug() << "New SerialDenominator! Storing it...";
-
-		// id = m_dbManager->addSerialDenominator(baseName);
 		id = addSerialDenominator(baseName);
-
 		storeAddress( address, id );
 	}
 	else {
-		//JFB Commented out: qDebug() << "SerialDenominator node stored already. Getting ID from the memory map...";
 		id = getStoredIdFromAddress(address);
 	}
-	//JFB Commented out: qDebug() << "ID:" << id;
 	return id;
 }
 
 
-QVariant WriteGeoModel::storeObj(const GeoSerialTransformer* pointer, const QVariant functionId, const QVariant volId, const QString volType, const unsigned int copies)
+  unsigned int WriteGeoModel::storeObj(const GeoSerialTransformer* pointer, const unsigned int &functionId, const unsigned int &volId, const std::string &volType, const unsigned int &copies)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::storeObj(GeoSerialTransformer*):" << volId.toUInt() << volType << "- n. of copies: " << copies;
-
-	QString address = getAddressStringFromPointer( pointer );
-	QVariant id;
+  const std::string address = getAddressStringFromPointer( pointer );
+	unsigned int id = 0;
 
 	if (! isAddressStored(address)) {
-		//JFB Commented out: qDebug() << "New SerialTransformer! Storing it...";
-
-		// id = m_dbManager->addSerialTransformer(functionId, volId, volType, copies);
 		id = addSerialTransformer(functionId, volId, volType, copies);
 
 		storeAddress( address, id );
 	}
 	else {
-		//JFB Commented out: qDebug() << "SerialTransformer node stored already. Getting ID from the memory map...";
 		id = getStoredIdFromAddress(address);
 	}
-	//JFB Commented out: qDebug() << "ID:" << id;
+  
+  if (!id) {
+    std::cout << "ERROR while storing a GeoSerialTransformer! Exiting..." << std::endl;
+    exit(EXIT_FAILURE);
+  }
 	return id;
 }
 
-QVariant WriteGeoModel::storeObj(const GeoXF::Function* pointer, const QString expression)
+  unsigned int WriteGeoModel::storeObj(const GeoXF::Function* pointer, const std::string &expression)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::storeObj(GeoXF::Function*)";
-
-	QString address = getAddressStringFromPointer( pointer );
-	QVariant id;
+  const std::string address = getAddressStringFromPointer( pointer );
+	unsigned int id = 0;
 
 	if (! isAddressStored(address)) {
-		//JFB Commented out: qDebug() << "New GeoXF::Function! Storing it...";
-
-		// id = m_dbManager->addFunction(expression);
 		id = addFunction(expression);
 
 		storeAddress( address, id );
 	}
 	else {
-		//JFB Commented out: qDebug() << "GeoXF::Function node stored already. Getting ID from the memory map...";
 		id = getStoredIdFromAddress(address);
 	}
-	//JFB Commented out: qDebug() << "ID:" << id;
+  
+  if (!id) {
+    std::cout << "ERROR while storing a Function! Exiting..." << std::endl;
+    exit(EXIT_FAILURE);
+  }
 	return id;
 }
 
-QVariant WriteGeoModel::storeObj(const GeoTransform* pointer, std::vector<double> parameters)
+unsigned int WriteGeoModel::storeObj(const GeoTransform* pointer, const std::vector<double> &parameters)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::storeObj(GeoTransform*)";
-
-
-	QString address = getAddressStringFromPointer( pointer );
-	QVariant id;
+  const std::string address = getAddressStringFromPointer( pointer );
+	unsigned int id = 0;
 
 	if (! isAddressStored(address)) {
-		//JFB Commented out: qDebug() << "New GeoXF::Function! Storing it...";
-
-		// id = m_dbManager->addTransform( QVector<double>::fromStdVector(parameters) );
 		id = addTransform( parameters );
-
 		storeAddress( address, id );
 	}
 	else {
-		//JFB Commented out: qDebug() << "GeoTransform node stored already. Getting ID from the memory map...";
 		id = getStoredIdFromAddress(address);
 	}
-	//JFB Commented out: qDebug() << "ID:" << id;
+  if (!id) {
+    std::cout << "ERROR while storing a GeoTransform! Exiting..." << std::endl;
+    exit(EXIT_FAILURE);
+  }
 	return id;
 }
 
-QVariant WriteGeoModel::storeObj(const GeoAlignableTransform* pointer, std::vector<double> parameters)
+unsigned int WriteGeoModel::storeObj(const GeoAlignableTransform* pointer, const std::vector<double> &parameters)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::storeObj(GeoAlignableTransform*)";
-
-
-	QString address = getAddressStringFromPointer( pointer );
-	QVariant id;
+  const std::string address = getAddressStringFromPointer( pointer );
+	unsigned int id = 0;
 
 	if (! isAddressStored(address)) {
-		//JFB Commented out: qDebug() << "New GeoXF::Function! Storing it...";
-
-		// id = m_dbManager->addAlignableTransform( QVector<double>::fromStdVector(parameters) );
 		id = addAlignableTransform( parameters );
-
 		storeAddress( address, id );
 	}
 	else {
-		//JFB Commented out: qDebug() << "GeoAlignableTransform node stored already. Getting ID from the memory map...";
 		id = getStoredIdFromAddress(address);
 	}
-	//JFB Commented out: qDebug() << "ID:" << id;
+  if (!id) {
+    std::cout << "ERROR while storing a GeoAlignableTransform! Exiting..." << std::endl;
+    exit(EXIT_FAILURE);
+  }
 	return id;
 }
 
 
-QVariant WriteGeoModel::storeObj(const GeoNameTag* pointer, const QString name)
+  unsigned int WriteGeoModel::storeObj(const GeoNameTag* pointer, const std::string &name)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::storeObj(GeoNameTag*) - name:" << name << "address:" << pointer;
-
-	QString address = getAddressStringFromPointer( pointer );
-	QVariant id;
+  const std::string address = getAddressStringFromPointer( pointer );
+	unsigned int id = 0;
 
 	if (! isAddressStored(address)) {
-		//JFB Commented out: qDebug() << "New SerialDenominator! Storing it...";
-
-		// id = m_dbManager->addNameTag(name);
 		id = addNameTag(name);
-
 		storeAddress( address, id );
 	}
 	else {
-		//JFB Commented out: qDebug() << "SerialDenominator node stored already. Getting ID from the memory map...";
 		id = getStoredIdFromAddress(address);
 	}
-	//JFB Commented out: qDebug() << "ID:" << id;
+  if (!id) {
+    std::cout << "ERROR while storing a GeoNameTag! Exiting..." << std::endl;
+    exit(EXIT_FAILURE);
+  }
 	return id;
 }
 
-void WriteGeoModel::storeChildPosition(const QVariant parentId, const QString parentType, const QVariant childId, const unsigned int parentCopyN, const unsigned int childPos, const QString childType, const unsigned int childCopyN)
+  void WriteGeoModel::storeChildPosition(const unsigned int &parentId, const std::string &parentType, const unsigned int &childId, const unsigned int &parentCopyN, const unsigned int &childPos, const std::string &childType, const unsigned int &childCopyN)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::storeChildPosition()";
 	addChildPosition(parentId, parentType, childId, parentCopyN, childPos, childType, childCopyN); // FIXME: change the positions of the parameters to a more logical order, like: parentID, parentType, parentCopyN, childPos, ChildType, childId, childCopyN
 	return;
 }
 
 
-unsigned int WriteGeoModel::addRecord(std::vector<QStringList>* container, const QStringList values) const
-{
+  unsigned int WriteGeoModel::addRecord(std::vector<std::vector<std::string>>* container, const std::vector<std::string> values) const
+  {
 	container->push_back(values);
 	unsigned int idx = container->size(); // index of pushed element = size after pushing, to match ID starting at 1 in the DB
 	return idx;
 }
 
-QVariant WriteGeoModel::addMaterial(const QString name, const QString density, const QString elements)
+  unsigned int WriteGeoModel::addMaterial(const std::string &name, const double &density, const std::string &elements)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::addMaterial(QString*) - name:" << name << "- density:" << density << "- elements:" << elements;
-	std::vector<QStringList>* container = &m_materials;
-	QStringList values;
-	values << name << density << elements;
-	return QVariant( addRecord(container, values) );
+  std::vector<std::vector<std::string>>* container = &m_materials;
+  std::vector<std::string> values;
+  values.push_back( name );
+  values.push_back( std::to_string(density) );
+  values.push_back( elements );
+	return addRecord(container, values);
 }
 
 
-QVariant WriteGeoModel::addElement(const QString name, const QString symbol, const QString elZ, const QString elA)
+unsigned int WriteGeoModel::addElement(const std::string &name, const std::string &symbol, const double &elZ, const double &elA)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::addElement(QString*) - name:" << name << "- symbol: " << symbol << "- elZ:" << elZ << "- elA:" << elA;
-	std::vector<QStringList>* container = &m_elements;
-	QStringList values;
-	values << name << symbol << elZ << elA;
-	return QVariant( addRecord(container, values) );
+  std::vector<std::vector<std::string>>* container = &m_elements;
+  std::vector<std::string> values;
+  values.insert(values.begin(), { name, symbol, std::to_string(elZ), std::to_string(elA)} );
+	return addRecord(container, values);
 }
 
 
-QVariant WriteGeoModel::addNameTag(const QString name)
+unsigned int WriteGeoModel::addNameTag(const std::string &name)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::addNameTag(QString*) - name:" << name;
-	std::vector<QStringList>* container = &m_nameTags;
-	QStringList values;
-	values << name;
-	return QVariant( addRecord(container, values) );
+	std::vector<std::vector<std::string>>* container = &m_nameTags;
+	std::vector<std::string> values;
+	values.push_back(name);
+	return addRecord(container, values);
 }
 
-QVariant WriteGeoModel::addSerialDenominator(const QString &baseName)
+unsigned int WriteGeoModel::addSerialDenominator(const std::string &baseName)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::addSerialDenominator(QString*) - basename:" << baseName;
-	std::vector<QStringList>* container = &m_serialDenominators;
-	QStringList values;
-	values << baseName;
-	return QVariant( addRecord(container, values) );
+	std::vector<std::vector<std::string>>* container = &m_serialDenominators;
+	std::vector<std::string> values;
+	values.push_back(baseName);
+	return addRecord(container, values);
 }
 
 
-QVariant WriteGeoModel::addFunction(const QString expression)
+unsigned int WriteGeoModel::addFunction(const std::string &expression)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::addFunction(QString*) - expression:" << expression;
-	std::vector<QStringList>* container = &m_functions;
-	QStringList values;
-	values << expression;
-	return QVariant( addRecord(container, values) );
+	std::vector<std::vector<std::string>>* container = &m_functions;
+	std::vector<std::string> values;
+	values.push_back(expression);
+	return addRecord(container, values);
 }
 
 
-QVariant WriteGeoModel::addAlignableTransform(const std::vector<double> params)
+unsigned int WriteGeoModel::addAlignableTransform(const std::vector<double> &params)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::addAlignableTransform(QString*)";
-	std::vector<QStringList>* container = &m_alignableTransforms;
-	QStringList values;
-	foreach(double par, params) {
-		values << QString::number(par);
+	std::vector<std::vector<std::string>>* container = &m_alignableTransforms;
+	std::vector<std::string> values;
+  for(const double& par : params) {
+    values.push_back( std::to_string(par) );
 	}
-	return QVariant( addRecord(container, values) );
+	return addRecord(container, values);
 }
 
 
 
-QVariant WriteGeoModel::addTransform(const std::vector<double> params)
+unsigned int WriteGeoModel::addTransform(const std::vector<double> &params)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::addTransform(QString*)";
-	std::vector<QStringList>* container = &m_transforms;
-	QStringList values;
-	foreach(double par, params) {
-		values << QString::number(par);
+	std::vector<std::vector<std::string>>* container = &m_transforms;
+	std::vector<std::string> values;
+  for(const double& par : params) {
+		values.push_back( std::to_string(par) );
 	}
-	return QVariant( addRecord(container, values) );
+	return addRecord(container, values);
 }
 
-QString WriteGeoModel::getIdFromNodeType( QString nodeType )
+  unsigned int WriteGeoModel::getIdFromNodeType( const std::string &nodeType )
 {
-        //JFB Commented out: qDebug() << "getIdFromNodeType(" << nodeType <<")";
-		if (m_memMap_Tables.contains(nodeType))
-			return QString::number(m_memMap_Tables.value(nodeType));
-		else
-			return QString("NULL");
+  std::unordered_map<std::string, unsigned int>::iterator it = m_memMap_Tables.find(nodeType);
+  if ( it != m_memMap_Tables.end() ) { // item found
+      return m_memMap_Tables.at(nodeType);
+  }
+  return 0; // item not found
 }
 
-QVariant WriteGeoModel::addSerialTransformer(const QVariant &funcId, const QVariant &physvolId, const QString volType, const unsigned int &copies)
+  unsigned int WriteGeoModel::addSerialTransformer(const unsigned int &funcId, const unsigned int &physvolId, const std::string volType, const unsigned int &copies)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::addSerialTransformer()";
-	std::vector<QStringList>* container = &m_serialTransformers;
-	QString volTypeID = getIdFromNodeType(volType);
+	std::vector<std::vector<std::string>>* container = &m_serialTransformers;
+  const unsigned int volTypeID = getIdFromNodeType(volType);
 
-	QStringList values;
-	values << funcId.toString() << physvolId.toString() << volTypeID << QString::number(copies);
+  std::vector<std::string> values;
+  values.insert( values.begin(),  {std::to_string(funcId), std::to_string(physvolId), std::to_string(volTypeID), std::to_string(copies)} );
 
-	return QVariant( addRecord(container, values) );
+	return addRecord(container, values);
 }
 
-QVariant WriteGeoModel::addShape(const QString &type, const QString &parameters)
+  unsigned int WriteGeoModel::addShape(const std::string &type, const std::string &parameters)
 {
-	std::vector<QStringList>* container = &m_shapes;
-	QStringList values;
-	values << type << parameters;
-	return QVariant( addRecord(container, values) );
+  std::vector<std::vector<std::string>>* container = &m_shapes;
+  std::vector<std::string> values;
+  values.push_back(type);
+  values.push_back(parameters);
+	return addRecord(container, values);
 }
 
 
-QVariant WriteGeoModel::addPhysVol(const QVariant &logVolId, const QVariant &parentPhysVolId, bool isRootVolume)
+unsigned int WriteGeoModel::addPhysVol(const unsigned int &logVolId, const unsigned int &parentPhysVolId, const bool &isRootVolume)
 {
-	std::vector<QStringList>* container = &m_physVols;
-
-	QStringList values;
-	values << logVolId.toString() << parentPhysVolId.toString(); // TODO: we should remove the parent info: it's not complete because the type is missing (PhysVol or FullPhysVol) and it's redundant, because we store the childrenPositions. It's only useful for quick visual debug, by dumping the PhysVol DB table
-
+	std::vector<std::vector<std::string>>* container = &m_physVols;
+  std::vector<std::string> values;
+  values.push_back( std::to_string(logVolId) );
 	unsigned int idx = addRecord(container, values);
-
 	if (isRootVolume) {
-		QStringList rootValues;
-		rootValues << QString::number(idx) << "GeoPhysVol";
+		std::vector<std::string> rootValues;
+    rootValues.insert(rootValues.begin(), { std::to_string(idx), "GeoPhysVol"} );
 		m_rootVolume = rootValues;
 	}
-	return QVariant(idx);
+	return idx;
 }
 
 
-QVariant WriteGeoModel::addFullPhysVol(const QVariant &logVolId, const QVariant &parentPhysVolId, bool isRootVolume)
+unsigned int WriteGeoModel::addFullPhysVol(const unsigned int &logVolId, const unsigned int &parentPhysVolId, const bool &isRootVolume)
 {
-	std::vector<QStringList>* container = &m_fullPhysVols;
-
-	QStringList values;
-	values << logVolId.toString() << parentPhysVolId.toString(); // TODO: we should remove the parent info: it's not complete because the type is missing (PhysVol or FullPhysVol) and it's redundant, because we store the childrenPositions. It's only useful for quick visual debug, by dumping the PhysVol DB table
-
+	std::vector<std::vector<std::string>>* container = &m_fullPhysVols;
+	std::vector<std::string> values;
+  values.push_back( std::to_string(logVolId) );
 	unsigned int idx = addRecord(container, values);
-
 	if (isRootVolume) {
-		QStringList rootValues;
-		rootValues << QString::number(idx) << "GeoFullPhysVol";
+		std::vector<std::string> rootValues;
+//    rootValues << QString::number(idx) << "GeoFullPhysVol";
+    rootValues.insert(rootValues.begin(), { std::to_string(idx), "GeoPhysVol"} );
 		m_rootVolume = rootValues;
 	}
-	return QVariant(idx);
+	return idx;
 }
 
-QVariant WriteGeoModel::addLogVol(const QString &name, const QVariant &shapeId, const QVariant &materialId)
+unsigned int WriteGeoModel::addLogVol(const std::string &name, const unsigned int &shapeId, const unsigned int &materialId)
 {
-	std::vector<QStringList>* container = &m_logVols;
-	QStringList values;
-	values << name << shapeId.toString() << materialId.toString();
-	return QVariant( addRecord(container, values) );
+	std::vector<std::vector<std::string>>* container = &m_logVols;
+  std::vector<std::string> values;
+  values.insert( values.begin(), {name, std::to_string(shapeId), std::to_string(materialId)} );
+	return addRecord(container, values);
 }
 
 
-void WriteGeoModel::addChildPosition(const QVariant parentId, const QString parentType, const QVariant childId, const unsigned int parentCopyN, const unsigned int childPos, const QString childType, const unsigned int childCopyN)
+  void WriteGeoModel::addChildPosition(const unsigned int &parentId, const std::string &parentType, const unsigned int &childId, const unsigned int &parentCopyN, const unsigned int &childPos, const std::string &childType, const unsigned int &childCopyN)
 {
-	std::vector<QStringList>* container = &m_childrenPositions;
-
-	QString parentTableID = getIdFromNodeType(parentType);
-	QString childTableID = getIdFromNodeType(childType);
+	std::vector<std::vector<std::string>>* container = &m_childrenPositions;
+	const unsigned int parentTableID = getIdFromNodeType(parentType);
+	const unsigned int childTableID = getIdFromNodeType(childType);
 
-	QStringList values;
-	values << parentId.toString() << parentTableID <<  QString::number(parentCopyN) << QString::number(childPos) << childTableID << childId.toString() << QString::number(childCopyN);
+	std::vector<std::string> values;
+//  values << parentId.toString() << parentTableID <<  QString::number(parentCopyN) << QString::number(childPos) << childTableID << childId.toString() << QString::number(childCopyN);
+  values.insert(values.begin(), { std::to_string(parentId), std::to_string(parentTableID), std::to_string(parentCopyN), std::to_string(childPos), std::to_string(childTableID), std::to_string(childId), std::to_string(childCopyN)} );
 	addRecord(container, values);
 	return;
 }
@@ -1669,8 +1482,7 @@ void WriteGeoModel::addChildPosition(const QVariant parentId, const QString pare
 
 void WriteGeoModel::saveToDB()
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::savetoDB()";
-    std::cout << "saving to file: " << m_dbpath.toStdString() << std::endl;
+    std::cout << "Saving the GeoModel tree to file: '" << m_dbpath << "'" << std::endl;
 
 	m_dbManager->addListOfRecords("GeoMaterial", m_materials);
 	m_dbManager->addListOfRecords("GeoElement", m_elements);
@@ -1689,126 +1501,112 @@ void WriteGeoModel::saveToDB()
 	m_dbManager->addRootVolume(m_rootVolume);
 
 	if ( !m_objectsNotPersistified.empty() ) {
-		qWarning() << "\n\tWARNING!! There are objects which need to be persistified! --> " << m_objectsNotPersistified << "\n\n";
+    std::cout << "\n\tWARNING!! There are shapes/nodes which need to be persistified! --> ";
+    printStdVectorStrings(m_objectsNotPersistified);
+    std::cout << "\n\n";
 	}
 
 	return;
 }
+ 
 
-
-void WriteGeoModel::storeAddress(const QString address, QVariant id)
+  void WriteGeoModel::storeAddress(const std::string &address, const unsigned int &id)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::storeAddress(" << address << "," << id << ")";
-	m_memMap.insert(address, id);
+  m_memMap.insert( std::pair<std::string,unsigned int>(address, id) );
 }
 
-bool WriteGeoModel::isAddressStored(const QString address)
+  bool WriteGeoModel::isAddressStored(const std::string &address)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::isAddressStored(): " << address;
 	//showMemoryMap(); // only for Debug
-	return m_memMap.contains(address);
+  std::unordered_map<std::string, unsigned int>::iterator it = m_memMap.find(address);
+  return ( it != m_memMap.end() );
 }
 
 
-QVariant WriteGeoModel::getStoredIdFromAddress(QString address)
+  unsigned int WriteGeoModel::getStoredIdFromAddress(const std::string &address)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::getStoredIdFromAddress(): " << address;
-	return m_memMap.value(address);
+	return m_memMap.at(address);
 }
 
 // get pointer string
-QString WriteGeoModel::getAddressStringFromPointer(const GeoMaterial* pointer)
+  std::string WriteGeoModel::getAddressStringFromPointer(const GeoMaterial* pointer)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::getAddressStringFromPointer(GeoMaterial*)";
 	std::ostringstream oss;
 	oss << pointer;
 	return getQStringFromOss(oss);
 }
 // get pointer string
-QString WriteGeoModel::getAddressStringFromPointer(const GeoElement* pointer)
+std::string WriteGeoModel::getAddressStringFromPointer(const GeoElement* pointer)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::getAddressStringFromPointer(GeoElement*)";
 	std::ostringstream oss;
 	oss << pointer;
 	return getQStringFromOss(oss);
 }
 // get pointer string
-QString WriteGeoModel::getAddressStringFromPointer(const GeoShape* pointer)
+std::string WriteGeoModel::getAddressStringFromPointer(const GeoShape* pointer)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::getAddressStringFromPointer(GeoShape*)";
 	std::ostringstream oss;
 	oss << pointer;
 	return getQStringFromOss(oss);
 }
 // get pointer string
-QString WriteGeoModel::getAddressStringFromPointer(const GeoLogVol* pointer)
+std::string WriteGeoModel::getAddressStringFromPointer(const GeoLogVol* pointer)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::getAddressStringFromPointer(GeoLogVol*)";
 	std::ostringstream oss;
 	oss << pointer;
 	return getQStringFromOss(oss);
 }
 // get pointer string
-QString WriteGeoModel::getAddressStringFromPointer(const GeoPhysVol* pointer)
+std::string WriteGeoModel::getAddressStringFromPointer(const GeoPhysVol* pointer)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::getAddressStringFromPointer(GeoPhysVol*)";
 	std::ostringstream oss;
 	oss << pointer;
 	return getQStringFromOss(oss);
 }
-QString WriteGeoModel::getAddressStringFromPointer(const GeoVPhysVol* pointer)
+std::string WriteGeoModel::getAddressStringFromPointer(const GeoVPhysVol* pointer)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::getAddressStringFromPointer(GeoVPhysVol*)";
 	std::ostringstream oss;
 	oss << pointer;
 	return getQStringFromOss(oss);
 }
 // get pointer string
-QString WriteGeoModel::getAddressStringFromPointer(const GeoSerialDenominator* pointer)
+std::string WriteGeoModel::getAddressStringFromPointer(const GeoSerialDenominator* pointer)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::getAddressStringFromPointer(GeoSerialDenominator*)";
 	std::ostringstream oss;
 	oss << pointer;
 	return getQStringFromOss(oss);
 }
-QString WriteGeoModel::getAddressStringFromPointer(const GeoSerialTransformer* pointer)
+std::string WriteGeoModel::getAddressStringFromPointer(const GeoSerialTransformer* pointer)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::getAddressStringFromPointer(GeoSerialTransformer*)";
 	std::ostringstream oss;
 	oss << pointer;
 	return getQStringFromOss(oss);
 }
-QString WriteGeoModel::getAddressStringFromPointer(const GeoXF::Function* pointer)
+std::string WriteGeoModel::getAddressStringFromPointer(const GeoXF::Function* pointer)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::getAddressStringFromPointer(GeoXF::Function*)";
 	std::ostringstream oss;
 	oss << pointer;
 	return getQStringFromOss(oss);
 }
 
-QString WriteGeoModel::getAddressStringFromPointer(const GeoTransform* pointer)
+std::string WriteGeoModel::getAddressStringFromPointer(const GeoTransform* pointer)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::getAddressStringFromPointer(GeoTransform*)";
 	std::ostringstream oss;
 	oss << pointer;
 	return getQStringFromOss(oss);
 }
 
-QString WriteGeoModel::getAddressStringFromPointer(const GeoNameTag* pointer)
+std::string WriteGeoModel::getAddressStringFromPointer(const GeoNameTag* pointer)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::getAddressStringFromPointer(GeoNameTag*)";
 	std::ostringstream oss;
 	oss << pointer;
 	return getQStringFromOss(oss);
 }
 
-QString WriteGeoModel::getQStringFromOss(std::ostringstream &oss)
+std::string WriteGeoModel::getQStringFromOss(std::ostringstream &oss)
 {
-	//JFB Commented out: qDebug() << "WriteGeoModel::getQStringFromOss()";
 	std::string addr = oss.str();
-	QString address = QString::fromStdString(addr);
-	//JFB Commented out: qDebug() << "address string:" << address;
-	return address;
+	return addr;
 }