diff --git a/src/Benchmarks/BLAS/tnl-benchmark-blas.h b/src/Benchmarks/BLAS/tnl-benchmark-blas.h
index a1bd3e92b0d2e11ac3f5377ffcf199d8a873ad60..3e05da6304d48d581434716deb2e7929f2c83f79 100644
--- a/src/Benchmarks/BLAS/tnl-benchmark-blas.h
+++ b/src/Benchmarks/BLAS/tnl-benchmark-blas.h
@@ -16,8 +16,7 @@
 #include <TNL/Allocators/CudaManaged.h>
 #include <TNL/Devices/Host.h>
 #include <TNL/Devices/Cuda.h>
-#include <TNL/Config/ConfigDescription.h>
-#include <TNL/Config/ParameterContainer.h>
+#include <TNL/Config/parseCommandLine.h>
 
 #include "array-operations.h"
 #include "vector-operations.h"
@@ -135,10 +134,8 @@ main( int argc, char* argv[] )
 
    setupConfig( conf_desc );
 
-   if( ! parseCommandLine( argc, argv, conf_desc, parameters ) ) {
-      conf_desc.printUsage( argv[ 0 ] );
+   if( ! parseCommandLine( argc, argv, conf_desc, parameters ) )
       return EXIT_FAILURE;
-   }
 
    if( ! Devices::Host::setup( parameters ) ||
        ! Devices::Cuda::setup( parameters ) )
diff --git a/src/Benchmarks/DistSpMV/tnl-benchmark-distributed-spmv.h b/src/Benchmarks/DistSpMV/tnl-benchmark-distributed-spmv.h
index b90b11088ef8f73511adb2ba5c58448e93e2bcf8..8aa22d6f72c78b5036047c63b71a1a2c5116c0a9 100644
--- a/src/Benchmarks/DistSpMV/tnl-benchmark-distributed-spmv.h
+++ b/src/Benchmarks/DistSpMV/tnl-benchmark-distributed-spmv.h
@@ -16,8 +16,7 @@
 #include <TNL/Debugging/FPE.h>
 #endif
 
-#include <TNL/Config/ConfigDescription.h>
-#include <TNL/Config/ParameterContainer.h>
+#include <TNL/Config/parseCommandLine.h>
 #include <TNL/Devices/Host.h>
 #include <TNL/Devices/Cuda.h>
 #include <TNL/Communicators/MpiCommunicator.h>
@@ -309,10 +308,8 @@ main( int argc, char* argv[] )
    Communicators::ScopedInitializer< CommunicatorType > scopedInit(argc, argv);
    const int rank = CommunicatorType::GetRank( CommunicatorType::AllGroup );
 
-   if( ! parseCommandLine( argc, argv, conf_desc, parameters ) ) {
-      conf_desc.printUsage( argv[ 0 ] );
+   if( ! parseCommandLine( argc, argv, conf_desc, parameters ) )
       return EXIT_FAILURE;
-   }
 
    if( ! Devices::Host::setup( parameters ) ||
        ! Devices::Cuda::setup( parameters ) ||
diff --git a/src/Benchmarks/HeatEquation/tnl-benchmark-simple-heat-equation.h b/src/Benchmarks/HeatEquation/tnl-benchmark-simple-heat-equation.h
index 6824329226ab58be502f89e71387ad5749d4fa5d..46e89748de975d65fa3c5201fa4d08d0928bc838 100644
--- a/src/Benchmarks/HeatEquation/tnl-benchmark-simple-heat-equation.h
+++ b/src/Benchmarks/HeatEquation/tnl-benchmark-simple-heat-equation.h
@@ -13,8 +13,7 @@
 
 #include <iostream>
 #include <stdio.h>
-#include <TNL/Config/ConfigDescription.h>
-#include <TNL/Config/ParameterContainer.h>
+#include <TNL/Config/parseCommandLine.h>
 #include <TNL/Timer.h>
 #include <TNL/Devices/Cuda.h>
 #include <TNL/Containers/StaticVector.h>
diff --git a/src/Benchmarks/LinearSolvers/tnl-benchmark-linear-solvers.h b/src/Benchmarks/LinearSolvers/tnl-benchmark-linear-solvers.h
index ea39d80b7b8011f53e7187e53a62e8446bdb8b82..08f45683a338491b89be826cb148df37137e3699 100644
--- a/src/Benchmarks/LinearSolvers/tnl-benchmark-linear-solvers.h
+++ b/src/Benchmarks/LinearSolvers/tnl-benchmark-linear-solvers.h
@@ -21,8 +21,7 @@
 #include <TNL/Debugging/FPE.h>
 #endif
 
-#include <TNL/Config/ConfigDescription.h>
-#include <TNL/Config/ParameterContainer.h>
+#include <TNL/Config/parseCommandLine.h>
 #include <TNL/Devices/Host.h>
 #include <TNL/Devices/Cuda.h>
 #include <TNL/Communicators/MpiCommunicator.h>
@@ -592,10 +591,8 @@ main( int argc, char* argv[] )
    Communicators::ScopedInitializer< CommunicatorType > scopedInit(argc, argv);
    const int rank = CommunicatorType::GetRank( CommunicatorType::AllGroup );
 
-   if( ! parseCommandLine( argc, argv, conf_desc, parameters ) ) {
-      conf_desc.printUsage( argv[ 0 ] );
+   if( ! parseCommandLine( argc, argv, conf_desc, parameters ) )
       return EXIT_FAILURE;
-   }
    if( ! Devices::Host::setup( parameters ) ||
        ! Devices::Cuda::setup( parameters ) ||
        ! CommunicatorType::setup( parameters ) )
diff --git a/src/Benchmarks/NDArray/tnl-benchmark-ndarray-boundary.h b/src/Benchmarks/NDArray/tnl-benchmark-ndarray-boundary.h
index 285dd6f3d7f97aa9f90a0914ca88fe535661d7eb..29445234c60b9d036d52c2c755f36e838a801a86 100644
--- a/src/Benchmarks/NDArray/tnl-benchmark-ndarray-boundary.h
+++ b/src/Benchmarks/NDArray/tnl-benchmark-ndarray-boundary.h
@@ -14,6 +14,7 @@
 
 #include <TNL/Assert.h>
 #include <TNL/Math.h>
+#include <TNL/Config/parseCommandLine.h>
 
 #include <TNL/Containers/NDArray.h>
 
@@ -423,10 +424,8 @@ int main( int argc, char* argv[] )
 
    setupConfig( conf_desc );
 
-   if( ! parseCommandLine( argc, argv, conf_desc, parameters ) ) {
-      conf_desc.printUsage( argv[ 0 ] );
+   if( ! parseCommandLine( argc, argv, conf_desc, parameters ) )
       return EXIT_FAILURE;
-   }
 
    if( ! Devices::Host::setup( parameters ) ||
        ! Devices::Cuda::setup( parameters ) )
diff --git a/src/Benchmarks/NDArray/tnl-benchmark-ndarray.h b/src/Benchmarks/NDArray/tnl-benchmark-ndarray.h
index 0c29b21b5894e46627f8c4f129eafc8697ae9aec..9f17b8b5c79b8a245a08551f4a51a597a05c059b 100644
--- a/src/Benchmarks/NDArray/tnl-benchmark-ndarray.h
+++ b/src/Benchmarks/NDArray/tnl-benchmark-ndarray.h
@@ -15,6 +15,7 @@
 #include <TNL/Assert.h>
 #include <TNL/Math.h>
 #include <TNL/Algorithms/ParallelFor.h>
+#include <TNL/Config/parseCommandLine.h>
 
 #include <TNL/Containers/NDArray.h>
 #include <TNL/Containers/ndarray/Operations.h>
@@ -411,10 +412,8 @@ int main( int argc, char* argv[] )
 
    setupConfig( conf_desc );
 
-   if( ! parseCommandLine( argc, argv, conf_desc, parameters ) ) {
-      conf_desc.printUsage( argv[ 0 ] );
+   if( ! parseCommandLine( argc, argv, conf_desc, parameters ) )
       return EXIT_FAILURE;
-   }
 
    if( ! Devices::Host::setup( parameters ) ||
        ! Devices::Cuda::setup( parameters ) )
diff --git a/src/Benchmarks/ODESolvers/tnl-benchmark-ode-solvers.h b/src/Benchmarks/ODESolvers/tnl-benchmark-ode-solvers.h
index dad2cdd8dc309b38844c1c5eb27232f75d6092dc..94c452cc6836883af1af94935b0d736273a7b5ef 100644
--- a/src/Benchmarks/ODESolvers/tnl-benchmark-ode-solvers.h
+++ b/src/Benchmarks/ODESolvers/tnl-benchmark-ode-solvers.h
@@ -20,8 +20,7 @@
 #include <TNL/Debugging/FPE.h>
 #endif
 
-#include <TNL/Config/ConfigDescription.h>
-#include <TNL/Config/ParameterContainer.h>
+#include <TNL/Config/parseCommandLine.h>
 #include <TNL/Devices/Host.h>
 #include <TNL/Devices/Cuda.h>
 #include <TNL/Communicators/MpiCommunicator.h>
@@ -193,7 +192,7 @@ configSetup( Config::ConfigDescription& config )
    config.addEntryEnum( "overwrite" );
    config.addEntry< int >( "loops", "Number of repetitions of the benchmark.", 10 );
    config.addEntry< int >( "verbose", "Verbose mode.", 1 );
-   config.addList< String >( "solvers", "Comma-separated list of solvers to run benchmarks for.", "all" );
+   config.addList< String >( "solvers", "List of solvers to run benchmarks for.", {"all"} );
    config.addEntryEnum< String >( "euler" );
    config.addEntryEnum< String >( "merson" );
    config.addEntryEnum< String >( "all" );
@@ -233,10 +232,8 @@ main( int argc, char* argv[] )
    Communicators::ScopedInitializer< CommunicatorType > scopedInit(argc, argv);
    const int rank = CommunicatorType::GetRank( CommunicatorType::AllGroup );
 
-   if( ! parseCommandLine( argc, argv, conf_desc, parameters ) ) {
-      conf_desc.printUsage( argv[ 0 ] );
+   if( ! parseCommandLine( argc, argv, conf_desc, parameters ) )
       return EXIT_FAILURE;
-   }
    if( ! Devices::Host::setup( parameters ) ||
        ! Devices::Cuda::setup( parameters ) ||
        ! CommunicatorType::setup( parameters ) )
diff --git a/src/Benchmarks/SpMV/tnl-benchmark-spmv.h b/src/Benchmarks/SpMV/tnl-benchmark-spmv.h
index 65416f0432085f744ee66a80efb497242ef0db81..4c6aea68ed065d331fb0f34663c128f132ecb6e3 100644
--- a/src/Benchmarks/SpMV/tnl-benchmark-spmv.h
+++ b/src/Benchmarks/SpMV/tnl-benchmark-spmv.h
@@ -16,8 +16,7 @@
 
 #include <TNL/Devices/Host.h>
 #include <TNL/Devices/Cuda.h>
-#include <TNL/Config/ConfigDescription.h>
-#include <TNL/Config/ParameterContainer.h>
+#include <TNL/Config/parseCommandLine.h>
 
 #include <Benchmarks/BLAS/array-operations.h>
 #include <Benchmarks/BLAS/vector-operations.h>
@@ -102,10 +101,8 @@ main( int argc, char* argv[] )
    //              terminate called after throwing an instance of 'int'
    //      [1]    17156 abort (core dumped)  ~/tnl-dev/Debug/bin/./tnl-benchmark-spmv-dbg --help
 
-   if( ! parseCommandLine( argc, argv, conf_desc, parameters ) ) {
-      conf_desc.printUsage( argv[ 0 ] );
+   if( ! parseCommandLine( argc, argv, conf_desc, parameters ) )
       return EXIT_FAILURE;
-   }
 
    if( ! Devices::Host::setup( parameters ) ||
        ! Devices::Cuda::setup( parameters ) )
diff --git a/src/Benchmarks/Traversers/tnl-benchmark-traversers.h b/src/Benchmarks/Traversers/tnl-benchmark-traversers.h
index dbe637d826fe4e0dcd593320fba11ad46588e9b3..30c364ac37f2bc32753a14c723ff2276332550c9 100644
--- a/src/Benchmarks/Traversers/tnl-benchmark-traversers.h
+++ b/src/Benchmarks/Traversers/tnl-benchmark-traversers.h
@@ -16,7 +16,7 @@
 //#include "grid-traversing.h"
 #include "GridTraversersBenchmark.h"
 
-#include <TNL/Config/ConfigDescription.h>
+#include <TNL/Config/parseCommandLine.h>
 #include <TNL/Devices/Host.h>
 #include <TNL/Devices/Cuda.h>
 #include <TNL/Algorithms/ParallelFor.h>
@@ -490,10 +490,8 @@ int main( int argc, char* argv[] )
    Config::ParameterContainer parameters;
    
    setupConfig( config );
-   if( ! parseCommandLine( argc, argv, config, parameters ) ) {
-      config.printUsage( argv[ 0 ] );
+   if( ! parseCommandLine( argc, argv, config, parameters ) )
       return EXIT_FAILURE;
-   }
 
    if( ! Devices::Host::setup( parameters ) ||
        ! Devices::Cuda::setup( parameters ) )
diff --git a/src/Examples/ConfigDescriptionExample.cpp b/src/Examples/ConfigDescriptionExample.cpp
index 59958f1d103458f247ec501b0bb08a1142eac8e6..210d36750d3485fe3fd29c680c58e0374f327b2a 100644
--- a/src/Examples/ConfigDescriptionExample.cpp
+++ b/src/Examples/ConfigDescriptionExample.cpp
@@ -1,9 +1,10 @@
 #include <iostream>
 #include <TNL/Config/ConfigDescription.h>
+#include <TNL/String.h>
 
 using namespace TNL;
 using namespace std;
-       
+
 int main()
 {
     Config::ConfigDescription confd;
diff --git a/src/TNL/Config/ConfigDelimiter.h b/src/TNL/Config/ConfigDelimiter.h
index 2c102c77888c18114fe39272782b0386434685aa..3bf9888d22df561eb18b96a47ee5b92d021206d7 100644
--- a/src/TNL/Config/ConfigDelimiter.h
+++ b/src/TNL/Config/ConfigDelimiter.h
@@ -2,12 +2,14 @@
                           ConfigDelimiter.h  -  description
                              -------------------
     begin                : Jul 5, 2014
-    copyright            : (C) 2014 by Tomas Oberhuber
+    copyright            : (C) 2014 by Tomas Oberhuber et al.
     email                : tomas.oberhuber@fjfi.cvut.cz
  ***************************************************************************/
 
 /* See Copyright Notice in tnl/Copyright */
 
+// Implemented by: Tomáš Oberhuber, Jakub Klinkovský
+
 #pragma once
 
 #include <TNL/Config/ConfigEntryBase.h>
@@ -15,17 +17,16 @@
 namespace TNL {
 namespace Config {
 
-struct ConfigDelimiter : public ConfigEntryBase
+class ConfigDelimiter : public ConfigEntryBase
 {
-   ConfigDelimiter( const String& delimiter )
+public:
+   ConfigDelimiter( const std::string& delimiter )
    : ConfigEntryBase( "", delimiter, false )
    {}
 
-   bool isDelimiter() const { return true; };
-
-   String getEntryType() const { return ""; };
+   virtual bool isDelimiter() const override { return true; };
 
-   String getUIEntryType() const { return ""; };
+   virtual std::string getUIEntryType() const override { return ""; };
 };
 
 } //namespace Config
diff --git a/src/TNL/Config/ConfigDescription.h b/src/TNL/Config/ConfigDescription.h
index 648db1d445de1ef7362e0265927226e4ec734887..35165e8d1456fabbe8c2f4a5876574a5d947395a 100644
--- a/src/TNL/Config/ConfigDescription.h
+++ b/src/TNL/Config/ConfigDescription.h
@@ -1,27 +1,24 @@
 /***************************************************************************
-                          Config::ConfigDescription.h  -  description
+                          ConfigDescription.h  -  description
                              -------------------
     begin                : 2007/06/09
-    copyright            : (C) 2007 by Tomas Oberhuber
+    copyright            : (C) 2007 by Tomas Oberhuber et al.
     email                : tomas.oberhuber@fjfi.cvut.cz
  ***************************************************************************/
 
 /* See Copyright Notice in tnl/Copyright */
 
+// Implemented by: Tomáš Oberhuber, Jakub Klinkovský
+
 #pragma once
 
-#include <iomanip>
-#include <string>
 #include <vector>
 #include <memory>
 
-#include <TNL/Assert.h>
-#include <TNL/String.h>
-#include <TNL/Config/ConfigEntryType.h>
 #include <TNL/Config/ConfigEntry.h>
 #include <TNL/Config/ConfigEntryList.h>
 #include <TNL/Config/ConfigDelimiter.h>
-#include <TNL/Config/ParameterContainer.h>
+#include <TNL/Exceptions/ConfigError.h>
 
 namespace TNL {
 namespace Config {
@@ -37,10 +34,11 @@ public:
     * \param description More specific information about the entry.
     */
    template< typename EntryType >
-   void addEntry( const String& name,
-                  const String& description )
+   void addEntry( const std::string& name,
+                  const std::string& description )
    {
-      entries.push_back( std::make_unique< ConfigEntry< EntryType > >( name, description, false ) );
+      using CoercedEntryType = typename ParameterTypeCoercion< EntryType >::type;
+      entries.push_back( std::make_unique< ConfigEntry< CoercedEntryType > >( name, description, false ) );
       currentEntry = entries.back().get();
       isCurrentEntryList = false;
    }
@@ -53,10 +51,11 @@ public:
     * \param description More specific information about the entry.
     */
    template< typename EntryType >
-   void addRequiredEntry( const String& name,
-                          const String& description )
+   void addRequiredEntry( const std::string& name,
+                          const std::string& description )
    {
-      entries.push_back( std::make_unique< ConfigEntry< EntryType > >( name, description, true ) );
+      using CoercedEntryType = typename ParameterTypeCoercion< EntryType >::type;
+      entries.push_back( std::make_unique< ConfigEntry< CoercedEntryType > >( name, description, true ) );
       currentEntry = entries.back().get();
       isCurrentEntryList = false;
    }
@@ -70,11 +69,13 @@ public:
     * \param defaultValue Default value of the entry.
     */
    template< typename EntryType >
-   void addEntry( const String& name,
-                  const String& description,
+   void addEntry( const std::string& name,
+                  const std::string& description,
                   const EntryType& defaultValue )
    {
-      entries.push_back( std::make_unique< ConfigEntry< EntryType > >( name, description, false, defaultValue ) );
+      using CoercedEntryType = typename ParameterTypeCoercion< EntryType >::type;
+      const auto convertedDefaultValue = ParameterTypeCoercion< EntryType >::convert( defaultValue );
+      entries.push_back( std::make_unique< ConfigEntry< CoercedEntryType > >( name, description, false, convertedDefaultValue ) );
       currentEntry = entries.back().get();
       isCurrentEntryList = false;
    }
@@ -87,10 +88,11 @@ public:
     * \param description More specific information about the list.
     */
    template< typename EntryType >
-   void addList( const String& name,
-                 const String& description )
+   void addList( const std::string& name,
+                 const std::string& description )
    {
-      entries.push_back( std::make_unique< ConfigEntryList< EntryType > >( name, description, false ) );
+      using CoercedEntryType = typename ParameterTypeCoercion< EntryType >::type;
+      entries.push_back( std::make_unique< ConfigEntryList< CoercedEntryType > >( name, description, false ) );
       currentEntry = entries.back().get();
       isCurrentEntryList = true;
    }
@@ -103,10 +105,11 @@ public:
     * \param description More specific information about the list.
     */
    template< typename EntryType >
-   void addRequiredList( const String& name,
-                         const String& description )
+   void addRequiredList( const std::string& name,
+                         const std::string& description )
    {
-      entries.push_back( std::make_unique< ConfigEntryList< EntryType > >( name, description, true ) );
+      using CoercedEntryType = typename ParameterTypeCoercion< EntryType >::type;
+      entries.push_back( std::make_unique< ConfigEntryList< CoercedEntryType > >( name, description, true ) );
       currentEntry = entries.back().get();
       isCurrentEntryList = true;
    }
@@ -120,11 +123,13 @@ public:
     * \param defaultValue Default value of the list.
     */
    template< typename EntryType >
-   void addList( const String& name,
-                 const String& description,
-                 const EntryType& defaultValue )
+   void addList( const std::string& name,
+                 const std::string& description,
+                 const std::vector< EntryType >& defaultValue )
    {
-      entries.push_back( std::make_unique< ConfigEntryList< EntryType > >( name, description, false, defaultValue ) );
+      using CoercedEntryType = typename ParameterTypeCoercion< EntryType >::type;
+      const auto convertedDefaultValue = ParameterTypeCoercion< std::vector< EntryType > >::convert( defaultValue );
+      entries.push_back( std::make_unique< ConfigEntryList< CoercedEntryType > >( name, description, false, convertedDefaultValue ) );
       currentEntry = entries.back().get();
       isCurrentEntryList = true;
    }
@@ -136,39 +141,29 @@ public:
     * \tparam EntryType Type of the entry enumeration.
     * \param entryEnum Value of the entry enumeration.
     */
-   template< typename EntryType >
-   void addEntryEnum( const EntryType& entryEnum )
+   template< typename EntryType = std::string >
+   void addEntryEnum( const EntryType entryEnum = EntryType{} )
    {
-      TNL_ASSERT_TRUE( this->currentEntry, "there is no current entry" );
+      if( this->currentEntry == nullptr )
+         throw Exceptions::ConfigError( "there is no current entry" );
+
+      using CoercedEntryType = typename ParameterTypeCoercion< EntryType >::type;
       if( isCurrentEntryList ) {
-         ConfigEntryList< EntryType >& entry = dynamic_cast< ConfigEntryList< EntryType >& >( *currentEntry );
-         entry.getEnumValues().push_back( entryEnum );
+         ConfigEntryList< CoercedEntryType >& entry = dynamic_cast< ConfigEntryList< CoercedEntryType >& >( *currentEntry );
+         entry.getEnumValues().push_back( ParameterTypeCoercion< EntryType >::convert( entryEnum ) );
       }
       else {
-         ConfigEntry< EntryType >& entry = dynamic_cast< ConfigEntry< EntryType >& >( *currentEntry );
-         entry.getEnumValues().push_back( entryEnum );
+         ConfigEntry< CoercedEntryType >& entry = dynamic_cast< ConfigEntry< CoercedEntryType >& >( *currentEntry );
+         entry.getEnumValues().push_back( ParameterTypeCoercion< EntryType >::convert( entryEnum ) );
       }
    }
 
-   /**
-    * \brief Adds new entry enumeration of type \e char.
-    *
-    * Adds new option of setting an entry value.
-    * \param entryEnum Value of the entry enumeration.
-    */
-   void addEntryEnum( const char* entryEnum )
-   {
-      TNL_ASSERT_TRUE( this->currentEntry, "there is no current entry" );
-      ConfigEntry< String >& entry = dynamic_cast< ConfigEntry< String >& >( *currentEntry );
-      entry.getEnumValues().push_back( String( entryEnum ) );
-   }
-
    /**
     * \brief Adds delimeter/section to the configuration description.
     *
     * \param delimeter String that defines how the delimeter looks like.
     */
-   void addDelimiter( const String& delimiter )
+   void addDelimiter( const std::string& delimiter )
    {
       entries.push_back( std::make_unique< ConfigDelimiter >( delimiter ) );
       currentEntry = nullptr;
@@ -179,227 +174,26 @@ public:
     *
     * \param name Name of the entry.
     */
-   const ConfigEntryBase* getEntry( const String& name ) const
+   const ConfigEntryBase* getEntry( const std::string& name ) const
    {
       // ConfigDelimiter has empty name
-      if( ! name )
+      if( name.empty() )
          return nullptr;
 
       const int entries_num = entries.size();
       for( int i = 0; i < entries_num; i++ )
-         if( entries[ i ]->name == name )
+         if( entries[ i ]->getName() == name )
             return entries[ i ].get();
       return nullptr;
    }
 
-
-   //! Returns empty string if given entry does not exist
-   //const String getEntryType( const char* name ) const;
-
-   //! Returns zero pointer if there is no default value
-   template< class T >
-   const T* getDefaultValue( const String& name ) const
-   {
-      // ConfigDelimiter has empty name
-      if( ! name )
-         return nullptr;
-
-      const int entries_num = entries.size();
-      for( int i = 0; i < entries_num; i++ )
-         if( entries[ i ]->name == name ) {
-            if( entries[ i ]->hasDefaultValue ) {
-               const ConfigEntry< T >& entry = dynamic_cast< ConfigEntry< T >& >( *entries[ i ] );
-               return entry->default_value;
-            }
-            return nullptr;
-         }
-      std::cerr << "Asking for the default value of unknown parameter." << std::endl;
-      return nullptr;
-   }
-
-   //! Returns zero pointer if there is no default value
-   template< class T >
-   T* getDefaultValue( const String& name )
-   {
-      // ConfigDelimiter has empty name
-      if( ! name )
-         return nullptr;
-
-      const int entries_num = entries.size();
-      for( int i = 0; i < entries_num; i++ )
-         if( entries[ i ] -> name == name ) {
-            if( entries[ i ] -> hasDefaultValue ) {
-               ConfigEntry< T >& entry = dynamic_cast< ConfigEntry< T >& >( *entries[ i ] );
-               return entry->default_value;
-            }
-            return nullptr;
-         }
-      std::cerr << "Asking for the default value of unknown parameter." << std::endl;
-      return nullptr;
-   }
-
-   /**
-    * \brief Fills in the parameters from the \e parameter_container.
-    *
-    * Parameters which were not defined in the command line by user but have their default value are added to the congiguration description.
-    * If there is missing entry with defined default value in the Config::ParameterContainer it is going to be added.
-    * \param parameter_container Name of the ParameterContainer object.
-    */
-   void addMissingEntries( Config::ParameterContainer& parameter_container ) const
-   {
-      const int size = entries.size();
-      for( int i = 0; i < size; i++ )
-      {
-         const char* entry_name = entries[ i ]->name.getString();
-         if( entries[ i ]->hasDefaultValue &&
-             ! parameter_container.checkParameter( entry_name ) )
-         {
-            if( entries[ i ]->getEntryType() == "TNL::String" )
-            {
-               ConfigEntry< String >& entry = dynamic_cast< ConfigEntry< String >& >( *entries[ i ] );
-               parameter_container.addParameter< String >( entry_name, entry.defaultValue );
-               continue;
-            }
-            else if( entries[ i ]->getEntryType() == "bool" )
-            {
-               ConfigEntry< bool >& entry = dynamic_cast< ConfigEntry< bool >& >( *entries[ i ] );
-               parameter_container.addParameter< bool >( entry_name, entry.defaultValue );
-               continue;
-            }
-            else if( entries[ i ]->getEntryType() == "int" )
-            {
-               ConfigEntry< int >& entry = dynamic_cast< ConfigEntry< int >& >( *entries[ i ] );
-               parameter_container.addParameter< int >( entry_name, entry.defaultValue );
-               continue;
-            }
-            else if( entries[ i ]->getEntryType() == "double" )
-            {
-               ConfigEntry< double >& entry = dynamic_cast< ConfigEntry< double >& >( *entries[ i ] );
-               parameter_container.addParameter< double >( entry_name, entry.defaultValue );
-               continue;
-            }
-            else if( entries[ i ]->getEntryType() == "ConfigEntryList< TNL::String >" )
-            {
-               ConfigEntryList< String >& entry = dynamic_cast< ConfigEntryList< String >& >( *entries[ i ] );
-               parameter_container.addList< String >( entry_name, entry.defaultValue );
-               continue;
-            }
-            else if( entries[ i ]->getEntryType() == "ConfigEntryList< bool >" )
-            {
-               ConfigEntryList< bool >& entry = dynamic_cast< ConfigEntryList< bool >& >( *entries[ i ] );
-               parameter_container.addList< bool >( entry_name, entry.defaultValue );
-               continue;
-            }
-            else if( entries[ i ]->getEntryType() == "ConfigEntryList< int >" )
-            {
-               ConfigEntryList< int >& entry = dynamic_cast< ConfigEntryList< int >& >( *entries[ i ] );
-               parameter_container.addList< int >( entry_name, entry.defaultValue );
-               continue;
-            }
-            else if( entries[ i ]->getEntryType() == "ConfigEntryList< double >" )
-            {
-               ConfigEntryList< double >& entry = dynamic_cast< ConfigEntryList< double >& >( *entries[ i ] );
-               parameter_container.addList< double >( entry_name, entry.defaultValue );
-               continue;
-            }
-            else
-            {
-               throw std::runtime_error( "Method ConfigDescription::addMissingEntries encountered "
-                                         "unsupported entry type: " + entries[ i ]->getEntryType() );
-            }
-         }
-      }
-   }
-
-   //! Check for all entries with the flag 'required'.
-   /*! Returns false if any parameter is missing.
-    */
-   bool checkMissingEntries( Config::ParameterContainer& parameter_container,
-                             bool printUsage,
-                             const char* programName ) const
-   {
-      const int size = entries.size();
-      std::vector< std::string > missingParameters;
-      for( int i = 0; i < size; i++ )
-      {
-         const char* entry_name = entries[ i ] -> name.getString();
-         if( entries[ i ] -> required &&
-             ! parameter_container.checkParameter( entry_name ) )
-            missingParameters.push_back( entry_name );
-      }
-      if( missingParameters.size() > 0 )
-      {
-         std::cerr << "Some mandatory parameters are misssing. They are listed at the end. " << std::endl;
-         if( printUsage )
-            this->printUsage( programName );
-         std::cerr << "Add the following missing parameters to the command line: " << std::endl << "   ";
-         for( int i = 0; i < (int) missingParameters.size(); i++ )
-            std::cerr << "--" << missingParameters[ i ] << " ... ";
-         std::cerr << std::endl;
-         return false;
-      }
-      return true;
-   }
-
-   /**
-    * \brief Prints configuration description with the \e program_name at the top.
-    *
-    * \param program_name Name of the program
-    */
-   void printUsage( const char* program_name ) const
-   {
-      std::cout << "Usage of: " << program_name << std::endl << std::endl;
-      const int entries_num = entries.size();
-      int max_name_length( 0 );
-      int max_type_length( 0 );
-      for( int j = 0; j < entries_num; j++ )
-         if( ! entries[ j ]->isDelimiter() )
-         {
-            max_name_length = std::max( max_name_length,
-                        entries[ j ] -> name. getLength() );
-            max_type_length = std::max( max_type_length,
-                        entries[ j ] -> getUIEntryType().getLength() );
-         }
-      max_name_length += 2; // this is for '--'
-
-      for( int j = 0; j < entries_num; j++ )
-      {
-         if( entries[ j ]->isDelimiter() )
-         {
-            std::cout << std::endl;
-            std::cout << entries[ j ]->description;
-            std::cout << std::endl << std::endl;
-         }
-         else
-         {
-            std::cout << std::setw( max_name_length + 3 ) << String( "--" ) + entries[ j ]->name
-                 << std::setw( max_type_length + 5 ) << entries[ j ] -> getUIEntryType()
-                 << "    " << entries[ j ]->description;
-            if( entries[ j ] -> required )
-               std::cout << " *** REQUIRED ***";
-            if( entries[ j ]->hasEnumValues() )
-            {
-               std::cout << std::endl
-                    << std::setw( max_name_length + 3 ) << ""
-                    << std::setw( max_type_length + 5 ) << ""
-                    << "    ";
-               entries[ j ]->printEnumValues();
-            }
-            if( entries[ j ]->hasDefaultValue )
-            {
-               std::cout << std::endl
-                    << std::setw( max_name_length + 3 ) << ""
-                    << std::setw( max_type_length + 5 ) << ""
-                    << "    ";
-               std::cout << "- Default value is: " << entries[ j ]->printDefaultValue();
-            }
-            std::cout << std::endl;
-         }
-      }
-      std::cout << std::endl;
-   }
-
-   //bool parseConfigDescription( const char* file_name );
+   // iterators
+   auto begin() noexcept { return entries.begin(); }
+   auto begin() const noexcept { return entries.begin(); }
+   auto cbegin() const noexcept { return entries.cbegin(); }
+   auto end() noexcept { return entries.end(); }
+   auto end() const noexcept { return entries.end(); }
+   auto cend() const noexcept { return entries.cend(); }
 
 protected:
    std::vector< std::unique_ptr< ConfigEntryBase > > entries;
@@ -407,7 +201,5 @@ protected:
    bool isCurrentEntryList = false;
 };
 
-} //namespace Config
-} //namespace TNL
-
-#include <TNL/Config/parseCommandLine.h>
+} // namespace Config
+} // namespace TNL
diff --git a/src/TNL/Config/ConfigEntry.h b/src/TNL/Config/ConfigEntry.h
index 370366e5ea3c7e906f948417d66c32de41b01aea..ff27d7708cdb2783bf5ce1869bb9a7ee290c3e9b 100644
--- a/src/TNL/Config/ConfigEntry.h
+++ b/src/TNL/Config/ConfigEntry.h
@@ -2,110 +2,119 @@
                           ConfigEntry.h  -  description
                              -------------------
     begin                : Jul 5, 2014
-    copyright            : (C) 2014 by Tomas Oberhuber
+    copyright            : (C) 2014 by Tomas Oberhuber et al.
     email                : tomas.oberhuber@fjfi.cvut.cz
  ***************************************************************************/
 
 /* See Copyright Notice in tnl/Copyright */
 
+// Implemented by: Tomáš Oberhuber, Jakub Klinkovský
+
 #pragma once
 
 #include <vector>
+#include <sstream>
 
-#include <TNL/TypeInfo.h>
 #include <TNL/Config/ConfigEntryBase.h>
+#include <TNL/Config/ConfigEntryType.h>
 
 namespace TNL {
 namespace Config {
 
-template< typename EntryType >
-struct ConfigEntry : public ConfigEntryBase
+template< typename EntryType, typename DefaultValueType = EntryType >
+class ConfigEntry : public ConfigEntryBase
 {
-   EntryType defaultValue;
+   static_assert( std::is_same< EntryType, DefaultValueType >::value || std::is_same< std::vector< EntryType >, DefaultValueType >::value,
+                  "DefaultValueType must be the same as either EntryType or std::vector< EntryType >" );
 
-   std::vector< EntryType > enumValues;
+   DefaultValueType defaultValue;
 
-   public:
+   std::vector< EntryType > enumValues;
 
-   ConfigEntry( const String& name,
-                const String& description,
+public:
+   ConfigEntry( const std::string& name,
+                const std::string& description,
                 bool required )
    : ConfigEntryBase( name,
                       description,
                       required )
    {
-      hasDefaultValue = false;
+      _hasDefaultValue = false;
    }
 
-   ConfigEntry( const String& name,
-                const String& description,
+   ConfigEntry( const std::string& name,
+                const std::string& description,
                 bool required,
-                const EntryType& defaultValue)
+                const DefaultValueType& defaultValue)
    : ConfigEntryBase( name,
                       description,
                       required ),
      defaultValue( defaultValue )
    {
-      hasDefaultValue = true;
+      _hasDefaultValue = true;
    }
 
-   String getEntryType() const
+   virtual std::string getUIEntryType() const override
    {
-      return TNL::getType< EntryType >();
+      return Config::getUIEntryType< DefaultValueType >();
    }
 
-   String getUIEntryType() const
+   virtual std::string printDefaultValue() const override
    {
-      return TNL::Config::getUIEntryType< EntryType >();
+      // printDefaultValue must be compilable even if DefaultValueType is std::vector,
+      // so we can't override the method in ConfigEntryList
+      return _print_value( defaultValue );
    }
 
-   String printDefaultValue() const
+   virtual bool hasEnumValues() const override
    {
-      return convertToString( defaultValue );
+      if( enumValues.size() > 0 )
+         return true;
+      return false;
    }
 
-   std::vector< EntryType >& getEnumValues()
+   virtual void printEnumValues( std::ostream& str ) const override
    {
-      return this->enumValues;
+      str << "- Can be:           ";
+      int i;
+      for( i = 0; i < (int) enumValues.size() - 1; i++ )
+         str << enumValues[ i ] << ", ";
+      str << enumValues[ i ];
+      str << " ";
    }
 
-   bool hasEnumValues() const
+   virtual DefaultValueType getDefaultValue() const
    {
-      if( enumValues.size() > 0 )
-         return true;
-      return false;
+      return defaultValue;
    }
 
-   void printEnumValues() const
+   virtual std::vector< EntryType >& getEnumValues()
    {
-      std::cout << "- Can be:           ";
-      int i;
-      for( i = 0; i < (int) enumValues.size() - 1; i++ )
-         std::cout << enumValues[ i ] << ", ";
-      std::cout << enumValues[ i ];
-      std::cout << " ";
+      return enumValues;
+   }
+
+   virtual const std::vector< EntryType >& getEnumValues() const
+   {
+      return enumValues;
+   }
+
+private:
+   static std::string _print_value( const EntryType& value )
+   {
+      std::stringstream str;
+      str << value;
+      return str.str();
    }
 
-   bool checkValue( const EntryType& value ) const
+   static std::string _print_value( const std::vector< EntryType >& vec )
    {
-      if( this->enumValues.size() > 0 )
-      {
-         bool found( false );
-         for( int i = 0; i < (int) this->enumValues.size(); i++ )
-            if( value == this->enumValues[ i ] )
-            {
-               found = true;
-               break;
-            }
-         if( ! found )
-         {
-            std::cerr << "The value " << value << " is not allowed for the config entry " << this->name << "." << std::endl;
-            this->printEnumValues();
-            std::cerr << std::endl;
-            return false;
-         }
-      }
-      return true;
+      std::stringstream str;
+      str << "[ ";
+      for( std::size_t i = 0; i < vec.size() - 1; i++ )
+         str << vec[ i ] << ", ";
+      str << vec[ vec.size() - 1 ];
+      str << " ]";
+      return str.str();
    }
 };
 
diff --git a/src/TNL/Config/ConfigEntryBase.h b/src/TNL/Config/ConfigEntryBase.h
index c6dc19fbfecaeb61a7dfcefcd2720522357e9922..8a38cb68377ced38cba8ba36e0af8fb085bcdb75 100644
--- a/src/TNL/Config/ConfigEntryBase.h
+++ b/src/TNL/Config/ConfigEntryBase.h
@@ -2,47 +2,59 @@
                           ConfigEntryBase.h  -  description
                              -------------------
     begin                : Jul 5, 2014
-    copyright            : (C) 2014 by Tomas Oberhuber
+    copyright            : (C) 2014 by Tomas Oberhuber et al.
     email                : tomas.oberhuber@fjfi.cvut.cz
  ***************************************************************************/
 
 /* See Copyright Notice in tnl/Copyright */
 
+// Implemented by: Tomáš Oberhuber, Jakub Klinkovský
+
 #pragma once
 
+#include <string>
+
 namespace TNL {
 namespace Config {
 
-struct ConfigEntryBase
+class ConfigEntryBase
 {
-   String name;
+protected:
+   std::string name;
 
-   String description;
+   std::string description;
 
    bool required;
 
-   bool hasDefaultValue;
+   bool _hasDefaultValue;
 
-   ConfigEntryBase( const String& name,
-                    const String& description,
+public:
+   ConfigEntryBase( const std::string& name,
+                    const std::string& description,
                     bool required )
       : name( name ),
         description( description ),
         required( required ),
-        hasDefaultValue( false )
+        _hasDefaultValue( false )
    {}
 
-   virtual String getEntryType() const = 0;
+   const std::string& getName() const { return name; }
+
+   const std::string& getDescription() const { return description; }
+
+   bool isRequired() const { return required; }
+
+   bool hasDefaultValue() const { return _hasDefaultValue; }
 
-   virtual String getUIEntryType() const = 0;
+   virtual std::string getUIEntryType() const = 0;
 
    virtual bool isDelimiter() const { return false; }
 
-   virtual String printDefaultValue() const { return ""; }
+   virtual std::string printDefaultValue() const { return ""; }
 
    virtual bool hasEnumValues() const { return false; }
 
-   virtual void printEnumValues() const {}
+   virtual void printEnumValues( std::ostream& str ) const {}
 
    virtual ~ConfigEntryBase() = default;
 };
diff --git a/src/TNL/Config/ConfigEntryList.h b/src/TNL/Config/ConfigEntryList.h
index 86f2642349ad470f2a8fd268ad117b1d3baf268a..7d6c06c71fc0cd9ca2bd7958a79b21b0e9b1bcf7 100644
--- a/src/TNL/Config/ConfigEntryList.h
+++ b/src/TNL/Config/ConfigEntryList.h
@@ -2,115 +2,27 @@
                           ConfigEntryList.h  -  description
                              -------------------
     begin                : Jul 5, 2014
-    copyright            : (C) 2014 by Tomas Oberhuber
+    copyright            : (C) 2014 by Tomas Oberhuber et al.
     email                : tomas.oberhuber@fjfi.cvut.cz
  ***************************************************************************/
 
 /* See Copyright Notice in tnl/Copyright */
 
-#pragma once
+// Implemented by: Tomáš Oberhuber, Jakub Klinkovský
 
-#include <vector>
+#pragma once
 
-#include <TNL/TypeInfo.h>
-#include <TNL/Config/ConfigEntryBase.h>
+#include <TNL/Config/ConfigEntry.h>
 
 namespace TNL {
 namespace Config {
 
 template< typename EntryType >
-struct ConfigEntryList : public ConfigEntryBase
+class ConfigEntryList : public ConfigEntry< EntryType, std::vector< EntryType > >
 {
-   EntryType defaultValue;
-
-   std::vector< EntryType > enumValues;
-
-   public:
-
-   ConfigEntryList( const String& name,
-                    const String& description,
-                    bool required )
-   : ConfigEntryBase( name,
-                      description,
-                      required )
-   {
-      hasDefaultValue = false;
-   }
-
-   ConfigEntryList( const String& name,
-                    const String& description,
-                    bool required,
-                    const EntryType& defaultValue)
-   : ConfigEntryBase( name,
-                      description,
-                      required ),
-     defaultValue( defaultValue )
-   {
-      hasDefaultValue = true;
-   }
-
-   String getEntryType() const
-   {
-      return String("ConfigEntryList< ") + TNL::getType< EntryType >() + " >";
-   }
-
-   String getUIEntryType() const
-   {
-      return TNL::Config::getUIEntryType< std::vector< EntryType > >();
-   }
-
-   String printDefaultValue() const
-   {
-      return convertToString( defaultValue );
-   }
-
-   std::vector< EntryType >& getEnumValues()
-   {
-      return this->enumValues;
-   }
-
-   bool hasEnumValues() const
-   {
-      if( enumValues.size() > 0 )
-         return true;
-      return false;
-   }
-
-   void printEnumValues() const
-   {
-      std::cout << "- Can be:           ";
-      int i;
-      for( i = 0; i < (int) enumValues.size() - 1; i++ )
-         std::cout << enumValues[ i ] << ", ";
-      std::cout << enumValues[ i ];
-      std::cout << " ";
-   }
-
-   bool checkValue( const std::vector< EntryType >& values ) const
-   {
-      if( this->enumValues.size() != 0 )
-      {
-         for( int j = 0; j < (int) values.size(); j++ )
-         {
-            const EntryType& value = values[ j ];
-            bool found( false );
-            for( int i = 0; i < (int) this->enumValues.size(); i++ )
-               if( value == this->enumValues[ i ] )
-               {
-                  found = true;
-                  break;
-               }
-            if( ! found )
-            {
-               std::cerr << "The value " << value << " is not allowed for the config entry " << this->name << "." << std::endl;
-               this->printEnumValues();
-               std::cerr << std::endl;
-               return false;
-            }
-         }
-      }
-      return true;
-   }
+public:
+   // inherit constructors
+   using ConfigEntry< EntryType, std::vector< EntryType > >::ConfigEntry;
 };
 
 } // namespace Config
diff --git a/src/TNL/Config/ConfigEntryType.h b/src/TNL/Config/ConfigEntryType.h
index bb656f8fcf6b38d07e68bb06a3c65e11a90e0269..b93cdaffb731c1a1b9b22de03df603e9816eeb82 100644
--- a/src/TNL/Config/ConfigEntryType.h
+++ b/src/TNL/Config/ConfigEntryType.h
@@ -2,54 +2,105 @@
                           ConfigEntryType.h  -  description
                              -------------------
     begin                : Jul 5, 2014
-    copyright            : (C) 2014 by Tomas Oberhuber
+    copyright            : (C) 2014 by Tomas Oberhuber et al.
     email                : tomas.oberhuber@fjfi.cvut.cz
  ***************************************************************************/
 
 /* See Copyright Notice in tnl/Copyright */
 
+// Implemented by: Tomáš Oberhuber, Jakub Klinkovský
+
 #pragma once
 
+#include <type_traits>
 #include <vector>
-
-#include <TNL/String.h>
+#include <TNL/variant.hpp>   // backport of std::variant from C++17
 
 namespace TNL {
 namespace Config {
 
-template< typename EntryType >
-inline String getUIEntryType() { return "Unknown type."; };
+using mpark::variant;
+using mpark::get;
+using mpark::monostate;
+using mpark::holds_alternative;
 
-template<> inline String getUIEntryType< String >() { return "string"; };
-template<> inline String getUIEntryType< bool >()   { return "bool"; };
-template<> inline String getUIEntryType< int >()    { return "integer"; };
-template<> inline String getUIEntryType< double >() { return "real"; };
+// aliases for integer types
+using UnsignedInteger = std::size_t;
+using Integer = std::make_signed_t< std::size_t >;
 
-template<> inline String getUIEntryType< std::vector< String > >() { return "list of string"; };
-template<> inline String getUIEntryType< std::vector< bool > >()   { return "list of bool"; };
-template<> inline String getUIEntryType< std::vector< int > >()    { return "list of integer"; };
-template<> inline String getUIEntryType< std::vector< double > >() { return "list of real"; };
+using Parameter = variant< monostate,
+                           bool,
+                           Integer,
+                           UnsignedInteger,
+                           double,
+                           std::string,
+                           std::vector< bool >,
+                           std::vector< Integer >,
+                           std::vector< UnsignedInteger >,
+                           std::vector< double >,
+                           std::vector< std::string >
+                         >;
 
-struct ConfigEntryType
+template< typename T >
+struct ParameterTypeCoercion
 {
-   String basic_type;
+   using type =
+      std::conditional_t< std::is_same< T, bool >::value, bool,
+         std::conditional_t< std::is_integral< T >::value && std::is_signed< T >::value, Integer,
+            std::conditional_t< std::is_integral< T >::value && std::is_unsigned< T >::value, UnsignedInteger,
+               std::conditional_t< std::is_floating_point< T >::value, double,
+                  std::conditional_t< std::is_base_of< std::string, T >::value, std::string,
+                     std::conditional_t< std::is_same< std::decay_t< T >, const char* >::value, std::string,
+                        T
+                     >
+                  >
+               >
+            >
+         >
+      >;
 
-   bool list_entry;
+   static type convert( const T& v ) { return v; }
+   template< typename Result >
+   static Result convert_back( const type& v ) { return v; }
+};
 
-   ConfigEntryType() = default;
+template< typename T >
+struct ParameterTypeCoercion< std::vector< T > >
+{
+   using type = std::vector< typename ParameterTypeCoercion< T >::type >;
 
-   ConfigEntryType( const String& _basic_type,
-                    const bool _list_entry )
-   : basic_type( _basic_type ),
-     list_entry( _list_entry )
-   {}
+   static type convert( const std::vector< T >& vec )
+   {
+      type new_vec;
+      for( auto value : vec )
+         new_vec.push_back( value );
+      return new_vec;
+   }
 
-   void Reset()
+   template< typename Result >
+   static Result convert_back( const type& vec )
    {
-      basic_type.clear();
-      list_entry = false;
+      Result new_vec;
+      for( auto value : vec )
+         new_vec.push_back( value );
+      return new_vec;
    }
 };
 
+template< typename EntryType >
+std::string getUIEntryType() { throw std::logic_error( "getUIEntryType called with unknown type." ); };
+
+template<> inline std::string getUIEntryType< bool >()             { return "bool"; };
+template<> inline std::string getUIEntryType< Integer >()          { return "integer"; };
+template<> inline std::string getUIEntryType< UnsignedInteger >()  { return "unsigned integer"; };
+template<> inline std::string getUIEntryType< double >()           { return "real"; };
+template<> inline std::string getUIEntryType< std::string >()      { return "string"; };
+
+template<> inline std::string getUIEntryType< std::vector< bool > >()             { return "list of bool"; };
+template<> inline std::string getUIEntryType< std::vector< Integer > >()          { return "list of integer"; };
+template<> inline std::string getUIEntryType< std::vector< UnsignedInteger > >()  { return "list of unsigned integer"; };
+template<> inline std::string getUIEntryType< std::vector< double > >()           { return "list of real"; };
+template<> inline std::string getUIEntryType< std::vector< std::string > >()      { return "list of string"; };
+
 } // namespace Config
 } // namespace TNL
diff --git a/src/TNL/Config/ParameterContainer.h b/src/TNL/Config/ParameterContainer.h
index 79a50e151063cd9b5358add30422d713e40a0cc8..264842c9418f91aeac479dd5f1a686992dfc68d8 100644
--- a/src/TNL/Config/ParameterContainer.h
+++ b/src/TNL/Config/ParameterContainer.h
@@ -1,327 +1,186 @@
 /***************************************************************************
-                          Config::ParameterContainer.h  -  description
+                          ParameterContainer.h  -  description
                              -------------------
     begin                : 2007/06/15
-    copyright            : (C) 2007 by Tomas Oberhuber
+    copyright            : (C) 2007 by Tomas Oberhuber et al.
     email                : tomas.oberhuber@fjfi.cvut.cz
  ***************************************************************************/
 
 /* See Copyright Notice in tnl/Copyright */
 
+// Implemented by: Tomáš Oberhuber, Jakub Klinkovský
+
 #pragma once
 
+#include <unordered_map>
 #include <vector>
-#include <memory>
 
+#include <TNL/Config/ConfigEntryType.h>
 #include <TNL/TypeInfo.h>
-#include <TNL/String.h>
-//#include <TNL/Debugging/StackBacktrace.h>
+#include <TNL/Exceptions/ConfigError.h>
 
 namespace TNL {
 namespace Config {
 
-struct ParameterBase
-{
-   ParameterBase( const String& name,
-                  const String& type )
-   : name( name ), type( type )
-   {}
-
-   String name, type;
-
-   // Virtual destructor is needed to avoid undefined behaviour when deleting the
-   // ParameterContainer::parameters vector, see https://stackoverflow.com/a/8752126
-   virtual ~ParameterBase() = default;
-};
-
-template< class T >
-struct Parameter : public ParameterBase
-{
-   Parameter( const String& name,
-              const String& type,
-              const T& value )
-   : ParameterBase( name, type ), value( value )
-   {}
-
-   T value;
-};
-
 class ParameterContainer
 {
 public:
    /**
     * \brief Adds new parameter to the ParameterContainer.
     *
-    * \tparam T Type of parameter value.
     * \param name Name of the new parameter.
     * \param value Value assigned to the parameter.
     */
    template< class T >
-   bool addParameter( const String& name,
+   void addParameter( const std::string& name,
                       const T& value )
    {
-      parameters.push_back( std::make_unique< Parameter< T > >( name, TNL::getType< T >(), value ) );
-      return true;
+      parameters.emplace( name, ParameterTypeCoercion< T >::convert( value ) );
    }
 
    /**
     * \brief Adds new parameter to the ParameterContainer.
     *
-    * \tparam T Type of parameter value.
     * \param name Name of the new parameter.
     * \param value Value assigned to the parameter.
     */
    template< class T >
-   bool addList( const String& name,
-                 const T& value )
+   void addParameter( const std::string& name,
+                      const std::vector< T >& value )
    {
-      std::vector< T > v;
-      v.push_back( value );
-      parameters.push_back( std::make_unique< Parameter< std::vector< T > > >( name, TNL::getType< T >(), v ) );
-      return true;
+      parameters.emplace( name, ParameterTypeCoercion< std::vector< T > >::convert( value ) );
    }
 
    /**
-    * \brief Checks if the ParameterContainer contains a parameter specified by its name.
+    * \brief Adds new list parameter to the ParameterContainer.
     *
-    * \param name Name of the parameter.
+    * \param name Name of the new parameter.
+    * \param value Vector of values assigned to the parameter.
     */
-   bool checkParameter( const String& name ) const
+   template< class T >
+   void addList( const std::string& name,
+                 const std::vector< T >& value )
    {
-      const int size = parameters.size();
-      for( int i = 0; i < size; i++ )
-         if( parameters[ i ]->name == name )
-            return true;
-      return false;
+      addParameter( name, value );
    }
 
    /**
-    * \brief Checks whether the ParameterContainer contains all specified parameter names.
+    * \brief Adds new list parameter to the ParameterContainer.
     *
-    * \param name Name of the parameter.
+    * \param name Name of the new parameter.
+    * \param value First value assigned to the parameter.
+    * \param values Other values assigned to the parameter.
     */
-   bool checkParameters( std::initializer_list< String > names ) const
+   template< class T, class... Ts >
+   void addList( const std::string& name,
+                 const T& value,
+                 const Ts&... values )
    {
-      for( auto name : names )
-         if( ! checkParameter( name ) )
-            return false;
-      return true;
+      addParameter( name, std::vector< T >{value, values...} );
    }
 
    /**
-    * \brief Assigns new \e value to the parameter \e name.
+    * \brief Checks if the ParameterContainer contains a parameter specified by its name.
     *
-    * \tparam T Type of the parameter value.
-    * \param name Name of parameter.
-    * \param value Value of type T assigned to the parameter.
+    * \param name Name of the parameter.
     */
-   template< class T >
-   bool setParameter( const String& name,
-                      const T& value )
+   bool checkParameter( const std::string& name ) const
    {
-      for( int i = 0; i < (int) parameters.size(); i++ ) {
-         if( parameters[ i ]->name == name ) {
-            if( parameters[ i ]->type == TNL::getType< T >() ) {
-               Parameter< T >& parameter = dynamic_cast< Parameter< T >& >( *parameters[ i ] );
-               parameter.value = value;
-               return true;
-            }
-            else {
-               std::cerr << "Parameter " << name << " already exists with different type "
-                         << parameters[ i ]->type << " not "
-                         << TNL::getType< T >() << std::endl;
-               throw 0;
-            }
-         }
-      }
-      return addParameter< T >( name, value );
+      return parameters.count( name );
    }
 
    /**
-    * \brief Checks whether the parameter \e name is given the \e value.
+    * \brief Checks if the ParameterContainer contains all specified parameter names.
     *
-    * Returns \e true if the parameter \e name is given the \e value.
-    * If the parameter does not have any value or has different value then the given
-    * \e value the method returns \e false and shows message when \e verbose is \e true.
-    *
-    * \param name Name of parameter.
-    * \param value Value of type T we want to check whether is assigned to the parameter.
-    * \param verbose Boolean value defining whether to show error message (when true) or not (when false).
+    * \param names List of the parameter names.
+    */
+   bool checkParameters( std::initializer_list< String > names ) const
+   {
+      for( auto name : names )
+         if( ! checkParameter( name ) )
+            return false;
+      return true;
+   }
+
+   /**
+    * \brief Checks if the parameter \e name already exists in ParameterContainer and holds a value of type \e T.
     *
-    * \par Example
-    * \include ParameterContainerExample.cpp
+    * \param name Name of the parameter.
     */
    template< class T >
-   bool getParameter( const String& name,
-                      T& value,
-                      bool verbose = true ) const
+   bool checkParameterType( const std::string& name ) const
    {
-      for( int i = 0; i < (int) parameters.size(); i++ )
-         if( parameters[ i ]->name == name )
-         {
-            // dynamic_cast throws std::bad_cast if parameters[i] does not have the type Parameter<T>
-            const Parameter< T >& parameter = dynamic_cast< Parameter< T >& >( *parameters[ i ] );
-            value = parameter.value;
+      using CoercedType = typename ParameterTypeCoercion< T >::type;
+      auto search = parameters.find( name );
+      if( search != parameters.end() ) {
+         if( holds_alternative< CoercedType >( search->second ) )
             return true;
-         }
-      if( verbose )
-      {
-         std::cerr << "Missing parameter '" << name << "'." << std::endl;
-         throw 0; //PrintStackBacktrace;
       }
       return false;
    }
 
    /**
-    * \brief Returns parameter value.
+    * \brief Assigns new \e value to the parameter \e name.
     *
+    * \tparam T Type of the parameter value.
     * \param name Name of parameter.
+    * \param value Value of type T assigned to the parameter.
     */
    template< class T >
-   T getParameter( const String& name ) const
+   void setParameter( const std::string& name,
+                      const T& value )
    {
-      for( int i = 0; i < (int) parameters.size(); i++ ) {
-         if( parameters[ i ]->name == name ) {
-            // dynamic_cast throws std::bad_cast if parameters[i] does not have the type Parameter<T>
-            const Parameter< T >& parameter = dynamic_cast< Parameter< T >& >( *parameters[ i ] );
-            return parameter.value;
+      using CoercedType = typename ParameterTypeCoercion< T >::type;
+      auto search = parameters.find( name );
+      if( search != parameters.end() ) {
+         if( holds_alternative< CoercedType >( search->second ) ) {
+            search->second = (CoercedType) value;
+         }
+         else {
+            throw std::logic_error( "Parameter " + name + " already exists with different type. "
+                                    "Current type index is " + std::to_string( search->second.index() ) +
+                                    " (variant type is " + std::string( TNL::getType< Parameter >() ) + "), "
+                                    "tried to set value of type " + std::string( TNL::getType< T >() ) + "." );
          }
       }
-      std::cerr << "The program attempts to get unknown parameter " << name << std::endl;
-      std::cerr << "Aborting the program." << std::endl;
-      throw 0;
+      addParameter< T >( name, value );
    }
 
    /**
-    * \brief Checks whether the parameter list \e name is given the \e value.
-    *
-    * Returns \e true if the parameter list \e name is given the \e value.
-    * If the parameter list does not have any value or has different value then the given
-    * \e value the method returns \e false and shows message when \e verbose is \e true.
-    *
-    * \param name Name of parameter list.
-    * \param value Value of type T we want to check whether is assigned to the parameter.
-    * \param verbose Boolean value defining whether to show error message (when true) or not (when false).
+    * \brief Returns parameter value.
     *
-    * \par Example
-    * \include ParameterContainerExample.cpp
+    * \param name Name of the parameter.
     */
    template< class T >
-   bool getParameter( const String& name,
-                      std::vector< T >& value,
-                      bool verbose = true ) const
+   T getParameter( const std::string& name ) const
    {
-      for( int i = 0; i < (int) parameters.size(); i++ )
-         if( parameters[ i ]->name == name )
-         {
-            // dynamic_cast throws std::bad_cast if parameters[i] does not have the type Parameter<T>
-            const Parameter< std::vector< T > >& parameter = dynamic_cast< Parameter< std::vector< T > >& >( *parameters[ i ] );
-            value = parameter.value;
-            return true;
-         }
-      if( verbose )
-      {
-         std::cerr << "Missing parameter '" << name << "'." << std::endl;
-         throw 0; //PrintStackBacktrace;
+      using CoercedType = typename ParameterTypeCoercion< T >::type;
+      auto search = parameters.find( name );
+      if( search != parameters.end() ) {
+         if( holds_alternative< CoercedType >( search->second ) )
+            return ParameterTypeCoercion< T >::template convert_back< T >( get< CoercedType >( search->second ) );
+         else
+            throw Exceptions::ConfigError( "Parameter " + name + " holds a value of different type than requested. "
+                                           "Current type index is " + std::to_string( search->second.index() ) +
+                                           " (variant type is " + std::string( TNL::getType< Parameter >() ) + "), "
+                                           "tried to get value of type " + std::string( TNL::getType< T >() ) + "." );
       }
-      return false;
+      throw Exceptions::ConfigError( "The program attempts to get unknown parameter " + name + "." );
    }
 
    /**
     * \brief Returns parameter list.
     *
-    * \param name Name of parameter list.
+    * \param name Name of the parameter list.
     */
    template< class T >
-   const std::vector< T >& getList( const String& name ) const
-   {
-      for( int i = 0; i < (int) parameters.size(); i++ ) {
-         if( parameters[ i ]->name == name ) {
-            // dynamic_cast throws std::bad_cast if parameters[i] does not have the type Parameter<T>
-            const Parameter< std::vector< T > >& parameter = dynamic_cast< Parameter< std::vector< T > >& >( *parameters[ i ] );
-            return parameter.value; 
-         }
-      }
-      std::cerr << "The program attempts to get unknown parameter " << name << std::endl;
-      std::cerr << "Aborting the program." << std::endl;
-      throw 0;
-   }
-
-
-/*
-   //! Broadcast to other nodes in MPI cluster
-   void MPIBcast( int root, MPI_Comm mpi_comm = MPI_COMM_WORLD )
+   std::vector< T > getList( const std::string& name ) const
    {
-   #ifdef USE_MPI
-      int i;
-      int size = parameters. getSize();
-      :: MPIBcast( size, 1, root, mpi_comm );
-      for( i = 0; i < size; i ++ )
-      {
-         if( MPIGetRank() == root )
-         {
-            tnlParameterBase* param = parameters[ i ];
-            param -> type. MPIBcast( root, MPI_COMM_WORLD );
-            param -> name. MPIBcast( root, MPI_COMM_WORLD );
-            if( param -> type == "String" )
-            {
-               ( ( tnlParameter< String >* ) param ) -> value. MPIBcast( root, mpi_comm );
-            }
-            if( param -> type == "bool" )
-            {
-               :: MPIBcast( ( ( tnlParameter< bool >* ) param ) -> value, 1, root, mpi_comm );
-            }
-            if( param -> type == "int" )
-            {
-               :: MPIBcast( ( ( tnlParameter< int >* ) param ) -> value, 1, root, mpi_comm );
-            }
-            if( param -> type == "double" )
-            {
-               :: MPIBcast( ( ( tnlParameter< double >* ) param ) -> value, 1, root, mpi_comm );
-            }
-         }
-         else
-         {
-            String param_type, param_name;
-            param_type. MPIBcast( root, MPI_COMM_WORLD );
-            param_name. MPIBcast( root, MPI_COMM_WORLD );
-            if( param_type == "mString" )
-            {
-               String val;
-               val. MPIBcast( root, mpi_comm );
-               addParameter< String >( param_name. getString(),
-                                        val );
-            }
-            if( param_type == "bool" )
-            {
-               bool val;
-               :: MPIBcast( val, 1, root, mpi_comm );
-               addParameter< bool >( param_name. getString(),
-                                     val );
-            }
-            if( param_type == "int" )
-            {
-               int val;
-               :: MPIBcast( val, 1, root, mpi_comm );
-               addParameter< int >( param_name. getString(),
-                                    val );
-            }
-            if( param_type == "double" )
-            {
-               double val;
-               :: MPIBcast( val, 1, root, mpi_comm );
-               addParameter< double >( param_name. getString(),
-                                       val );
-            }
-
-         }
-      }
-   #endif
+      return getParameter< std::vector< T > >( name );
    }
-*/
 
 protected:
-   std::vector< std::unique_ptr< ParameterBase > > parameters;
+   std::unordered_map< std::string, Parameter > parameters;
 };
 
 } // namespace Config
diff --git a/src/TNL/Config/iniparser.hpp b/src/TNL/Config/iniparser.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..bfda58d79c0a560e7b617902aa754d04ad27f34e
--- /dev/null
+++ b/src/TNL/Config/iniparser.hpp
@@ -0,0 +1,1508 @@
+/*==============================================================================================================/
+|                   _         _                  _   ___ _   _ ___ ____                                         |
+|                  | |    ___| | _____ _   _ ___( ) |_ _| \ | |_ _|  _ \ __ _ _ __ ___  ___ _ __                |
+|                  | |   / _ \ |/ / __| | | / __|/   | ||  \| || || |_) / _` | '__/ __|/ _ \ '__|               |
+|                  | |__|  __/   <\__ \ |_| \__ \    | || |\  || ||  __/ (_| | |  \__ \  __/ |                  |
+|                  |_____\___|_|\_\___/\__, |___/   |___|_| \_|___|_|   \__,_|_|  |___/\___|_|                  |
+|                                      |___/                                                                    |
+/===============================================================================================================/
+| All-in-one INI file parser                                                                                    |
+| Provides convenient cross-platform class to load and save .INI files                                          |
+| Extends classic INI-file format with                                                                          |
+| -> arrays (comma (',') separated values: |val1, val2, val3|)                                                  |
+| -> maps (declared as |key1:val1, key2:val2, ... , keyN:valN|)                                                 |
+| -> nested sections (Section2 is considered child of Section1, if Section2 is defined as [Section1.Section2])  |
+| -> file includes (use ";#include <file_path>" to include file with relative or full system path <file_path>)  |
+| Please look in provided file ini-test/test1.ini for extended ini examples, in test_app.cpp for usage examples |
+| Language: C++, STL used                                                                                       |
+| Version:  1.1                                                                                                 |
+/===============================================================================================================/
+| Copyright (c) 2015 by Borovik Alexey
+| MIT License
+|
+| 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.
+/===============================================================================================================*/
+
+#ifndef _INIPARSER_H
+#define _INIPARSER_H
+
+/*---------------------------------------------------------------------------------------------------------------/
+/ Includes
+/---------------------------------------------------------------------------------------------------------------*/
+#include <string>     // for std::string
+#include <vector>     // for std::vector
+#include <map>        // for std::map
+#include <sstream>    // for std::stringstream
+#include <algorithm>  // for std::transform
+#include <functional> // for std::not1, std::ptr_fun
+#include <cctype>     // for std::isspace()
+#include <ctype.h>    // for tolower() and toupper()
+#include <fstream>    // for std::fstream
+#include <string.h>   // for strlen()
+#include <iomanip>    // for std::setprecision
+
+#include <TNL/TypeInfo.h>   // for TNL::getType
+/*---------------------------------------------------------------------------------------------------------------/
+/ Defines & Settings
+/---------------------------------------------------------------------------------------------------------------*/
+
+// Error codes
+#define INI_ERR_INVALID_FILENAME  -1   // Can't open file for reading or writing
+#define INI_ERR_PARSING_ERROR     -2   // File parse error
+
+// INI file syntax can be changed here
+// NOTE: When saving INI files first characters of provided arrays are used
+
+/// Each of the characters in this string opens section
+#define INI_SECTION_OPEN_CHARS   "[{"
+/// Each of the characters in this string closes section
+#define INI_SECTION_CLOSE_CHARS  "]}"
+/// Each of the characters in this string starts comment
+#define INI_COMMENT_CHARS         ";#"
+/// Each of the characters in this string separates entry's name from entry's value
+#define INI_NAME_VALUE_SEP_CHARS "=:"
+/// Each of the characters in this string when found last in input line tells that next line
+/// should be considered as current line's continuation
+#define INI_MULTILINE_CHARS         "\\/"
+
+// Delimiter of values in array
+#define INI_ARRAY_DELIMITER         ','
+// Symbol, opening the segment of array (INI_ARRAY_DELIMITER in segment is not considered as delimiter)
+#define INI_ARRAY_SEGMENT_OPEN      '{'
+// Symbol, closing the segment of array
+#define INI_ARRAY_SEGMENT_CLOSE     '}'
+// Escape character in INI file (INI_ARRAY_SEGMENT_OPEN and INI_ARRAY_SEGMENT_CLOSE if found inside
+// elements of array and maps should be escaped for proper parsing)
+#define INI_ESCAPE_CHARACTER        '\\'
+// Symbol, separating key from value in map
+#define INI_MAP_KEY_VAL_DELIMETER   ':'
+// Delimiter, separating parent name from child name in section full name
+#define INI_SUBSECTION_DELIMETER    '.'
+// Delimiter to separate section name from value name when getting value directly from file
+#define INI_SECTION_VALUE_DELIMETER ':'
+
+// Inclusion line
+// Please note, that inclusion command must appear in comment section (i.e. ;INI_INCLUDE_SEQ)
+#define INI_INCLUDE_SEQ "#include "
+
+// Path delimiter
+#ifdef WIN32
+#    define SYSTEM_PATH_DELIM '\\'
+#else
+#    define SYSTEM_PATH_DELIM '/'
+#endif
+
+/*---------------------------------------------------------------------------------------------------------------/
+/ Auxiliaries
+/---------------------------------------------------------------------------------------------------------------*/
+
+// All parser-related stuff is in INI namespace
+namespace INI
+{
+    /// String to lower case
+    static inline void string_to_lower(std::string& str)
+    {
+        std::transform(str.begin(), str.end(), str.begin(), ::tolower);
+    }
+
+    /// String to upper case
+    static inline void string_to_upper(std::string& str)
+    {
+        std::transform(str.begin(), str.end(), str.begin(), ::toupper);
+    }
+
+    /// stream to be used for conversions
+    class convstream: public std::stringstream
+    {
+    public:
+        convstream() {*this << std::setprecision(17) << std::boolalpha;}
+    };
+
+    /// Convert anything (int, double etc) to string
+    template<class T> std::string t_to_string(const T& i)
+    {
+        convstream ss;
+        std::string s;
+        ss << i;
+        s = ss.str();
+        return s;
+    }
+    /// Special case for string (to avoid overheat)
+    template<> inline std::string t_to_string<std::string>(const std::string& i)
+    { 
+        return i; 
+    }
+
+    /// Convert string to anything (int, double, etc)
+    template<class T> T string_to_t(const std::string& v)
+    {
+        convstream ss;
+        T out;
+        ss << v;
+        ss >> out;
+        if (ss.fail())
+            throw std::runtime_error("Value '" + v + "' could not be converted to type " + TNL::getType<T>().getString() + ".");
+        return out;
+    }
+    /// Special case for string
+    template<> inline std::string string_to_t<std::string>(const std::string& v)
+    {
+        return v;
+    }
+    /// Special case for boolean value
+    /// std::boolalpha only covers 'true' or 'false', while we have more cases
+    template<> inline bool string_to_t<bool>(const std::string& v)
+    {
+        if (!v.size())
+            return false;
+        if (v[0] == '1' || v[0] == 't' || v[0] == 'T' || v[0] == 'Y' || v[0] == 'y')
+            return true;
+        return false;
+    }
+
+    // Calling isspace with signed chars in Visual Studio causes assert failure in DEBUG builds
+    // This hack is approved by MS itself
+#if defined (_MSC_VER)
+    static inline int __in_isspace(int ch) {return std::isspace((unsigned char)ch);}
+#else
+#    define __in_isspace std::isspace
+#endif
+
+    // Trim string from start
+    static inline std::string &ltrim(std::string &s) {
+        s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(__in_isspace))));
+        return s;
+    }
+
+    // Trim string from end
+    static inline std::string &rtrim(std::string &s) {
+        s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(__in_isspace))).base(), s.end());
+        return s;
+    }
+
+    // Trim string from both ends
+    static inline std::string &trim(std::string &s) {
+        return ltrim(rtrim(s));
+    }
+
+    /// Split strings (and trim result) based on the provided separator
+    /// Strings in the vector would be trimmed as well
+    static inline std::vector<std::string> split_string(const std::string& str, const std::string& sep)
+    {
+        std::vector<std::string> lst;
+        if (str.empty())
+            return lst;
+        size_t cur_pos = 0;
+        size_t prev_pos = 0;
+        for (; ; cur_pos+=sep.size(),prev_pos = cur_pos)
+        {
+            cur_pos = str.find(sep,cur_pos);
+            if (cur_pos == std::string::npos)
+                break;
+            std::string out = str.substr(prev_pos,cur_pos-prev_pos);
+            trim(out);
+            lst.push_back(out);
+        }
+        std::string out = str.substr(prev_pos);
+        trim(out);
+        lst.push_back(out);
+        return lst;
+    }
+
+    /// Join array of strings into one with specified separator
+    static inline std::string join_string(const std::vector<std::string>& array, const std::string& sep)
+    {
+        std::string ret;
+        if (!array.size())
+            return ret;
+        for (size_t i = 0; i < array.size()-1; i++)
+            ret += array.at(i) + sep;
+        ret += array.at(array.size()-1);
+        return ret;
+    }
+
+    /// Test whether provided char is in the charset
+    static inline bool char_is_one_of(int ch, const char* charset)
+    {
+        for (const char* it = charset; *it != '\0'; ++it)
+            if (ch == *it)
+                return true;
+        return false;
+    }
+
+    // Replace every occurrence of @param what to @param ret in @param str
+    static inline std::string& str_replace(std::string& str, const std::string& what, const std::string& rep)
+    {
+        int diff = rep.size() - what.size() + 1;
+        for (size_t pos = 0; ;pos += diff)
+        {
+            pos = str.find(what.c_str(),pos);
+            if (pos == std::string::npos)
+                break;
+            str.replace(pos,what.size(),rep);
+        }
+        return str;
+    }
+
+    // normalize path (for relative path to be system-independent)
+    static inline void normalize_path(std::string& path)
+    {
+#ifdef WIN32
+        str_replace(path,"/","\\");
+#else
+        str_replace(path,"\\","/");
+#endif
+    }
+
+    // get file path (excluding file name)
+    static inline std::string file_path(const std::string& file_fullname)
+    {
+        size_t pos = file_fullname.rfind(SYSTEM_PATH_DELIM);
+        if (pos == std::string::npos)
+            return std::string();
+        return file_fullname.substr(0,pos);
+    }
+
+    // get file name (excluding file path)
+    static inline std::string file_name(const std::string& file_fullname)
+    {
+        size_t pos = file_fullname.rfind(SYSTEM_PATH_DELIM);
+        if (pos == std::string::npos)
+            return file_fullname;
+        return file_fullname.substr(pos+1);
+    }
+
+    // check whether path is absolute
+    static inline bool path_is_absolute(const std::string& path)
+    {
+#ifdef WIN32
+        if (path.size() < 2)
+            return false;
+        // absolute filenames should start with drive letter (i.e. c:) 
+        // or network path (\\)
+        if (path[1] == ':' || (path[0] == '\\' && path[1] == '\\'))
+            return true;
+        return false;
+#else
+        // everything is simple under POSIX: absolute path should start with /
+        if (path.size() < 1)
+            return false;
+        if (path[0] == '/')
+            return true;
+        return false;
+#endif
+    }
+
+    // check whether path is relative
+    static inline bool path_is_relative(const std::string& path) {return !path_is_absolute(path);}
+
+    /*---------------------------------------------------------------------------------------------------------------/
+    / INI-related classes
+    /---------------------------------------------------------------------------------------------------------------*/
+
+    // forward declarations
+    class Value;
+    class Array;
+    class Map;
+    class Section;
+    class File;
+    typedef std::vector<Section*> SectionVector;
+    
+    /**
+      * Reference-counting helper class
+      * This should be used as a member in reference-counting classes Value, Array and Map
+    **/
+    template<class T> class RefCountPtr
+    {
+    public:
+        RefCountPtr():_ptr(NULL){}
+        RefCountPtr(const RefCountPtr& cp):_ptr(cp._ptr) {Increment();}
+        RefCountPtr(const T& val):_ptr(NULL){_ptr = new refcont(val);}
+        virtual ~RefCountPtr() {Decrement();}
+        RefCountPtr& operator= (const RefCountPtr& rt)
+        {
+            Decrement();
+            _ptr = rt._ptr;
+            Increment();
+            return *this;
+        }
+        bool operator== (const RefCountPtr& val) const
+        {
+            if (_ptr == val._ptr)
+                return true;
+            if (!_ptr || !val._ptr)
+                return false;
+            return _ptr->val == val._ptr->val;
+        }
+        bool operator!= (const Value& val) const { return !(*this == val);}
+        bool IsValid() const {return (_ptr != NULL);}
+        T* DataPtr() {return (_ptr==NULL?NULL:&_ptr->val);}
+        const T* DataPtr() const { return (_ptr==NULL?NULL:&_ptr->val);}
+        T& Data() {return _ptr->val;}
+        const T& Data() const {return _ptr->val;}
+        T DataObj(const T& defval = T()) const {return (_ptr==NULL?defval:_ptr->val);}
+        T* operator->() {return DataPtr();}
+        const T* operator->() const {return DataPtr();}
+        void Copy()
+        {
+            if (!_ptr)
+                _ptr = new refcont();
+            else if (_ptr->count > 1)
+            {
+                refcont* cp = new refcont(_ptr->val);
+                Decrement();
+                _ptr = cp;
+            }
+        }
+    private:
+        void Decrement()
+        {
+            if (!_ptr)
+                return;
+            _ptr->count--;
+            if (!_ptr->count)
+            {
+                delete(_ptr);
+                _ptr = NULL;
+            }
+        }
+        void Increment()
+        {
+            if (_ptr)
+                _ptr->count++;
+        }
+        struct refcont
+        {
+            refcont() :count(1) {}
+            refcont(const T& pval) :val(pval), count(1) {}
+            T val;
+            size_t count;
+        };
+        refcont* _ptr;
+    };
+
+    /**
+      * Value to be stored in INI-file
+      * This is a simple reference-counting class, storing a pointer to original string
+      * It has some functions for easy converting to\from other types
+      * Value can contain array (in string representation) and be converted to\from it 
+    **/
+    class Value
+    {
+    public:
+        Value(){}
+        Value(const Value& cp):_val(cp._val){}
+        template<class T> Value(const T& val){Set(val);}
+        Value(const char* value){Set(value);}
+        virtual ~Value(){}
+
+        Value& operator= (const Value& rt) {_val = rt._val; return *this;}
+        template<class T> Value& operator= (const T& value) { Set(value); return *this;}
+        bool operator== (const Value& rgh) const { return _val == rgh._val;}
+        bool operator!= (const Value& val) const { return !(*this == val);}
+        bool operator< (const Value& rgh) const
+        {
+            if (!_val.IsValid())
+                return true;
+            if (!rgh._val.IsValid())
+                return false;
+            return (_val.Data() < rgh._val.Data());
+        }
+
+        /// Template function to convert value to any type
+        template<class T> T Get() const { return string_to_t<T>(_val.DataObj());}
+        void Set(const std::string& str) { _val = RefCountPtr<std::string>(str); }
+        /// Template function to set value
+        template<class T> void Set(const T& value) { Set(t_to_string(value)); }
+        // const char* (as any pointer) is defaulting in template to int or bool, which is not what we want
+        void Set(const char* value) { _val = RefCountPtr<std::string>(std::string(value)); }
+
+        /// Converts Value to std::string
+        std::string AsString() const { return _val.DataObj();}
+        /// Converts Value to integer
+        int AsInt() const {return Get<int>();}
+        /// Converts Value to double
+        double AsDouble() const {return Get<double>();}
+        /// Converts Value to boolean
+        bool AsBool() const {return Get<bool>();}
+        /// Converts Value to Array
+        Array AsArray() const;
+        /// Converts Value to Map
+        Map AsMap() const;
+        /// Converts Value to specified type T
+        template<class T> T AsT() const {return Get<T>();}
+
+    private:
+        RefCountPtr<std::string> _val;
+    };
+
+    /**
+      * Array of Values
+      * Reference-counting class
+    **/
+    class Array
+    {
+    public:
+        Array(){}
+        Array(const Array& cp) :_val(cp._val){}
+        Array(const std::string& str, char sep = INI_ARRAY_DELIMITER, char seg_open = INI_ARRAY_SEGMENT_OPEN,
+              char seg_close = INI_ARRAY_SEGMENT_CLOSE, char esc = INI_ESCAPE_CHARACTER)
+        {
+            FromString(str,sep,seg_open,seg_close,esc);
+        }
+        template<class T> Array(const std::vector<T>& vect){FromVector(vect);} 
+        virtual ~Array() {}
+
+        Array& operator= (const Array& rt) {_val = rt._val; return *this;}
+        Array& operator<< (const Value & val) { return PushBack(val);}
+        /// Returns reference to value on specified position @param pos
+        /// Array is automatically widen (if needed) for that operation to always succeed
+        Value& operator[] (size_t pos)
+        {
+            _val.Copy();
+            if (pos >= _val->size())
+                _val->insert(_val->end(),pos-_val->size()+1,Value());
+            return _val.Data()[pos];
+        }
+
+        /// Gets value with specified position @param pos
+        /// If @param pos is >= Size() returns @param def_val
+        Value GetValue(size_t pos, const Value& def_val = Value()) const
+        {
+            if (!_val.IsValid())
+                return def_val;
+            if (pos >= _val->size())
+                return def_val;
+            return _val->at(pos);
+        }
+        /// Sets value @param value to specified position @param pos
+        /// Array is automatically widen (if needed) to allow this operation 
+        void SetValue(size_t pos, const Value& value)
+        {
+            _val.Copy();
+            if (pos >= _val->size())
+            {
+                _val->insert(_val->end(),pos-_val->size(),Value());
+                _val->push_back(value);
+            }
+            else
+                _val->at(pos) = value;
+        }
+        /// Adds @param val to the end of the Array
+        Array& PushBack(const Value& val) 
+        {
+            _val.Copy();
+            _val->push_back(val); 
+            return *this;
+        }
+        /// Returns array size
+        size_t Size() const { if (!_val.IsValid()) return 0; return _val->size(); }
+        
+        /// Formats string with all values of this array represented as strings, separated by @param sep
+        std::string ToString(char sep = INI_ARRAY_DELIMITER, char seg_open = INI_ARRAY_SEGMENT_OPEN,
+                             char seg_close = INI_ARRAY_SEGMENT_CLOSE, char esc = INI_ESCAPE_CHARACTER) const
+        {
+            std::string ret;
+            if (!_val.IsValid())
+                return ret;
+            for (size_t i = 0; i < _val->size(); i++)
+            {
+                std::string tmp = _val->at(i).AsString();
+                std::string out;
+                bool has_delim = false;
+                for (size_t j = 0; j < tmp.size(); j++)
+                {
+                    if (tmp[j] == seg_open || tmp[j] == seg_close)
+                        out.push_back(esc);
+                    else if (tmp[j] == sep)
+                        has_delim = true;
+                    out.push_back(tmp[j]);
+                }
+                if (has_delim)
+                    ret += seg_open + out + seg_close;
+                else
+                    ret += out;
+                if (i != _val->size()-1)
+                    ret += sep;
+            }
+            return ret;
+        }
+        /// Fills array with values from string @param str, separated by @param sep
+        void FromString(const std::string& str, char sep = INI_ARRAY_DELIMITER, 
+                        char seg_open  = INI_ARRAY_SEGMENT_OPEN,
+                        char seg_close = INI_ARRAY_SEGMENT_CLOSE, char esc = INI_ESCAPE_CHARACTER)
+        {
+            _val.Copy();
+            _val->clear();
+            if (str.empty())
+                return;
+            std::string cur_str;
+            int segm_cnt = 0;
+            bool escaped = false;
+            int preesc = 0;
+            for (size_t i = 0; i <= str.size(); ++i)
+            {
+                if (escaped && i < str.size())
+                {
+                    cur_str.push_back(str[i]);
+                    escaped = false;
+                    if (str[i] == esc)
+                        preesc = 2;
+                }
+                else if ((i == str.size()) || (str[i] == sep && !segm_cnt))
+                {
+                    trim(cur_str);
+                    _val->push_back(cur_str);
+                    cur_str.clear();
+                }
+                else if (str[i] == seg_open && !preesc)
+                {
+                    if (segm_cnt)
+                        cur_str.push_back(str[i]);
+                    segm_cnt++;
+                }
+                else if (str[i] == seg_close && !preesc)
+                {
+                    segm_cnt--;
+                    if (segm_cnt < 0)
+                        segm_cnt = 0;
+                    if (segm_cnt)
+                        cur_str.push_back(str[i]);
+                }
+                else if (str[i] == esc)
+                    escaped = true;
+                else
+                    cur_str.push_back(str[i]);
+                if (preesc)
+                    preesc--;
+            }
+        }
+        template<class T> std::vector<T> ToVector() const
+        {
+            std::vector<T> ret;
+            if (!_val.IsValid())
+                return ret;
+            for (size_t i = 0; i < _val->size(); ++i)
+                ret.push_back(_val->at(i).AsT<T>());
+            return ret;
+        }
+        template<class T> void FromVector(const std::vector<T>& vect)
+        {
+            _val.Copy();
+            _val->clear();
+            for (size_t i = 0; i < vect.size(); ++i)
+                _val->push_back(Value(vect.at(i)));
+        }
+        /// Converts Array to Value
+        Value ToValue() const
+        {
+            return Value(ToString());
+        }
+        /// Gets Array from Value
+        void FromValue(const Value& val)
+        {
+            FromString(val.AsString());
+        }
+    private:
+        RefCountPtr<std::vector<Value> > _val;
+    };
+
+    template<> inline void Value::Set<Array> (const Array& value)
+    {
+        Set(value.ToString());
+    }
+    template<> inline Array Value::Get<Array>() const
+    {
+        if (!_val.IsValid())
+            return Array();
+        return Array(_val.Data());
+    }
+    inline Array Value::AsArray() const {return Get<Array>();}
+
+    /**
+      * Map of Values
+      * Reference-counting class
+    **/
+    class Map
+    {
+    public:
+        Map(){}
+        Map(const Map& cp) :_val(cp._val){}
+        Map(const std::string& str, char sep = INI_ARRAY_DELIMITER, char seg_open = INI_ARRAY_SEGMENT_OPEN,
+              char seg_close = INI_ARRAY_SEGMENT_CLOSE, char kval = INI_MAP_KEY_VAL_DELIMETER,
+              char esc = INI_ESCAPE_CHARACTER)
+        {
+            FromString(str,sep,seg_open,seg_close,kval,esc);
+        }
+        template<class T, class M> Map(const std::map<T,M>& mp){FromMap(mp);} 
+        virtual ~Map(){}
+        Map& operator= (const Map& rt) {_val = rt._val; return *this;}
+        /// Returns reference to value of the specified key @param key
+        /// Map is automatically inserts entry for this operation to always succeed
+        Value& operator[] (const Value& key)
+        {
+            _val.Copy();
+            return _val.Data()[key];
+        }
+
+        /// Gets value for specified key @param key
+        /// If there is no specified key - returns @param def_val
+        Value GetValue(const Value& key, const Value& def_val = Value()) const
+        {
+            if (!_val.IsValid())
+                return def_val;
+            std::map<Value,Value>::const_iterator it = _val->find(key);
+            if (it == _val->end())
+                return def_val;
+            return it->second;
+        }
+        /// Sets value @param value to specified key @param key
+        void SetValue(const Value& key, const Value& value)
+        {
+            _val.Copy();
+            std::pair<std::map<Value,Value>::iterator,bool> res = 
+                _val->insert(std::pair<Value,Value>(key,value));
+            if (!res.second)
+                res.first->second = value;
+        }
+        /// Returns map size
+        size_t Size() const { if (!_val.IsValid()) return 0; return _val->size();}
+
+
+        /// Formats string with all values of this map
+        std::string ToString(char sep = INI_ARRAY_DELIMITER, char seg_open = INI_ARRAY_SEGMENT_OPEN,
+                             char seg_close = INI_ARRAY_SEGMENT_CLOSE, char kval = INI_MAP_KEY_VAL_DELIMETER, 
+                             char esc = INI_ESCAPE_CHARACTER) const
+        {
+            std::string ret;
+            if (!_val.IsValid())
+                return ret;
+            for (std::map<Value,Value>::const_iterator it = _val->begin(); it != _val->end(); ++it)
+            {
+                if (it != _val->begin())
+                    ret += sep;
+                std::string key = it->first.AsString();
+                std::string out_key;
+                std::string val = it->second.AsString();
+                std::string out_val;
+                // key
+                bool has_delim = false;
+                for (size_t j = 0; j < key.size(); j++)
+                {
+                    if (key[j] == seg_open || key[j] == seg_close)
+                        out_key.push_back(esc);
+                    else if (key[j] == kval || key[j] == sep)
+                        has_delim = true;
+                    out_key.push_back(key[j]);
+                }
+                if (has_delim)
+                    out_key = seg_open + out_key + seg_close;
+                // value
+                has_delim = false;
+                for (size_t j = 0; j < val.size(); j++)
+                {
+                    if (val[j] == seg_open || val[j] == seg_close)
+                        out_val.push_back(esc);
+                    else if (val[j] == kval || val[j] == sep)
+                        has_delim = true;
+                    out_val.push_back(val[j]);
+                }
+                if (has_delim)
+                    out_val = seg_open + out_val + seg_close;
+                // pair
+                ret += out_key + kval + out_val;
+            }
+            return ret;
+        }
+        /// Fills map with values from string @param str
+        void FromString(const std::string& str, char sep = INI_ARRAY_DELIMITER, 
+                        char seg_open  = INI_ARRAY_SEGMENT_OPEN,
+                        char seg_close = INI_ARRAY_SEGMENT_CLOSE, char kval = INI_MAP_KEY_VAL_DELIMETER,
+                        char esc = INI_ESCAPE_CHARACTER)
+        {
+            _val.Copy();
+            _val->clear();
+            if (str.empty())
+                return;
+            std::string cur_str;
+            std::string cur_key;
+            int segm_cnt = 0;
+            bool escaped = false;
+            int preesc = 0;
+            for (size_t i = 0; i <= str.size(); ++i)
+            {
+                if (escaped && i < str.size())
+                {
+                    cur_str.push_back(str[i]);
+                    escaped = false;
+                    if (str[i] == esc)
+                        preesc = 2;
+                }
+                else if ((i==str.size()) || (str[i] == sep && !segm_cnt))
+                {
+                    trim(cur_str);
+                    std::pair<std::map<Value,Value>::iterator,bool> res = 
+                        _val->insert(std::pair<Value,Value>(Value(cur_key),Value(cur_str)));
+                    if (!res.second)
+                        res.first->second = cur_str;
+                    cur_str.clear();
+                    cur_key.clear();
+                }
+                else if (str[i] == kval && !segm_cnt)
+                {
+                    trim(cur_str);
+                    cur_key = cur_str;
+                    cur_str.clear();
+                }
+                else if (str[i] == seg_open && !preesc)
+                {
+                    if (segm_cnt)
+                        cur_str.push_back(str[i]);
+                    segm_cnt++;
+                }
+                else if (str[i] == seg_close && !preesc)
+                {
+                    segm_cnt--;
+                    if (segm_cnt < 0)
+                        segm_cnt = 0;
+                    if (segm_cnt)
+                        cur_str.push_back(str[i]);
+                }
+                else if (str[i] == esc)
+                    escaped = true;
+                else
+                    cur_str.push_back(str[i]);
+                if (preesc)
+                    preesc--;
+            }
+        }
+        template<class T, class M> std::map<T,M> ToMap() const
+        {
+            std::map<T,M> ret;
+            if (!_val.IsValid())
+                return ret;
+            for (auto it = _val->begin(); it != _val->end(); ++it)
+                ret.insert(std::pair<T,M>(it->first.AsT<T>(),it->second.AsT<M>()));
+            return ret;
+        }
+        template<class T, class M> void FromMap(const std::map<T,M>& mp)
+        {
+            _val.Copy();
+            _val->clear();
+            for (typename std::map<T,M>::const_iterator it = mp.begin(); it != mp.end(); ++it)
+                _val->insert(std::pair<Value,Value>(Value(it->first),Value(it->second)));
+        }
+        /// Converts Array to Value
+        Value ToValue() const
+        {
+            return Value(ToString());
+        }
+        /// Gets Array from Value
+        void FromValue(const Value& val)
+        {
+            FromString(val.AsString());
+        }
+    private:
+        RefCountPtr<std::map<Value,Value> > _val;
+    };
+
+    template<> inline void Value::Set<Map> (const Map& value)
+    {
+        Set(value.ToString());
+    }
+    template<> inline Map Value::Get<Map>() const
+    {
+        if (!_val.IsValid())
+            return Map();
+        return Map(_val.Data());
+    }
+    inline Map Value::AsMap() const {return Get<Map>();}
+
+    /*---------------------------------------------------------------------------------------------------------------/
+    / INI file creation and parsing
+    /---------------------------------------------------------------------------------------------------------------*/
+
+    /**
+      * One section of the ini-file
+      * This can be created by INIFile class only
+    **/
+    class Section
+    {
+        friend class File;
+        typedef std::map<std::string, Value> EntryMap;
+        typedef std::pair<std::string,Value> EntryPair;
+        typedef std::map<std::string, std::string> CommentMap;
+        typedef std::pair<std::string, std::string> CommentPair;
+    /*-----------------------------------------------------------------------------------------------------------/
+    / General functions & iterators
+    /-----------------------------------------------------------------------------------------------------------*/
+    public:
+        typedef EntryMap::iterator values_iter;
+        typedef EntryMap::const_iterator const_values_iter;
+        values_iter ValuesBegin() {return _entries.begin();}
+        const_values_iter ValuesBegin() const {return _entries.begin();}
+        values_iter ValuesEnd() {return _entries.end();}
+        const_values_iter ValuesEnd() const {return _entries.end();}
+        size_t ValuesSize() const {return _entries.size();}
+
+        /// Returns full section name (subsection name will contain '.', separating subsection part)
+        const std::string& FullName() const { return _name;}
+        /// Return section name (will be parsed to get individual section name)
+        std::string Name() const
+        {
+            return _name.substr(_name.rfind(INI_SUBSECTION_DELIMETER)+1);
+        }
+        /// Get comment, associated with this section (written before the line with the section, or after it)
+        std::string Comment() const {return _comment;}
+        /// Return all keys in this section
+        std::vector<std::string> GetSectionKeys() const
+        {
+            std::vector<std::string> keys;
+            for (EntryMap::const_iterator it = _entries.begin(); it != _entries.end(); it++)
+                keys.push_back(it->first);
+            return keys;
+        }
+        /// Get Value
+        Value GetValue(const std::string& key, const Value& def_value = Value()) const
+        {
+            EntryMap::const_iterator it = _entries.find(key);
+            if (it == _entries.end())
+                return def_value;
+            return it->second;
+        }
+        /// Set Value
+        void SetValue(const std::string& key, const Value& val, const std::string& comment = std::string())
+        {
+            _entries[key] = val;
+            if (!comment.empty())
+                _comments[key] = comment;
+        }
+        /// Set Value to array
+        void SetArrayValue(const std::string& key, size_t pos, const Value& val)
+        {
+            Array ar = GetValue(key).AsArray();
+            ar.SetValue(pos,val);
+            SetValue(key,ar);
+        }
+        /// Remove value
+        void RemoveValue(const std::string& key)
+        {
+            EntryMap::iterator it = _entries.find(key);
+            if (it == _entries.end())
+                return;
+            _entries.erase(it);
+        }
+        /// Get comment, associated with provided value
+        std::string GetComment(const std::string& key) const
+        {
+            CommentMap::const_iterator it = _comments.find(key);
+            if (it == _comments.end())
+                return std::string();
+            return it->second;
+        }
+        /// Set comment for specified key
+        void SetComment(const std::string& key, const std::string& comment)
+        {
+            _comments[key] = comment;
+        }
+        /// Save the section's contents to specified stream
+        void Save(std::ostream& stream) const;
+    /*-----------------------------------------------------------------------------------------------------------/
+    / Parent & child parsing
+    /-----------------------------------------------------------------------------------------------------------*/
+    public:
+        Section* FindParent() const;
+        Section* GetParent();
+        Section* FindSubSection(const std::string& name) const;
+        Section* GetSubSection(const std::string& name);
+        SectionVector FindSubSections() const;
+    /*-----------------------------------------------------------------------------------------------------------/
+    / Internal contents
+    /-----------------------------------------------------------------------------------------------------------*/
+    private:
+        /// Only INI::File can create sections
+        Section(File* file, const std::string& name, const std::string& comment = std::string()):
+           _file(file),_name(name),_comment(comment){}
+        /// Class should be copied only by File class to properly handle _file
+        Section(const Section& cp):_file(cp._file),_name(cp._name),_comment(cp._comment),_entries(cp._entries),
+            _comments(cp._comments){}
+        File*       _file;                   // file, this section is associated with
+        std::string _name;                 // name of the section
+        std::string _comment;              // comment to the section
+        EntryMap    _entries;              // all entries in the section
+        CommentMap  _comments;             // all comments, associated with values in the section
+    };
+
+    /**
+      * Main class of the parser
+      * Provides way to load and save ini-files, as well as
+      * setting specific values in them
+    **/
+    class File
+    {
+    public:
+        /// Sections stores all values and comments inside them
+        typedef std::map<std::string, Section*> SectionMap;
+        typedef std::pair<std::string, Section*> SectionPair;
+        typedef SectionMap::iterator          sections_iter;
+        typedef SectionMap::const_iterator    const_sections_iter;
+        /// Result of previous parse operation
+        struct PResult
+        {
+            PResult():error_code(0),error_line_num(0){}
+            void Set(int code, int line_num = 0, const std::string& line = std::string())
+            {
+                error_code = code;
+                error_line_num = line_num;
+                error_line = line;
+            }
+            void Invalidate() {error_code = 0; error_line_num = 0; error_line.clear();}
+            std::string GetErrorDesc() const
+            {
+                if (!error_code)
+                    return "No error";
+                if (error_code == INI_ERR_INVALID_FILENAME)
+                    return std::string("Failed to open file ") + file_name + "!";
+                if (error_code == INI_ERR_PARSING_ERROR)
+                    return std::string("Parse error in file ") + file_name + " on line " 
+                    + t_to_string(error_line) + ": \"" + error_line + "\""; 
+                return "Unknown error!";
+            }
+            int error_code;         // code of the error. 0 if no error
+            int error_line_num;     // number of line with the error
+            std::string error_line; // line with error
+            std::string file_name;  // name of the file with error (will be empty for stream)
+            operator bool() const {return !error_code;}
+        };
+    public:
+        File(){}
+        File(const std::string& fname) { Load(fname);}
+        File(const File& lf) { CopyFrom(lf); }
+        File& operator= (const File& lf) { Unload(); CopyFrom(lf); return *this; }
+        void CopyFrom(const File& lf)
+        {
+            for (SectionMap::const_iterator it = lf._sections.begin(); it != lf._sections.end(); it++)
+            {
+                Section* sect = new Section(*it->second);
+                sect->_file = this;
+                _sections.insert(SectionPair(it->first,sect));
+            }
+            _result = lf._result;
+        }
+        virtual ~File() {Unload();}
+    /*---------------------------------------------------------------------------------------------------------------/
+    / Section & values manipulations
+    /---------------------------------------------------------------------------------------------------------------*/
+    public:
+        /// A way to iterate through all sections
+        /// Section pointer can be accesed as SectionMap::iterator::second, section name - as ::first
+        size_t SectionsSize() const {return _sections.size();}
+        sections_iter SectionsBegin() {return _sections.begin();}
+        const_sections_iter SectionsBegin() const {return _sections.begin();}
+        sections_iter SectionsEnd() {return _sections.end();}
+        const_sections_iter SectionsEnd() const {return _sections.end();}
+
+        /// Get value from the file
+        /// Use INI_SECTION_VALUE_DELIMETER to separate section name from value name
+        Value GetValue(const std::string& name, const Value& def_val = Value())
+        {
+            size_t pos = name.rfind(INI_SECTION_VALUE_DELIMETER);
+            std::string nm;
+            if (pos != std::string::npos)
+                nm = name.substr(0,pos);
+            SectionMap::iterator it = _sections.find(nm);
+            if (it == _sections.end())
+                return def_val;
+            return it->second->GetValue(name.substr(pos+1),def_val);
+        }
+
+        /// Set value to the file
+        /// Use INI_SECTION_VALUE_DELIMETER to separate section name from value name
+        void SetValue(const std::string& name, const Value& value, const std::string& comment = std::string())
+        {
+            size_t pos = name.rfind(INI_SECTION_VALUE_DELIMETER);
+            std::string nm;
+            if (pos != std::string::npos)
+                nm = name.substr(0,pos);
+            Section* sect = GetSection(nm);
+            sect->SetValue(name.substr(pos+1),value,comment);
+        }
+
+        /// Set array Value to array
+        void SetArrayValue(const std::string& key, size_t pos, const Value& val)
+        {
+            Array ar = GetValue(key).AsArray();
+            ar.SetValue(pos,val);
+            SetValue(key,ar);
+        }
+
+        /// Returns pointer to section with specified name
+        /// If section does not exists - creates it
+        Section* GetSection(const std::string& name)
+        {
+            SectionMap::iterator it = _sections.find(name);
+            if (it != _sections.end())
+                return it->second;
+            Section* sc = new Section(this,name);
+            _sections.insert(SectionPair(name,sc));
+            return sc;
+        }
+
+        /// Find existing section by name
+        /// @return pointer to existing section or NULL
+        Section* FindSection(const std::string& name) const
+        {
+            SectionMap::const_iterator it = _sections.find(name);
+            if (it == _sections.end())
+                return NULL;
+            return it->second;
+        }
+
+        /// Deletes section with specified name
+        void DeleteSection(const std::string& name)
+        {
+            SectionMap::iterator it = _sections.find(name);
+            if (it != _sections.end())
+                _sections.erase(it);
+        }
+
+        /// Get subsection of specified section with specified name
+        Section* FindSubSection(const Section* sect, const std::string& name) const
+        {
+            return FindSection(sect->FullName() + INI_SUBSECTION_DELIMETER + name);
+        }
+
+        /// If subsection does not exists, creates it
+        Section* GetSubSection(Section* sect, const std::string& name)
+        {
+            return GetSection(sect->FullName() + INI_SUBSECTION_DELIMETER + name);
+        }
+
+        /// Get parent section of the specified section
+        Section* FindParentSection(const Section* sect) const
+        {
+            size_t pos = sect->FullName().rfind(INI_SUBSECTION_DELIMETER);
+            std::string nm;
+            if (pos != std::string::npos)
+                nm = sect->FullName().substr(0,pos);
+            return FindSection(nm);
+        }
+        /// Get parent section (created if needed)
+        Section* GetParentSection(const Section* sect)
+        {
+            size_t pos = sect->FullName().rfind(INI_SUBSECTION_DELIMETER);
+            std::string nm;
+            if (pos != std::string::npos)
+                nm = sect->FullName().substr(0,pos);
+            return GetSection(nm);
+        }
+        /// Find subsections of the specified section
+        SectionVector FindSubSections(const Section* sect) const
+        {
+            SectionVector ret;
+            if (sect->_file != this)
+                return ret;
+            for (SectionMap::const_iterator it = _sections.begin(); it != _sections.end(); ++it)
+            {
+                if (it->second == sect)
+                    continue;
+                if (it->first.find(sect->FullName() + INI_SUBSECTION_DELIMETER) != std::string::npos)
+                    ret.push_back(it->second);
+            }
+            return ret;
+        }
+        // Get top-level sections (not child of any other sections)
+        SectionVector GetTopLevelSections() const
+        {
+            SectionVector ret;
+            for (SectionMap::const_iterator it = _sections.begin(); it != _sections.end(); ++it)
+            {
+                if (it->first.find(INI_SUBSECTION_DELIMETER) == std::string::npos)
+                    ret.push_back(it->second);
+            }
+            return ret;
+        }
+    /*---------------------------------------------------------------------------------------------------------------/
+    / Load & Save functions
+    /---------------------------------------------------------------------------------------------------------------*/
+    public:
+        /// Load file from input stream, that must be properly opened and prepared for reading
+        /// Set @param unload_prev to true for unloading any stuff currently in memory before loading 
+        /// Set @param rpath to be used as relative path when searching for inclusions in files
+        /// @return 1 if load succeeds, 0 if not
+        /// in case load was not succesfull you can access extended information by calling ParseResult()
+        int Load(std::istream& stream, bool unload_prev = false, const std::string& rpath = std::string())
+        {
+            if (unload_prev)
+                DeleteSections(_sections);
+            return ParseStream(stream,"",rpath,_sections);
+        }
+        /// Load ini from file in system
+        /// Set @param unload_prev to false for not unloading any stuff currently in memory before loading 
+        int Load(const std::string& fname, bool unload_prev = true)
+        {
+            _result.file_name = fname;
+            normalize_path(_result.file_name);
+            std::ifstream file(_result.file_name.c_str(), std::ios::in);
+            if (!file.is_open())
+            {
+                _result.Set(INI_ERR_INVALID_FILENAME);
+                return 0;
+            }
+            return Load(file,unload_prev,file_path(_result.file_name));
+        }
+
+        /// Save ini file to stream
+        void Save(std::ostream& stream) const
+        {
+            SaveStream(stream,_sections); 
+        }
+
+        /// Save ini file to file
+        int Save(const std::string& fname)
+        {
+            _result.file_name = fname;
+            normalize_path(_result.file_name);
+            std::ofstream file(_result.file_name.c_str(), std::ios::out);
+            if (!file.is_open())
+            {
+                _result.Set(INI_ERR_INVALID_FILENAME);
+                return 0;
+            }
+            SaveStream(file,_sections);
+            return 1;
+        }
+
+        /// Save only one section to specifed stream
+        void Save(std::ostream& stream, const Section* sect) const
+        {
+            SectionMap mp;
+            mp.insert(SectionPair(sect->FullName(),const_cast<Section*>(sect)));
+            SaveStream(stream,mp);
+        }
+
+        /// Adds comment line to provided stream
+        /// For it to be not associated with anything you should add newline after it
+        static void AddCommentToStream(std::ostream& stream, const std::string& str)
+        {
+            std::vector<std::string> ar = split_string(str,"\n");
+            for (size_t i = 0; i < ar.size(); i++)
+                stream << INI_COMMENT_CHARS[0] << ar.at(i) << std::endl;
+        }
+
+        /// Adds inclusion line to stream
+        static void AddIncludeToStream(std::ostream& stream, const std::string& path)
+        {
+            stream << INI_COMMENT_CHARS[0] << INI_INCLUDE_SEQ << path << std::endl;
+        }
+
+        /// Unload memory
+        void Unload()
+        {
+            DeleteSections(_sections);
+        }
+        /// Return last operation result
+        const PResult& LastResult() {return _result;}
+    /*---------------------------------------------------------------------------------------------------------------/
+    / Parsing & Saving internals
+    /---------------------------------------------------------------------------------------------------------------*/
+    protected:
+        enum LineType
+        {
+            EMPTY,
+            SECTION,
+            ENTRY,
+            COMMENT,
+            ERROR
+        };
+
+        /**
+          * Parse input line
+          * Line must be concatenated (if needed) and trimmed before passing to this function
+          * @param input_line - Line to parse
+          * @param section - will contain section name in case SECTION return
+          * @param key - will contain key in case ENTRY return
+          * @param value - will contain value in case ENTRY return
+          * @param comment - will contain comment in case of return != EMPTY and != ERROR
+          * @return type of the parsed line
+        **/
+        LineType ParseLine(const std::string& input_line, std::string& section, 
+                           std::string& key, std::string& value, std::string& comment) const
+        {
+            LineType ret = EMPTY;
+            size_t last_pos = input_line.npos;
+            if (input_line.empty())
+                return ret;
+            // comment parsing
+            if (char_is_one_of(input_line.at(0),INI_COMMENT_CHARS))
+            {
+                ret = COMMENT;
+                // Can't do it that way, cause of 'initialization of non-const reference of type from a temporary of type'
+                // stupid GCC rule
+                //comment = trim(input_line.substr(1));
+                comment = input_line.substr(1);
+                trim(comment);
+                return ret;
+            }
+            // section parsing
+            else if (char_is_one_of(input_line.at(0),INI_SECTION_OPEN_CHARS))
+            {
+                last_pos = input_line.find_first_of(INI_SECTION_CLOSE_CHARS);
+                if (last_pos == input_line.npos)
+                    return ERROR;
+                ret = SECTION;
+                section = input_line.substr(1,last_pos-1);
+                trim(section);
+                last_pos++;
+            }
+            // key-value pair parsing
+            else
+            {
+                size_t pos = input_line.find_first_of(INI_NAME_VALUE_SEP_CHARS);
+                // not section, not comment, not empty - error
+                if (pos == input_line.npos || pos == input_line.size()-1)
+                    return ERROR;
+                ret = ENTRY;
+                key = input_line.substr(0,pos);
+                trim(key);
+                last_pos = input_line.find_first_of(INI_COMMENT_CHARS,pos+1);
+                value = input_line.substr(pos+1,last_pos-pos-1);
+                trim(value);
+                if (last_pos != input_line.npos)
+                    last_pos--;
+            }
+            // get associated comment
+            last_pos = input_line.find_first_of(INI_COMMENT_CHARS,last_pos);
+            if (last_pos != input_line.npos)
+            {
+                comment = input_line.substr(last_pos+1);
+                trim(comment);
+            }
+            return ret;
+        }
+
+        /// Parse provided input stream to specified section map
+        /// Comments separated from closest section or key-value pair by 1 or more empty strings
+        /// would be ignored
+        /// Default section will be created if needed
+        int ParseStream(std::istream& stream, const std::string& def_section, 
+                        const std::string& rpath, SectionMap& pmap)
+        {
+            Section* cur_sect = NULL;
+            std::string pcomment;
+            std::string prev_line;
+            _result.Invalidate();
+
+            // Find whether default section already exists in provided map
+            // if not - it will be created later if needed
+            SectionMap::iterator it = pmap.find(def_section);
+            if (it != pmap.end())
+                cur_sect = it->second;
+
+            for (int lnc = 1; !stream.eof(); lnc++)
+            {
+                std::string line;
+                std::getline(stream,line);
+                trim(line);
+                if (line.empty())
+                {
+                    pcomment.clear();
+                    continue;
+                }
+                // Handle multiline strings
+                if (char_is_one_of(line.at(line.size()-1),INI_MULTILINE_CHARS))
+                {
+                    prev_line = prev_line + line.substr(0,line.size()-1);
+                    continue;
+                }
+                else if (!prev_line.empty())
+                {
+                    line = prev_line + line;
+                    prev_line.clear();
+                }
+                std::string section_key, value, comment;
+                LineType lt = ParseLine(line,section_key,section_key,value,comment);
+                if (lt == EMPTY)
+                {
+                    pcomment.clear();
+                    continue;
+                }
+                else if (lt == ERROR)
+                {
+                    _result.Set(INI_ERR_PARSING_ERROR,lnc,line);
+                    return 0;
+                }
+                // Handle inclusion
+                else if (lt == COMMENT && comment.substr(0,strlen(INI_INCLUDE_SEQ)) == INI_INCLUDE_SEQ)
+                {
+                    std::string incname = comment.substr(strlen(INI_INCLUDE_SEQ));
+                    trim(incname);
+                    normalize_path(incname);
+                    // try to open file
+                    std::string fpath;
+                    if (path_is_relative(incname) && !rpath.empty())
+                        fpath = rpath + SYSTEM_PATH_DELIM + incname;
+                    else
+                        fpath = incname;
+                    std::ifstream file(fpath.c_str(), std::ios::in);
+                    std::string prevfn = _result.file_name;
+                    _result.file_name = fpath;
+                    if (!file.is_open())
+                    {
+                        _result.Set(INI_ERR_INVALID_FILENAME,lnc,line);
+                        return 0;
+                    }
+                    std::string scname; 
+                    if (cur_sect)
+                        scname = cur_sect->FullName();
+                    else
+                        scname = def_section;
+                    if (!ParseStream(file,scname,file_path(fpath),pmap))
+                        return 0;
+                    _result.file_name = prevfn;
+                    continue;
+                }
+                // Add comment (it can be set with any string type, other than EMPTY and ERROR)
+                pcomment += comment;
+                // Add section (or modify comment of existing one if needed)
+                if (lt == SECTION)
+                {
+                    SectionMap::iterator it = pmap.find(section_key);
+                    if (it == pmap.end())
+                    {
+                        cur_sect = new Section(this,section_key,pcomment);
+                        pmap.insert(SectionPair(section_key, cur_sect));
+                    }
+                    else
+                    {
+                        cur_sect = it->second;
+                        if (!pcomment.empty())
+                        {
+                            if (!it->second->_comment.empty())
+                                it->second->_comment += stream.widen('\n') + pcomment;
+                            else
+                                it->second->_comment = pcomment;
+                        }
+                    }
+                    pcomment.clear();
+                }
+                else if (lt == ENTRY)
+                {
+                    // Try to create default section if it is not already in the array
+                    if (!cur_sect)
+                    {
+                        cur_sect = new Section(this,def_section);
+                        pmap.insert(SectionPair(def_section, cur_sect));
+                    }
+                    cur_sect->SetValue(section_key,value,pcomment);
+                    pcomment.clear();
+                }
+            }
+            // Clears eof flag for future usage of stream
+            stream.clear();
+            return 1;
+        }
+        /// Save provided section map to stream
+        void SaveStream(std::ostream& stream, const SectionMap& pmap) const
+        {
+            for (SectionMap::const_iterator it = pmap.begin(); it != pmap.end(); ++it)
+            {
+                if (it->second->ValuesSize() == 0)
+                    continue;
+                if (!it->second->Comment().empty())
+                    AddCommentToStream(stream,it->second->Comment());
+                if (!it->first.empty())
+                    stream << INI_SECTION_OPEN_CHARS[0] << it->first << INI_SECTION_CLOSE_CHARS[0] << std::endl;
+                for (Section::values_iter vit = it->second->ValuesBegin(); vit != it->second->ValuesEnd(); vit++)
+                {
+                    stream << vit->first << " " << INI_NAME_VALUE_SEP_CHARS[0] << " " << vit->second.AsString();
+                    std::string cmn = it->second->GetComment(vit->first);
+                    if (!cmn.empty())
+                        stream << " " << INI_COMMENT_CHARS[0] << cmn;
+                    stream << std::endl;
+                }
+                stream << std::endl;
+            }
+        }
+    private:
+        void DeleteSections(SectionMap& mp)
+        {
+            for (SectionMap::iterator it = mp.begin(); it != mp.end(); ++it)
+                delete(it->second);
+            mp.clear();
+        }
+        // All sections (including subsections) in one map
+        SectionMap        _sections;
+        PResult       _result;
+    };
+
+    /*-----------------------------------------------------------------------------------------------------------/
+    / Some functions left unimplemented
+    /-----------------------------------------------------------------------------------------------------------*/
+    inline Section* Section::GetParent() {return _file->GetParentSection(this);}
+    inline Section* Section::FindParent() const {return _file->FindParentSection(this);}
+    inline Section* Section::GetSubSection(const std::string& name) {return _file->GetSubSection(this,name);}
+    inline Section* Section::FindSubSection(const std::string& name) const {return _file->FindSubSection(this,name);}
+    inline SectionVector Section::FindSubSections() const {return _file->FindSubSections(this);}
+    inline void Section::Save(std::ostream& stream) const {return _file->Save(stream,this);}
+}
+/*---------------------------------------------------------------------------------------------------------------/
+/ Stream operators
+/---------------------------------------------------------------------------------------------------------------*/
+static inline std::ostream& operator<< (std::ostream& stream, const INI::File& file)
+{
+    file.Save(stream);
+    return stream;
+}
+static inline std::ostream& operator<< (std::ostream& stream, const INI::Section* sect)
+{
+    sect->Save(stream);
+    return stream;
+}
+static inline std::ostream& operator<< (std::ostream& stream, const INI::Array& ar)
+{
+    stream << ar.ToValue().AsString();
+    return stream;
+}
+static inline std::istream& operator>> (std::istream& stream, INI::File& file)
+{
+    file.Load(stream,false);
+    return stream;
+}
+
+/*---------------------------------------------------------------------------------------------------------------/
+/ Little tidying up
+/---------------------------------------------------------------------------------------------------------------*/
+#undef INI_SECTION_OPEN_CHARS
+#undef INI_SECTION_CLOSE_CHARS
+#undef INI_COMMENT_CHARS
+#undef INI_NAME_VALUE_SEP_CHARS
+#undef INI_MULTILINE_CHARS
+#undef INI_ARRAY_DELIMITER
+#undef INI_ARRAY_SEGMENT_OPEN
+#undef INI_ARRAY_SEGMENT_CLOSE
+#undef INI_ESCAPE_CHARACTER
+#undef INI_MAP_KEY_VAL_DELIMETER
+#undef INI_SUBSECTION_DELIMETER
+#undef INI_SECTION_VALUE_DELIMETER
+#undef INI_INCLUDE_SEQ
+#undef SYSTEM_PATH_DELIM
+// Note: error definitions are left
+
+#endif
diff --git a/src/TNL/Config/parseCommandLine.h b/src/TNL/Config/parseCommandLine.h
index 8993de027b2d4149112a4d74098478d3bdee3268..afaf19ecc4ca5b1f33c6a197311a730da8cae52f 100644
--- a/src/TNL/Config/parseCommandLine.h
+++ b/src/TNL/Config/parseCommandLine.h
@@ -2,26 +2,220 @@
                           parseCommandLine.h  -  description
                              -------------------
     begin                : 2007/06/15
-    copyright            : (C) 2007 by Tomas Oberhuber
+    copyright            : (C) 2007 by Tomas Oberhuber et al.
     email                : tomas.oberhuber@fjfi.cvut.cz
  ***************************************************************************/
 
 /* See Copyright Notice in tnl/Copyright */
 
+// Implemented by: Tomáš Oberhuber, Jakub Klinkovský
+
 #pragma once
 
-#include <cstring>
 #include <string>
+#include <sstream>
+#include <iomanip>
 
 #include <TNL/Config/ConfigDescription.h>
 #include <TNL/Config/ParameterContainer.h>
 
 namespace TNL {
+namespace Config {
+
+/**
+ * \brief Fills in the parameters from the \e parameters.
+ *
+ * Parameters which were not defined in the command line by user but have their default value are added to the congiguration description.
+ * If there is missing entry with defined default value in the Config::ParameterContainer it is going to be added.
+ * \param parameters Name of the ParameterContainer object.
+ */
+inline void
+addDefaultValues( const ConfigDescription& config, ParameterContainer& parameters )
+{
+   for( const auto& entryBase : config )
+   {
+      const std::string entry_name = entryBase->getName();
+      if( entryBase->hasDefaultValue() && ! parameters.checkParameter( entry_name ) )
+      {
+         if( entryBase->getUIEntryType() == "bool" ) {
+            ConfigEntry< bool >& entry = dynamic_cast< ConfigEntry< bool >& >( *entryBase );
+            parameters.addParameter< bool >( entry_name, entry.getDefaultValue() );
+            continue;
+         }
+         else if( entryBase->getUIEntryType() == "integer" ) {
+            ConfigEntry< Integer >& entry = dynamic_cast< ConfigEntry< Integer >& >( *entryBase );
+            parameters.addParameter< Integer >( entry_name, entry.getDefaultValue() );
+            continue;
+         }
+         else if( entryBase->getUIEntryType() == "unsigned integer" ) {
+            ConfigEntry< UnsignedInteger >& entry = dynamic_cast< ConfigEntry< UnsignedInteger >& >( *entryBase );
+            parameters.addParameter< UnsignedInteger >( entry_name, entry.getDefaultValue() );
+            continue;
+         }
+         else if( entryBase->getUIEntryType() == "real" ) {
+            ConfigEntry< double >& entry = dynamic_cast< ConfigEntry< double >& >( *entryBase );
+            parameters.addParameter< double >( entry_name, entry.getDefaultValue() );
+            continue;
+         }
+         else if( entryBase->getUIEntryType() == "string" ) {
+            ConfigEntry< std::string >& entry = dynamic_cast< ConfigEntry< std::string >& >( *entryBase );
+            parameters.addParameter< std::string >( entry_name, entry.getDefaultValue() );
+            continue;
+         }
+         else if( entryBase->getUIEntryType() == "list of bool" ) {
+            ConfigEntryList< bool >& entry = dynamic_cast< ConfigEntryList< bool >& >( *entryBase );
+            parameters.addList< bool >( entry_name, entry.getDefaultValue() );
+            continue;
+         }
+         else if( entryBase->getUIEntryType() == "list of integer" ) {
+            ConfigEntryList< Integer >& entry = dynamic_cast< ConfigEntryList< Integer >& >( *entryBase );
+            parameters.addList< Integer >( entry_name, entry.getDefaultValue() );
+            continue;
+         }
+         else if( entryBase->getUIEntryType() == "list of unsigned integer" ) {
+            ConfigEntryList< UnsignedInteger >& entry = dynamic_cast< ConfigEntryList< UnsignedInteger >& >( *entryBase );
+            parameters.addList< UnsignedInteger >( entry_name, entry.getDefaultValue() );
+            continue;
+         }
+         else if( entryBase->getUIEntryType() == "list of real" ) {
+            ConfigEntryList< double >& entry = dynamic_cast< ConfigEntryList< double >& >( *entryBase );
+            parameters.addList< double >( entry_name, entry.getDefaultValue() );
+            continue;
+         }
+         else if( entryBase->getUIEntryType() == "list of string" ) {
+            ConfigEntryList< std::string >& entry = dynamic_cast< ConfigEntryList< std::string >& >( *entryBase );
+            parameters.addList< std::string >( entry_name, entry.getDefaultValue() );
+            continue;
+         }
+         else
+            throw std::runtime_error( "Function addDefaultValues encountered unsupported entry type: " +
+                                      entryBase->getUIEntryType() );
+      }
+   }
+}
 
-std::vector< String >
-parseObjectType( const String& objectType );
+/**
+ * \brief Prints configuration description with the \e program_name at the top.
+ *
+ * \param program_name Name of the program
+ */
+inline void
+printUsage( const ConfigDescription& config, const char* program_name )
+{
+   std::cout << "Usage of: " << program_name << std::endl << std::endl;
+   std::size_t max_name_length = 0;
+   std::size_t max_type_length = 0;
 
-namespace Config {
+   for( const auto& entry : config ) {
+      if( ! entry->isDelimiter() ) {
+         max_name_length = std::max( max_name_length, entry->getName().length() );
+         max_type_length = std::max( max_type_length, entry->getUIEntryType().length() );
+      }
+   }
+   max_name_length += 2; // this is for "--"
+
+   for( const auto& entry : config ) {
+      if( entry->isDelimiter() )
+      {
+         std::cout << std::endl;
+         std::cout << entry->getDescription();
+         std::cout << std::endl << std::endl;
+      }
+      else
+      {
+         std::cout << std::setw( max_name_length + 3 ) << "--" << entry->getName()
+                   << std::setw( max_type_length + 5 ) << entry->getUIEntryType()
+                   << "    " << entry->getDescription();
+         if( entry->isRequired() )
+            std::cout << " *** REQUIRED ***";
+         if( entry->hasEnumValues() )
+         {
+            std::cout << std::endl
+                 << std::setw( max_name_length + 3 ) << ""
+                 << std::setw( max_type_length + 5 ) << ""
+                 << "    ";
+            entry->printEnumValues( std::cout );
+         }
+         if( entry->hasDefaultValue() )
+         {
+            std::cout << std::endl
+                 << std::setw( max_name_length + 3 ) << ""
+                 << std::setw( max_type_length + 5 ) << ""
+                 << "    ";
+            std::cout << "- Default value is: " << entry->printDefaultValue();
+         }
+         std::cout << std::endl;
+      }
+   }
+   std::cout << std::endl;
+}
+
+//! Check for all entries with the flag 'required'.
+/*! Returns false if any parameter is missing.
+ */
+inline bool
+checkMissingEntries( const ConfigDescription& config,
+                     Config::ParameterContainer& parameters,
+                     bool printUsage,
+                     const char* programName )
+{
+   std::vector< std::string > missingParameters;
+   for( const auto& entryBase : config ) {
+      const std::string entry_name = entryBase->getName();
+      if( entryBase->isRequired() && ! parameters.checkParameter( entry_name ) )
+          missingParameters.push_back( entry_name );
+   }
+   if( missingParameters.size() > 0 ) {
+      std::cerr << "Some mandatory parameters are misssing. They are listed at the end." << std::endl;
+      if( printUsage )
+         Config::printUsage( config, programName );
+      std::cerr << "Add the following missing parameters to the command line:" << std::endl << "   ";
+      for( int i = 0; i < (int) missingParameters.size(); i++ )
+         std::cerr << "--" << missingParameters[ i ] << " ... ";
+      std::cerr << std::endl;
+      return false;
+   }
+   return true;
+}
+
+template< typename EntryType, typename DefaultValueType >
+void
+checkEnumValues( const ConfigEntry< EntryType, DefaultValueType >& entry, const std::string& name, const EntryType& value )
+{
+   if( entry.getEnumValues().size() > 0 ) {
+      for( auto enumValue : entry.getEnumValues() )
+         if( value == enumValue )
+            return;
+      std::stringstream str;
+      str << "The value " << value << " is not allowed for the config entry " << name << ".\n";
+      entry.printEnumValues( str );
+      throw Exceptions::ConfigError( str.str() );
+   }
+}
+
+template< typename EntryType, typename DefaultValueType >
+void
+checkEnumValues( const ConfigEntry< EntryType, DefaultValueType >& entry, const std::string& name, const std::vector< EntryType >& values )
+{
+   if( entry.getEnumValues().size() > 0 ) {
+      for( auto value : values )
+         checkEnumValues( entry, name, value );
+   }
+}
+
+template< typename T >
+T
+convertStringValue( const String& value, const String& param )
+{
+   T v;
+   std::stringstream str;
+   str << value;
+   str >> v;
+   if( str.fail() )
+      throw Exceptions::ConfigError( "Value '" + value + "' could not be converted to type " + TNL::getType<T>().getString() +
+                                     " as required for the parameter " + param + "." );
+   return v;
+}
 
 inline bool
 parseCommandLine( int argc, char* argv[],
@@ -39,158 +233,138 @@ parseCommandLine( int argc, char* argv[],
       return true;
    };
 
-   auto matob = [iequals]( const char* value ) -> int
+   auto matob = [iequals]( const std::string& value, const std::string& option ) -> int
    {
       if( iequals( value, "yes" ) || iequals( value, "true" ) )
          return true;
       if( iequals( value, "no" ) || iequals( value, "false" ) )
          return false;
-      return -1;
+      throw Exceptions::ConfigError( "Yes/true or no/false is required for the parameter " + option + "." );
    };
 
    int i;
-   bool parse_error( false );
-   for( i = 1; i < argc; i++ )
-   {
-      const char* _option = argv[ i ];
-      if( _option[ 0 ] != '-' )
-      {
-         std::cerr << "Unknown option " << _option << ". Options must have prefix '--' or '-'." << std::endl;
-         parse_error = true;
-         continue;
-      }
-      if( strcmp( _option, "--help" ) == 0 )
-      {
-          config_description.printUsage( argv[ 0 ] );
-          return true;
-      }
-      const char* option = _option + 2;
-      const ConfigEntryBase* entry;
-      if( ( entry = config_description.getEntry( option ) ) == NULL )
+   try {
+      for( i = 1; i < argc; i++ )
       {
-         std::cerr << "Unknown parameter " << _option << "." << std::endl;
-         parse_error = true;
-      }
-      else
-      {
-         const String& entryType = entry->getEntryType();
-         const char* value = argv[ ++i ];
-         if( ! value )
-         {
-            std::cerr << "Missing value for the parameter " << option << "." << std::endl;
+         const std::string _option = argv[ i ];
+         if( _option.length() < 3 || _option[ 0 ] != '-' || _option[ 1 ] != '-' )
+            throw Exceptions::ConfigError( "Unknown option " + _option + ". Options must be prefixed with \"--\"." );
+         if( _option == "--help" ) {
+            Config::printUsage( config_description, argv[ 0 ] );
+            // FIXME: false here indicates that the program should terminate, but the exit code should indicate success instead of failure
             return false;
          }
-         std::vector< String > parsedEntryType = parseObjectType( entryType );
+         const std::string option = _option.substr(2);
+         const ConfigEntryBase* entryBase = config_description.getEntry( option );
+         if( entryBase == NULL )
+            throw Exceptions::ConfigError( "Unknown parameter " + _option + "." );
+         const String entryType = entryBase->getUIEntryType();
+         if( i == argc - 1 )
+            throw Exceptions::ConfigError( "Missing value for the parameter " + option + "." );
+         const std::string value = argv[ ++i ];
+         if( value.empty() )
+            throw Exceptions::ConfigError( "Missing value for the parameter " + option + "." );
+         const std::vector< String > parsedEntryType = entryType.split();
          if( parsedEntryType.size() == 0 )
+            throw Exceptions::ConfigError( "Internal error: Unknown config entry type " + entryType + "." );
+         if( parsedEntryType[ 0 ] == "list" )
          {
-            std::cerr << "Internal error: Unknown config entry type " << entryType << "." << std::endl;
-            return false;
-         }
-         if( parsedEntryType[ 0 ] == "ConfigEntryList" )
-         {
-            std::vector< String > string_list;
             std::vector< bool > bool_list;
-            std::vector< int > integer_list;
+            std::vector< Integer > integer_list;
+            std::vector< UnsignedInteger > unsigned_integer_list;
             std::vector< double > real_list;
+            std::vector< std::string > string_list;
 
-            while( i < argc && ( ( argv[ i ] )[ 0 ] != '-' || ( atof( argv[ i ] ) < 0.0 && ( parsedEntryType[ 1 ] == "int" || parsedEntryType[ 1 ] == "double" ) ) ) )
+            while( i < argc && std::string( argv[ i ] ).substr(0, 2) != "--" )
             {
-               const char* value = argv[ i ++ ];
-               if( parsedEntryType[ 1 ] == "TNL::String" )
-               {
-                  string_list.push_back( String( value ) );
+               const std::string value = argv[ i++ ];
+               if( entryType == "list of bool" ) {
+                  const bool v = matob( value, option );
+                  bool_list.push_back( v );
+               }
+               else if( entryType == "list of integer" ) {
+                  const Integer v = convertStringValue< Integer >( value, option );
+                  const ConfigEntryList< Integer >& entry = dynamic_cast< const ConfigEntryList< Integer >& >( *entryBase );
+                  checkEnumValues( entry, option, v );
+                  integer_list.push_back( v );
                }
-               else if( parsedEntryType[ 1 ] == "bool" )
-               {
-                  const int v = matob( value );
-                  if( v == -1 )
-                  {
-                     std::cerr << "Yes/true or no/false is required for the parameter " << option << "." << std::endl;
-                     parse_error = true;
-                  }
-                  else bool_list.push_back( v );
+               else if( entryType == "list of unsigned integer" ) {
+                  const UnsignedInteger v = convertStringValue< UnsignedInteger >( value, option );
+                  const ConfigEntryList< UnsignedInteger >& entry = dynamic_cast< const ConfigEntryList< UnsignedInteger >& >( *entryBase );
+                  checkEnumValues( entry, option, v );
+                  unsigned_integer_list.push_back( v );
                }
-               else if( parsedEntryType[ 1 ] == "int" )
-               {
-                  integer_list.push_back( atoi( value ) );
+               else if( entryType == "list of real" ) {
+                  const double v = convertStringValue< double >( value, option );
+                  const ConfigEntryList< double >& entry = dynamic_cast< const ConfigEntryList< double >& >( *entryBase );
+                  checkEnumValues( entry, option, v );
+                  real_list.push_back( v );
                }
-               else if( parsedEntryType[ 1 ] == "double" )
-               {
-                  real_list.push_back( atof( value ) );
+               else if( entryType == "list of string" ) {
+                  const ConfigEntryList< std::string >& entry = dynamic_cast< const ConfigEntryList< std::string >& >( *entryBase );
+                  checkEnumValues( entry, option, value );
+                  string_list.push_back( value );
                }
                else
-               {
                   // this will not happen if all entry types are handled above
                   throw std::runtime_error( "Function parseCommandLine encountered unsupported entry type: " + entryType );
-               }
             }
-            if( string_list.size() )
-               parameters.addParameter< std::vector< String > >( option, string_list );
             if( bool_list.size() )
                parameters.addParameter< std::vector< bool > >( option, bool_list );
             if( integer_list.size() )
-               parameters.addParameter< std::vector< int > >( option, integer_list );
+               parameters.addParameter< std::vector< Integer > >( option, integer_list );
+            if( unsigned_integer_list.size() )
+               parameters.addParameter< std::vector< UnsignedInteger > >( option, unsigned_integer_list );
             if( real_list.size() )
                parameters.addParameter< std::vector< double > >( option, real_list );
-            if( i < argc ) i --;
-            continue;
+            if( string_list.size() )
+               parameters.addParameter< std::vector< std::string > >( option, string_list );
          }
          else
          {
-            if( parsedEntryType[ 0 ] == "TNL::String" )
-            {
-               if( ! ( ( ConfigEntry< String >* ) entry )->checkValue( value ) )
-                  return false;
-                parameters.addParameter< String >( option, value );
-                continue;
+            if( entryType == "bool" ) {
+               const bool v = matob( value, option );
+               parameters.addParameter< bool >( option, v );
             }
-            else if( parsedEntryType[ 0 ] == "bool" )
-            {
-               const int v = matob( value );
-               if( v == -1 )
-               {
-                  std::cerr << "Yes/true or no/false is required for the parameter " << option << "." << std::endl;
-                  parse_error = true;
-               }
-               else parameters.addParameter< bool >( option, v );
-               continue;
+            else if( entryType == "integer" ) {
+               const Integer v = convertStringValue< Integer >( value, option );
+               const ConfigEntry< Integer >& entry = dynamic_cast< const ConfigEntry< Integer >& >( *entryBase );
+               checkEnumValues( entry, option, v );
+               parameters.addParameter< Integer >( option, v );
             }
-            else if( parsedEntryType[ 0 ] == "int" )
-            {
-               /*if( ! std::isdigit( value ) ) //TODO: Check for real number
-               {
-                  std::cerr << "Integer constant is required for the parameter " << option << "." << std::endl;
-                  parse_error = true;
-                  continue;
-               }*/
-               if( ! ( ( ConfigEntry< int >* ) entry )->checkValue( atoi( value ) ) )
-                  return false;
-               parameters.addParameter< int >( option, atoi( value ) );
+            else if( entryType == "unsigned integer" ) {
+               const UnsignedInteger v = convertStringValue< UnsignedInteger >( value, option );
+               const ConfigEntry< UnsignedInteger >& entry = dynamic_cast< const ConfigEntry< UnsignedInteger >& >( *entryBase );
+               checkEnumValues( entry, option, v );
+               parameters.addParameter< UnsignedInteger >( option, v );
             }
-            else if( parsedEntryType[ 0 ] == "double" )
-            {
-               /*if( ! std::isdigit( value ) )  //TODO: Check for real number
-               {
-                  std::cerr << "Real constant is required for the parameter " << option << "." << std::endl;
-                  parse_error = true;
-                  continue;
-               }*/
-               if( ! ( ( ConfigEntry< double >* ) entry )->checkValue( atof( value ) ) )
-                  return false;
-               parameters.addParameter< double >( option, atof( value ) );
+            else if( entryType == "real" ) {
+               const double v = convertStringValue< double >( value, option );
+               const ConfigEntry< double >& entry = dynamic_cast< const ConfigEntry< double >& >( *entryBase );
+               checkEnumValues( entry, option, v );
+               parameters.addParameter< double >( option, v );
+            }
+            else if( entryType == "string" ) {
+               const ConfigEntry< std::string >& entry = dynamic_cast< const ConfigEntry< std::string >& >( *entryBase );
+               checkEnumValues( entry, option, (std::string) value );
+               parameters.addParameter< std::string >( option, value );
             }
             else
-            {
                // this will not happen if all entry types are handled above
                throw std::runtime_error( "Function parseCommandLine encountered unsupported entry type: " + entryType );
-            }
          }
       }
    }
-   config_description.addMissingEntries( parameters );
-   if( ! config_description.checkMissingEntries( parameters, printUsage, argv[ 0 ] ) )
+   catch ( const Exceptions::ConfigError& e ) {
+      std::cerr << argv[ 0 ] << ": failed to parse the command line due to the following error:\n" << e.what() << std::endl;
+      if( printUsage )
+         Config::printUsage( config_description, argv[ 0 ] );
+      return false;
+   }
+   addDefaultValues( config_description, parameters );
+   if( ! checkMissingEntries( config_description, parameters, printUsage, argv[ 0 ] ) )
       return false;
-   return ! parse_error;
+   return true;
 }
 
 } // namespace Config
diff --git a/src/TNL/Config/parseINIConfigFile.h b/src/TNL/Config/parseINIConfigFile.h
new file mode 100644
index 0000000000000000000000000000000000000000..f03e63f4f88918d68fc372853045b449578f88df
--- /dev/null
+++ b/src/TNL/Config/parseINIConfigFile.h
@@ -0,0 +1,129 @@
+/***************************************************************************
+                          parseINIConfigFile.h  -  description
+                             -------------------
+    begin                : Feb 1, 2020
+    copyright            : (C) 2020 by Tomas Oberhuber et al.
+    email                : tomas.oberhuber@fjfi.cvut.cz
+ ***************************************************************************/
+
+/* See Copyright Notice in tnl/Copyright */
+
+// Implemented by: Jakub KlinkovskĂ˝
+
+#pragma once
+
+#include <set>
+
+#include <TNL/Config/ConfigDescription.h>
+#include <TNL/Config/ParameterContainer.h>
+#include <TNL/Config/parseCommandLine.h>    // for addDefaultValues and checkEnumValues
+
+#include "iniparser.hpp"
+
+namespace TNL {
+namespace Config {
+
+ParameterContainer
+parseINIConfigFile( const std::string& configPath,
+                    const ConfigDescription& description )
+{
+    ParameterContainer parameters;
+
+    INI::File file;
+    if( ! file.Load( configPath ) )
+        throw std::runtime_error( file.LastResult().GetErrorDesc() );
+
+    // aggregated sets
+    std::set< std::string > undefinedOptions;
+
+    // iterate over all entries in the global section
+    const INI::Section* section = file.GetSection( "" );
+    for( auto e = section->ValuesBegin(); e != section->ValuesEnd(); e++ ) {
+        const std::string name = e->first;
+        const auto* entryBase = description.getEntry( name );
+        if( entryBase == nullptr ) {
+            undefinedOptions.insert( name );
+            continue;
+        }
+        const std::string entryType = entryBase->getUIEntryType();
+        const auto value = e->second;
+        if( value.AsString().empty() )
+            throw std::runtime_error( "Missing value for the parameter " + name + "." );
+
+        if( entryType == "bool" ) {
+            parameters.addParameter< bool >( name, value.AsBool() );
+            continue;
+        }
+        else if( entryType == "integer" ) {
+            const auto v = value.AsT< Integer >();
+            const ConfigEntry< Integer >& entry = dynamic_cast< const ConfigEntry< Integer >& >( *entryBase );
+            checkEnumValues( entry, name, v );
+            parameters.addParameter< Integer >( name, v );
+        }
+        else if( entryType == "unsigned integer" ) {
+            const auto v = value.AsT< UnsignedInteger >();
+            const ConfigEntry< UnsignedInteger >& entry = dynamic_cast< const ConfigEntry< UnsignedInteger >& >( *entryBase );
+            checkEnumValues( entry, name, v );
+            parameters.addParameter< UnsignedInteger >( name, v );
+        }
+        else if( entryType == "real" ) {
+            const auto v = value.AsDouble();
+            const ConfigEntry< double >& entry = dynamic_cast< const ConfigEntry< double >& >( *entryBase );
+            checkEnumValues( entry, name, v );
+            parameters.addParameter< double >( name, v );
+        }
+        else if( entryType == "string" ) {
+            const auto v = value.AsString();
+            const ConfigEntry< std::string >& entry = dynamic_cast< const ConfigEntry< std::string >& >( *entryBase );
+            checkEnumValues( entry, name, v );
+            parameters.addParameter< std::string >( name, v );
+            continue;
+        }
+        else if( entryType == "list of bool" ) {
+            const auto list = value.AsArray().ToVector< bool >();
+            parameters.addList< bool >( name, list );
+        }
+        else if( entryType == "list of integer" ) {
+            const auto list = value.AsArray().ToVector< Integer >();
+            const ConfigEntry< Integer >& entry = dynamic_cast< const ConfigEntry< Integer >& >( *entryBase );
+            checkEnumValues( entry, name, list );
+            parameters.addList< Integer >( name, list );
+        }
+        else if( entryType == "list of unsigned integer" ) {
+            const auto list = value.AsArray().ToVector< UnsignedInteger >();
+            const ConfigEntry< UnsignedInteger >& entry = dynamic_cast< const ConfigEntry< UnsignedInteger >& >( *entryBase );
+            checkEnumValues( entry, name, list );
+            parameters.addList< UnsignedInteger >( name, list );
+        }
+        else if( entryType == "list of real" ) {
+            const auto list = value.AsArray().ToVector< double >();
+            const ConfigEntry< double >& entry = dynamic_cast< const ConfigEntry< double >& >( *entryBase );
+            checkEnumValues( entry, name, list );
+            parameters.addList< double >( name, list );
+        }
+        else if( entryType == "list of string" ) {
+            const auto list = value.AsArray().ToVector< std::string >();
+            const ConfigEntry< std::string >& entry = dynamic_cast< const ConfigEntry< std::string >& >( *entryBase );
+            checkEnumValues( entry, name, list );
+            parameters.addList< std::string >( name, list );
+        }
+        else
+            // this will not happen if all entry types are handled above
+            throw std::runtime_error( "Function parseINIConfigFil encountered unsupported entry type: " + entryType );
+    }
+
+    if( ! undefinedOptions.empty() ) {
+        std::string msg = "The configuration file contains the following options which are not defined in the program:\n";
+        for( auto option : undefinedOptions )
+            msg += " - " + option + "\n";
+        throw std::runtime_error( msg );
+    }
+
+    // add default values to the parameters
+    addDefaultValues( description, parameters );
+
+    return parameters;
+}
+
+} // namespace Config
+} // namespace TNL
diff --git a/src/TNL/Containers/StaticVector.hpp b/src/TNL/Containers/StaticVector.hpp
index 4a1b41b2198d114bfe31fed16b3d04f37cf3ad5d..bbcf8a09f5f88edc202438cb9e323bd227e6f813 100644
--- a/src/TNL/Containers/StaticVector.hpp
+++ b/src/TNL/Containers/StaticVector.hpp
@@ -42,12 +42,7 @@ StaticVector< Size, Real >::setup( const Config::ParameterContainer& parameters,
                                    const String& prefix )
 {
    for( int i = 0; i < Size; i++ )
-   {
-      double aux;
-      if( ! parameters.template getParameter< double >( prefix + convertToString( i ), aux ) )
-         return false;
-      this->data[ i ] = aux;
-   }
+      this->data[ i ] = parameters.template getParameter< double >( prefix + convertToString( i ) );
    return true;
 }
 
diff --git a/src/TNL/Exceptions/ConfigError.h b/src/TNL/Exceptions/ConfigError.h
new file mode 100644
index 0000000000000000000000000000000000000000..275d58cfc25eb517c9ed350ca47bb00c8812cf9d
--- /dev/null
+++ b/src/TNL/Exceptions/ConfigError.h
@@ -0,0 +1,29 @@
+/***************************************************************************
+                          ConfigError.h  -  description
+                             -------------------
+    begin                : Apr 12, 2019
+    copyright            : (C) 2019 by Tomas Oberhuber et al.
+    email                : tomas.oberhuber@fjfi.cvut.cz
+ ***************************************************************************/
+
+/* See Copyright Notice in tnl/Copyright */
+
+// Implemented by: Jakub Klinkovsky
+
+#pragma once
+
+#include <stdexcept>
+
+namespace TNL {
+namespace Exceptions {
+
+struct ConfigError
+   : public std::runtime_error
+{
+   ConfigError( std::string msg )
+   : std::runtime_error( msg )
+   {}
+};
+
+} // namespace Exceptions
+} // namespace TNL
diff --git a/src/TNL/Solvers/Solver_impl.h b/src/TNL/Solvers/Solver_impl.h
index ef865e8b7b25b6ce426c1b0d3c2918187058918d..9182c620fe2ed589389ab5a64328917f392795c7 100644
--- a/src/TNL/Solvers/Solver_impl.h
+++ b/src/TNL/Solvers/Solver_impl.h
@@ -13,6 +13,7 @@
 #include <TNL/Solvers/SolverInitiator.h>
 #include <TNL/Solvers/SolverStarter.h>
 #include <TNL/Solvers/SolverConfig.h>
+#include <TNL/Config/parseCommandLine.h>
 #include <TNL/Devices/Cuda.h>
 #include <TNL/Communicators/MpiCommunicator.h>
 #include <TNL/Communicators/ScopedInitializer.h>
diff --git a/src/TNL/variant.hpp b/src/TNL/variant.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c6a6d34b5e15da76ee9f43b0ebef518ed09aec6e
--- /dev/null
+++ b/src/TNL/variant.hpp
@@ -0,0 +1,2815 @@
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_VARIANT_HPP
+#define MPARK_VARIANT_HPP
+
+/*
+   variant synopsis
+
+namespace std {
+
+  // 20.7.2, class template variant
+  template <class... Types>
+  class variant {
+  public:
+
+    // 20.7.2.1, constructors
+    constexpr variant() noexcept(see below);
+    variant(const variant&);
+    variant(variant&&) noexcept(see below);
+
+    template <class T> constexpr variant(T&&) noexcept(see below);
+
+    template <class T, class... Args>
+    constexpr explicit variant(in_place_type_t<T>, Args&&...);
+
+    template <class T, class U, class... Args>
+    constexpr explicit variant(
+        in_place_type_t<T>, initializer_list<U>, Args&&...);
+
+    template <size_t I, class... Args>
+    constexpr explicit variant(in_place_index_t<I>, Args&&...);
+
+    template <size_t I, class U, class... Args>
+    constexpr explicit variant(
+        in_place_index_t<I>, initializer_list<U>, Args&&...);
+
+    // 20.7.2.2, destructor
+    ~variant();
+
+    // 20.7.2.3, assignment
+    variant& operator=(const variant&);
+    variant& operator=(variant&&) noexcept(see below);
+
+    template <class T> variant& operator=(T&&) noexcept(see below);
+
+    // 20.7.2.4, modifiers
+    template <class T, class... Args>
+    T& emplace(Args&&...);
+
+    template <class T, class U, class... Args>
+    T& emplace(initializer_list<U>, Args&&...);
+
+    template <size_t I, class... Args>
+    variant_alternative<I, variant>& emplace(Args&&...);
+
+    template <size_t I, class U, class...  Args>
+    variant_alternative<I, variant>& emplace(initializer_list<U>, Args&&...);
+
+    // 20.7.2.5, value status
+    constexpr bool valueless_by_exception() const noexcept;
+    constexpr size_t index() const noexcept;
+
+    // 20.7.2.6, swap
+    void swap(variant&) noexcept(see below);
+  };
+
+  // 20.7.3, variant helper classes
+  template <class T> struct variant_size; // undefined
+
+  template <class T>
+  constexpr size_t variant_size_v = variant_size<T>::value;
+
+  template <class T> struct variant_size<const T>;
+  template <class T> struct variant_size<volatile T>;
+  template <class T> struct variant_size<const volatile T>;
+
+  template <class... Types>
+  struct variant_size<variant<Types...>>;
+
+  template <size_t I, class T> struct variant_alternative; // undefined
+
+  template <size_t I, class T>
+  using variant_alternative_t = typename variant_alternative<I, T>::type;
+
+  template <size_t I, class T> struct variant_alternative<I, const T>;
+  template <size_t I, class T> struct variant_alternative<I, volatile T>;
+  template <size_t I, class T> struct variant_alternative<I, const volatile T>;
+
+  template <size_t I, class... Types>
+  struct variant_alternative<I, variant<Types...>>;
+
+  constexpr size_t variant_npos = -1;
+
+  // 20.7.4, value access
+  template <class T, class... Types>
+  constexpr bool holds_alternative(const variant<Types...>&) noexcept;
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>>&
+  get(variant<Types...>&);
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>>&&
+  get(variant<Types...>&&);
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>> const&
+  get(const variant<Types...>&);
+
+  template <size_t I, class... Types>
+  constexpr variant_alternative_t<I, variant<Types...>> const&&
+  get(const variant<Types...>&&);
+
+  template <class T, class...  Types>
+  constexpr T& get(variant<Types...>&);
+
+  template <class T, class... Types>
+  constexpr T&& get(variant<Types...>&&);
+
+  template <class T, class... Types>
+  constexpr const T& get(const variant<Types...>&);
+
+  template <class T, class... Types>
+  constexpr const T&& get(const variant<Types...>&&);
+
+  template <size_t I, class... Types>
+  constexpr add_pointer_t<variant_alternative_t<I, variant<Types...>>>
+  get_if(variant<Types...>*) noexcept;
+
+  template <size_t I, class... Types>
+  constexpr add_pointer_t<const variant_alternative_t<I, variant<Types...>>>
+  get_if(const variant<Types...>*) noexcept;
+
+  template <class T, class... Types>
+  constexpr add_pointer_t<T>
+  get_if(variant<Types...>*) noexcept;
+
+  template <class T, class... Types>
+  constexpr add_pointer_t<const T>
+  get_if(const variant<Types...>*) noexcept;
+
+  // 20.7.5, relational operators
+  template <class... Types>
+  constexpr bool operator==(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator!=(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator<(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator>(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator<=(const variant<Types...>&, const variant<Types...>&);
+
+  template <class... Types>
+  constexpr bool operator>=(const variant<Types...>&, const variant<Types...>&);
+
+  // 20.7.6, visitation
+  template <class Visitor, class... Variants>
+  constexpr see below visit(Visitor&&, Variants&&...);
+
+  // 20.7.7, class monostate
+  struct monostate;
+
+  // 20.7.8, monostate relational operators
+  constexpr bool operator<(monostate, monostate) noexcept;
+  constexpr bool operator>(monostate, monostate) noexcept;
+  constexpr bool operator<=(monostate, monostate) noexcept;
+  constexpr bool operator>=(monostate, monostate) noexcept;
+  constexpr bool operator==(monostate, monostate) noexcept;
+  constexpr bool operator!=(monostate, monostate) noexcept;
+
+  // 20.7.9, specialized algorithms
+  template <class... Types>
+  void swap(variant<Types...>&, variant<Types...>&) noexcept(see below);
+
+  // 20.7.10, class bad_variant_access
+  class bad_variant_access;
+
+  // 20.7.11, hash support
+  template <class T> struct hash;
+  template <class... Types> struct hash<variant<Types...>>;
+  template <> struct hash<monostate>;
+
+} // namespace std
+
+*/
+
+#include <cstddef>
+#include <exception>
+#include <functional>
+#include <initializer_list>
+#include <new>
+#include <type_traits>
+#include <utility>
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_CONFIG_HPP
+#define MPARK_CONFIG_HPP
+
+// MSVC 2015 Update 3.
+#if __cplusplus < 201103L && (!defined(_MSC_VER) || _MSC_FULL_VER < 190024210)
+#error "MPark.Variant requires C++11 support."
+#endif
+
+#ifndef __has_attribute
+#define __has_attribute(x) 0
+#endif
+
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+
+#ifndef __has_include
+#define __has_include(x) 0
+#endif
+
+#ifndef __has_feature
+#define __has_feature(x) 0
+#endif
+
+#if __has_attribute(always_inline) || defined(__GNUC__)
+#define MPARK_ALWAYS_INLINE __attribute__((__always_inline__)) inline
+#elif defined(_MSC_VER)
+#define MPARK_ALWAYS_INLINE __forceinline
+#else
+#define MPARK_ALWAYS_INLINE inline
+#endif
+
+#if __has_builtin(__builtin_addressof) || \
+    (defined(__GNUC__) && __GNUC__ >= 7) || defined(_MSC_VER)
+#define MPARK_BUILTIN_ADDRESSOF
+#endif
+
+#if __has_builtin(__builtin_unreachable) || defined(__GNUC__)
+#define MPARK_BUILTIN_UNREACHABLE __builtin_unreachable()
+#elif defined(_MSC_VER)
+#define MPARK_BUILTIN_UNREACHABLE __assume(false)
+#else
+#define MPARK_BUILTIN_UNREACHABLE
+#endif
+
+#if __has_builtin(__type_pack_element)
+#define MPARK_TYPE_PACK_ELEMENT
+#endif
+
+#if defined(__cpp_constexpr) && __cpp_constexpr >= 200704 && \
+    !(defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 9)
+#define MPARK_CPP11_CONSTEXPR
+#endif
+
+#if defined(__cpp_constexpr) && __cpp_constexpr >= 201304
+#define MPARK_CPP14_CONSTEXPR
+#endif
+
+#if __has_feature(cxx_exceptions) || defined(__cpp_exceptions) || \
+    (defined(_MSC_VER) && defined(_CPPUNWIND))
+#define MPARK_EXCEPTIONS
+#endif
+
+#if defined(__cpp_generic_lambdas) || defined(_MSC_VER)
+#define MPARK_GENERIC_LAMBDAS
+#endif
+
+#if defined(__cpp_lib_integer_sequence)
+#define MPARK_INTEGER_SEQUENCE
+#endif
+
+#if defined(__cpp_return_type_deduction) || defined(_MSC_VER)
+#define MPARK_RETURN_TYPE_DEDUCTION
+#endif
+
+#if defined(__cpp_lib_transparent_operators) || defined(_MSC_VER)
+#define MPARK_TRANSPARENT_OPERATORS
+#endif
+
+#if defined(__cpp_variable_templates) || defined(_MSC_VER)
+#define MPARK_VARIABLE_TEMPLATES
+#endif
+
+#if !defined(__GLIBCXX__) || __has_include(<codecvt>)  // >= libstdc++-5
+#define MPARK_TRIVIALITY_TYPE_TRAITS
+#define MPARK_INCOMPLETE_TYPE_TRAITS
+#endif
+
+#endif  // MPARK_CONFIG_HPP
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_IN_PLACE_HPP
+#define MPARK_IN_PLACE_HPP
+
+#include <cstddef>
+
+
+namespace mpark {
+
+  struct in_place_t { explicit in_place_t() = default; };
+
+  template <std::size_t I>
+  struct in_place_index_t { explicit in_place_index_t() = default; };
+
+  template <typename T>
+  struct in_place_type_t { explicit in_place_type_t() = default; };
+
+#ifdef MPARK_VARIABLE_TEMPLATES
+  constexpr in_place_t in_place{};
+
+  template <std::size_t I> constexpr in_place_index_t<I> in_place_index{};
+
+  template <typename T> constexpr in_place_type_t<T> in_place_type{};
+#endif
+
+}  // namespace mpark
+
+#endif  // MPARK_IN_PLACE_HPP
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_LIB_HPP
+#define MPARK_LIB_HPP
+
+#include <memory>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+
+#define MPARK_RETURN(...) \
+  noexcept(noexcept(__VA_ARGS__)) -> decltype(__VA_ARGS__) { return __VA_ARGS__; }
+
+namespace mpark {
+  namespace lib {
+    template <typename T>
+    struct identity { using type = T; };
+
+    inline namespace cpp14 {
+      template <typename T, std::size_t N>
+      struct array {
+        constexpr const T &operator[](std::size_t index) const {
+          return data[index];
+        }
+
+        T data[N == 0 ? 1 : N];
+      };
+
+      template <typename T>
+      using add_pointer_t = typename std::add_pointer<T>::type;
+
+      template <typename... Ts>
+      using common_type_t = typename std::common_type<Ts...>::type;
+
+      template <typename T>
+      using decay_t = typename std::decay<T>::type;
+
+      template <bool B, typename T = void>
+      using enable_if_t = typename std::enable_if<B, T>::type;
+
+      template <typename T>
+      using remove_const_t = typename std::remove_const<T>::type;
+
+      template <typename T>
+      using remove_reference_t = typename std::remove_reference<T>::type;
+
+      template <typename T>
+      inline constexpr T &&forward(remove_reference_t<T> &t) noexcept {
+        return static_cast<T &&>(t);
+      }
+
+      template <typename T>
+      inline constexpr T &&forward(remove_reference_t<T> &&t) noexcept {
+        static_assert(!std::is_lvalue_reference<T>::value,
+                      "can not forward an rvalue as an lvalue");
+        return static_cast<T &&>(t);
+      }
+
+      template <typename T>
+      inline constexpr remove_reference_t<T> &&move(T &&t) noexcept {
+        return static_cast<remove_reference_t<T> &&>(t);
+      }
+
+#ifdef MPARK_INTEGER_SEQUENCE
+      using std::integer_sequence;
+      using std::index_sequence;
+      using std::make_index_sequence;
+      using std::index_sequence_for;
+#else
+      template <typename T, T... Is>
+      struct integer_sequence {
+        using value_type = T;
+        static constexpr std::size_t size() noexcept { return sizeof...(Is); }
+      };
+
+      template <std::size_t... Is>
+      using index_sequence = integer_sequence<std::size_t, Is...>;
+
+      template <typename Lhs, typename Rhs>
+      struct make_index_sequence_concat;
+
+      template <std::size_t... Lhs, std::size_t... Rhs>
+      struct make_index_sequence_concat<index_sequence<Lhs...>,
+                                        index_sequence<Rhs...>>
+          : identity<index_sequence<Lhs..., (sizeof...(Lhs) + Rhs)...>> {};
+
+      template <std::size_t N>
+      struct make_index_sequence_impl;
+
+      template <std::size_t N>
+      using make_index_sequence = typename make_index_sequence_impl<N>::type;
+
+      template <std::size_t N>
+      struct make_index_sequence_impl
+          : make_index_sequence_concat<make_index_sequence<N / 2>,
+                                       make_index_sequence<N - (N / 2)>> {};
+
+      template <>
+      struct make_index_sequence_impl<0> : identity<index_sequence<>> {};
+
+      template <>
+      struct make_index_sequence_impl<1> : identity<index_sequence<0>> {};
+
+      template <typename... Ts>
+      using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
+#endif
+
+      // <functional>
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using equal_to = std::equal_to<>;
+#else
+      struct equal_to {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          MPARK_RETURN(lib::forward<Lhs>(lhs) == lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using not_equal_to = std::not_equal_to<>;
+#else
+      struct not_equal_to {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          MPARK_RETURN(lib::forward<Lhs>(lhs) != lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using less = std::less<>;
+#else
+      struct less {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          MPARK_RETURN(lib::forward<Lhs>(lhs) < lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using greater = std::greater<>;
+#else
+      struct greater {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          MPARK_RETURN(lib::forward<Lhs>(lhs) > lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using less_equal = std::less_equal<>;
+#else
+      struct less_equal {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          MPARK_RETURN(lib::forward<Lhs>(lhs) <= lib::forward<Rhs>(rhs))
+      };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+      using greater_equal = std::greater_equal<>;
+#else
+      struct greater_equal {
+        template <typename Lhs, typename Rhs>
+        inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+          MPARK_RETURN(lib::forward<Lhs>(lhs) >= lib::forward<Rhs>(rhs))
+      };
+#endif
+    }  // namespace cpp14
+
+    inline namespace cpp17 {
+
+      // <type_traits>
+      template <bool B>
+      using bool_constant = std::integral_constant<bool, B>;
+
+      template <typename...>
+      struct voider : identity<void> {};
+
+      template <typename... Ts>
+      using void_t = typename voider<Ts...>::type;
+
+      namespace detail {
+        namespace swappable {
+
+          using std::swap;
+
+          template <typename T>
+          struct is_swappable {
+            private:
+            template <typename U,
+                      typename = decltype(swap(std::declval<U &>(),
+                                               std::declval<U &>()))>
+            inline static std::true_type test(int);
+
+            template <typename U>
+            inline static std::false_type test(...);
+
+            public:
+            static constexpr bool value = decltype(test<T>(0))::value;
+          };
+
+          template <bool IsSwappable, typename T>
+          struct is_nothrow_swappable {
+            static constexpr bool value =
+                noexcept(swap(std::declval<T &>(), std::declval<T &>()));
+          };
+
+          template <typename T>
+          struct is_nothrow_swappable<false, T> : std::false_type {};
+
+        }  // namespace swappable
+      }  // namespace detail
+
+      using detail::swappable::is_swappable;
+
+      template <typename T>
+      using is_nothrow_swappable =
+          detail::swappable::is_nothrow_swappable<is_swappable<T>::value, T>;
+
+      // <functional>
+      namespace detail {
+
+        template <typename T>
+        struct is_reference_wrapper : std::false_type {};
+
+        template <typename T>
+        struct is_reference_wrapper<std::reference_wrapper<T>>
+            : std::true_type {};
+
+        template <bool, int>
+        struct Invoke;
+
+        template <>
+        struct Invoke<true /* pmf */, 0 /* is_base_of */> {
+          template <typename R, typename T, typename Arg, typename... Args>
+          inline static constexpr auto invoke(R T::*pmf, Arg &&arg, Args &&... args)
+            MPARK_RETURN((lib::forward<Arg>(arg).*pmf)(lib::forward<Args>(args)...))
+        };
+
+        template <>
+        struct Invoke<true /* pmf */, 1 /* is_reference_wrapper */> {
+          template <typename R, typename T, typename Arg, typename... Args>
+          inline static constexpr auto invoke(R T::*pmf, Arg &&arg, Args &&... args)
+            MPARK_RETURN((lib::forward<Arg>(arg).get().*pmf)(lib::forward<Args>(args)...))
+        };
+
+        template <>
+        struct Invoke<true /* pmf */, 2 /* otherwise */> {
+          template <typename R, typename T, typename Arg, typename... Args>
+          inline static constexpr auto invoke(R T::*pmf, Arg &&arg, Args &&... args)
+            MPARK_RETURN(((*lib::forward<Arg>(arg)).*pmf)(lib::forward<Args>(args)...))
+        };
+
+        template <>
+        struct Invoke<false /* pmo */, 0 /* is_base_of */> {
+          template <typename R, typename T, typename Arg>
+          inline static constexpr auto invoke(R T::*pmo, Arg &&arg)
+            MPARK_RETURN(lib::forward<Arg>(arg).*pmo)
+        };
+
+        template <>
+        struct Invoke<false /* pmo */, 1 /* is_reference_wrapper */> {
+          template <typename R, typename T, typename Arg>
+          inline static constexpr auto invoke(R T::*pmo, Arg &&arg)
+            MPARK_RETURN(lib::forward<Arg>(arg).get().*pmo)
+        };
+
+        template <>
+        struct Invoke<false /* pmo */, 2 /* otherwise */> {
+          template <typename R, typename T, typename Arg>
+          inline static constexpr auto invoke(R T::*pmo, Arg &&arg)
+              MPARK_RETURN((*lib::forward<Arg>(arg)).*pmo)
+        };
+
+        template <typename R, typename T, typename Arg, typename... Args>
+        inline constexpr auto invoke(R T::*f, Arg &&arg, Args &&... args)
+          MPARK_RETURN(
+              Invoke<std::is_function<R>::value,
+                     (std::is_base_of<T, lib::decay_t<Arg>>::value
+                          ? 0
+                          : is_reference_wrapper<lib::decay_t<Arg>>::value
+                                ? 1
+                                : 2)>::invoke(f,
+                                              lib::forward<Arg>(arg),
+                                              lib::forward<Args>(args)...))
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4100)
+#endif
+        template <typename F, typename... Args>
+        inline constexpr auto invoke(F &&f, Args &&... args)
+          MPARK_RETURN(lib::forward<F>(f)(lib::forward<Args>(args)...))
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+      }  // namespace detail
+
+      template <typename F, typename... Args>
+      inline constexpr auto invoke(F &&f, Args &&... args)
+        MPARK_RETURN(detail::invoke(lib::forward<F>(f),
+                                    lib::forward<Args>(args)...))
+
+      namespace detail {
+
+        template <typename Void, typename, typename...>
+        struct invoke_result {};
+
+        template <typename F, typename... Args>
+        struct invoke_result<void_t<decltype(lib::invoke(
+                                 std::declval<F>(), std::declval<Args>()...))>,
+                             F,
+                             Args...>
+            : identity<decltype(
+                  lib::invoke(std::declval<F>(), std::declval<Args>()...))> {};
+
+      }  // namespace detail
+
+      template <typename F, typename... Args>
+      using invoke_result = detail::invoke_result<void, F, Args...>;
+
+      template <typename F, typename... Args>
+      using invoke_result_t = typename invoke_result<F, Args...>::type;
+
+      namespace detail {
+
+        template <typename Void, typename, typename...>
+        struct is_invocable : std::false_type {};
+
+        template <typename F, typename... Args>
+        struct is_invocable<void_t<invoke_result_t<F, Args...>>, F, Args...>
+            : std::true_type {};
+
+        template <typename Void, typename, typename, typename...>
+        struct is_invocable_r : std::false_type {};
+
+        template <typename R, typename F, typename... Args>
+        struct is_invocable_r<void_t<invoke_result_t<F, Args...>>,
+                              R,
+                              F,
+                              Args...>
+            : std::is_convertible<invoke_result_t<F, Args...>, R> {};
+
+      }  // namespace detail
+
+      template <typename F, typename... Args>
+      using is_invocable = detail::is_invocable<void, F, Args...>;
+
+      template <typename R, typename F, typename... Args>
+      using is_invocable_r = detail::is_invocable_r<void, R, F, Args...>;
+
+      namespace detail {
+
+        template <bool Invocable, typename F, typename... Args>
+        struct is_nothrow_invocable {
+          static constexpr bool value =
+              noexcept(lib::invoke(std::declval<F>(), std::declval<Args>()...));
+        };
+
+        template <typename F, typename... Args>
+        struct is_nothrow_invocable<false, F, Args...> : std::false_type {};
+
+        template <bool Invocable, typename R, typename F, typename... Args>
+        struct is_nothrow_invocable_r {
+          private:
+          inline static R impl() {
+            return lib::invoke(std::declval<F>(), std::declval<Args>()...);
+          }
+
+          public:
+          static constexpr bool value = noexcept(impl());
+        };
+
+        template <typename R, typename F, typename... Args>
+        struct is_nothrow_invocable_r<false, R, F, Args...> : std::false_type {};
+
+      }  // namespace detail
+
+      template <typename F, typename... Args>
+      using is_nothrow_invocable = detail::
+          is_nothrow_invocable<is_invocable<F, Args...>::value, F, Args...>;
+
+      template <typename R, typename F, typename... Args>
+      using is_nothrow_invocable_r =
+          detail::is_nothrow_invocable_r<is_invocable_r<R, F, Args...>::value,
+                                         R,
+                                         F,
+                                         Args...>;
+
+      // <memory>
+#ifdef MPARK_BUILTIN_ADDRESSOF
+      template <typename T>
+      inline constexpr T *addressof(T &arg) noexcept {
+        return __builtin_addressof(arg);
+      }
+#else
+      namespace detail {
+
+        namespace has_addressof_impl {
+
+          struct fail;
+
+          template <typename T>
+          inline fail operator&(T &&);
+
+          template <typename T>
+          inline static constexpr bool impl() {
+            return (std::is_class<T>::value || std::is_union<T>::value) &&
+                   !std::is_same<decltype(&std::declval<T &>()), fail>::value;
+          }
+
+        }  // namespace has_addressof_impl
+
+        template <typename T>
+        using has_addressof = bool_constant<has_addressof_impl::impl<T>()>;
+
+        template <typename T>
+        inline constexpr T *addressof(T &arg, std::true_type) noexcept {
+          return std::addressof(arg);
+        }
+
+        template <typename T>
+        inline constexpr T *addressof(T &arg, std::false_type) noexcept {
+          return &arg;
+        }
+
+      }  // namespace detail
+
+      template <typename T>
+      inline constexpr T *addressof(T &arg) noexcept {
+        return detail::addressof(arg, detail::has_addressof<T>{});
+      }
+#endif
+
+      template <typename T>
+      inline constexpr T *addressof(const T &&) = delete;
+
+    }  // namespace cpp17
+
+    template <typename T>
+    struct remove_all_extents : identity<T> {};
+
+    template <typename T, std::size_t N>
+    struct remove_all_extents<array<T, N>> : remove_all_extents<T> {};
+
+    template <typename T>
+    using remove_all_extents_t = typename remove_all_extents<T>::type;
+
+    template <std::size_t N>
+    using size_constant = std::integral_constant<std::size_t, N>;
+
+    template <std::size_t I, typename T>
+    struct indexed_type : size_constant<I> { using type = T; };
+
+    template <bool... Bs>
+    using all = std::is_same<integer_sequence<bool, true, Bs...>,
+                             integer_sequence<bool, Bs..., true>>;
+
+#ifdef MPARK_TYPE_PACK_ELEMENT
+    template <std::size_t I, typename... Ts>
+    using type_pack_element_t = __type_pack_element<I, Ts...>;
+#else
+    template <std::size_t I, typename... Ts>
+    struct type_pack_element_impl {
+      private:
+      template <typename>
+      struct set;
+
+      template <std::size_t... Is>
+      struct set<index_sequence<Is...>> : indexed_type<Is, Ts>... {};
+
+      template <typename T>
+      inline static std::enable_if<true, T> impl(indexed_type<I, T>);
+
+      inline static std::enable_if<false> impl(...);
+
+      public:
+      using type = decltype(impl(set<index_sequence_for<Ts...>>{}));
+    };
+
+    template <std::size_t I, typename... Ts>
+    using type_pack_element = typename type_pack_element_impl<I, Ts...>::type;
+
+    template <std::size_t I, typename... Ts>
+    using type_pack_element_t = typename type_pack_element<I, Ts...>::type;
+#endif
+
+#ifdef MPARK_TRIVIALITY_TYPE_TRAITS
+    using std::is_trivially_copy_constructible;
+    using std::is_trivially_move_constructible;
+    using std::is_trivially_copy_assignable;
+    using std::is_trivially_move_assignable;
+#else
+    template <typename T>
+    struct is_trivially_copy_constructible
+        : bool_constant<
+              std::is_copy_constructible<T>::value && __has_trivial_copy(T)> {};
+
+    template <typename T>
+    struct is_trivially_move_constructible : bool_constant<__is_trivial(T)> {};
+
+    template <typename T>
+    struct is_trivially_copy_assignable
+        : bool_constant<
+              std::is_copy_assignable<T>::value && __has_trivial_assign(T)> {};
+
+    template <typename T>
+    struct is_trivially_move_assignable : bool_constant<__is_trivial(T)> {};
+#endif
+
+    template <typename T, bool>
+    struct dependent_type : T {};
+
+    template <typename Is, std::size_t J>
+    struct push_back;
+
+    template <typename Is, std::size_t J>
+    using push_back_t = typename push_back<Is, J>::type;
+
+    template <std::size_t... Is, std::size_t J>
+    struct push_back<index_sequence<Is...>, J> {
+      using type = index_sequence<Is..., J>;
+    };
+
+  }  // namespace lib
+}  // namespace mpark
+
+#undef MPARK_RETURN
+
+#endif  // MPARK_LIB_HPP
+
+
+namespace mpark {
+
+#ifdef MPARK_RETURN_TYPE_DEDUCTION
+
+#define AUTO auto
+#define AUTO_RETURN(...) { return __VA_ARGS__; }
+
+#define AUTO_REFREF auto &&
+#define AUTO_REFREF_RETURN(...) { return __VA_ARGS__; }
+
+#define DECLTYPE_AUTO decltype(auto)
+#define DECLTYPE_AUTO_RETURN(...) { return __VA_ARGS__; }
+
+#else
+
+#define AUTO auto
+#define AUTO_RETURN(...) \
+  -> lib::decay_t<decltype(__VA_ARGS__)> { return __VA_ARGS__; }
+
+#define AUTO_REFREF auto
+#define AUTO_REFREF_RETURN(...)                                           \
+  -> decltype((__VA_ARGS__)) {                                            \
+    static_assert(std::is_reference<decltype((__VA_ARGS__))>::value, ""); \
+    return __VA_ARGS__;                                                   \
+  }
+
+#define DECLTYPE_AUTO auto
+#define DECLTYPE_AUTO_RETURN(...) \
+  -> decltype(__VA_ARGS__) { return __VA_ARGS__; }
+
+#endif
+
+  class bad_variant_access : public std::exception {
+    public:
+    virtual const char *what() const noexcept override { return "bad_variant_access"; }
+  };
+
+  [[noreturn]] inline void throw_bad_variant_access() {
+#ifdef MPARK_EXCEPTIONS
+    throw bad_variant_access{};
+#else
+    std::terminate();
+    MPARK_BUILTIN_UNREACHABLE;
+#endif
+  }
+
+  template <typename... Ts>
+  class variant;
+
+  template <typename T>
+  struct variant_size;
+
+#ifdef MPARK_VARIABLE_TEMPLATES
+  template <typename T>
+  constexpr std::size_t variant_size_v = variant_size<T>::value;
+#endif
+
+  template <typename T>
+  struct variant_size<const T> : variant_size<T> {};
+
+  template <typename T>
+  struct variant_size<volatile T> : variant_size<T> {};
+
+  template <typename T>
+  struct variant_size<const volatile T> : variant_size<T> {};
+
+  template <typename... Ts>
+  struct variant_size<variant<Ts...>> : lib::size_constant<sizeof...(Ts)> {};
+
+  template <std::size_t I, typename T>
+  struct variant_alternative;
+
+  template <std::size_t I, typename T>
+  using variant_alternative_t = typename variant_alternative<I, T>::type;
+
+  template <std::size_t I, typename T>
+  struct variant_alternative<I, const T>
+      : std::add_const<variant_alternative_t<I, T>> {};
+
+  template <std::size_t I, typename T>
+  struct variant_alternative<I, volatile T>
+      : std::add_volatile<variant_alternative_t<I, T>> {};
+
+  template <std::size_t I, typename T>
+  struct variant_alternative<I, const volatile T>
+      : std::add_cv<variant_alternative_t<I, T>> {};
+
+  template <std::size_t I, typename... Ts>
+  struct variant_alternative<I, variant<Ts...>> {
+    static_assert(I < sizeof...(Ts),
+                  "index out of bounds in `std::variant_alternative<>`");
+    using type = lib::type_pack_element_t<I, Ts...>;
+  };
+
+  constexpr std::size_t variant_npos = static_cast<std::size_t>(-1);
+
+  namespace detail {
+
+    constexpr std::size_t not_found = static_cast<std::size_t>(-1);
+    constexpr std::size_t ambiguous = static_cast<std::size_t>(-2);
+
+#ifdef MPARK_CPP14_CONSTEXPR
+    template <typename T, typename... Ts>
+    inline constexpr std::size_t find_index() {
+      constexpr lib::array<bool, sizeof...(Ts)> matches = {
+          {std::is_same<T, Ts>::value...}
+      };
+      std::size_t result = not_found;
+      for (std::size_t i = 0; i < sizeof...(Ts); ++i) {
+        if (matches[i]) {
+          if (result != not_found) {
+            return ambiguous;
+          }
+          result = i;
+        }
+      }
+      return result;
+    }
+#else
+    inline constexpr std::size_t find_index_impl(std::size_t result,
+                                                 std::size_t) {
+      return result;
+    }
+
+    template <typename... Bs>
+    inline constexpr std::size_t find_index_impl(std::size_t result,
+                                                 std::size_t idx,
+                                                 bool b,
+                                                 Bs... bs) {
+      return b ? (result != not_found ? ambiguous
+                                      : find_index_impl(idx, idx + 1, bs...))
+               : find_index_impl(result, idx + 1, bs...);
+    }
+
+    template <typename T, typename... Ts>
+    inline constexpr std::size_t find_index() {
+      return find_index_impl(not_found, 0, std::is_same<T, Ts>::value...);
+    }
+#endif
+
+    template <std::size_t I>
+    using find_index_sfinae_impl =
+        lib::enable_if_t<I != not_found && I != ambiguous,
+                         lib::size_constant<I>>;
+
+    template <typename T, typename... Ts>
+    using find_index_sfinae = find_index_sfinae_impl<find_index<T, Ts...>()>;
+
+    template <std::size_t I>
+    struct find_index_checked_impl : lib::size_constant<I> {
+      static_assert(I != not_found, "the specified type is not found.");
+      static_assert(I != ambiguous, "the specified type is ambiguous.");
+    };
+
+    template <typename T, typename... Ts>
+    using find_index_checked = find_index_checked_impl<find_index<T, Ts...>()>;
+
+    struct valueless_t {};
+
+    enum class Trait { TriviallyAvailable, Available, Unavailable };
+
+    template <typename T,
+              template <typename> class IsTriviallyAvailable,
+              template <typename> class IsAvailable>
+    inline constexpr Trait trait() {
+      return IsTriviallyAvailable<T>::value
+                 ? Trait::TriviallyAvailable
+                 : IsAvailable<T>::value ? Trait::Available
+                                         : Trait::Unavailable;
+    }
+
+#ifdef MPARK_CPP14_CONSTEXPR
+    template <typename... Traits>
+    inline constexpr Trait common_trait(Traits... traits_) {
+      Trait result = Trait::TriviallyAvailable;
+      lib::array<Trait, sizeof...(Traits)> traits = {{traits_...}};
+      for (std::size_t i = 0; i < sizeof...(Traits); ++i) {
+        Trait t = traits[i];
+        if (static_cast<int>(t) > static_cast<int>(result)) {
+          result = t;
+        }
+      }
+      return result;
+    }
+#else
+    inline constexpr Trait common_trait_impl(Trait result) { return result; }
+
+    template <typename... Traits>
+    inline constexpr Trait common_trait_impl(Trait result,
+                                             Trait t,
+                                             Traits... ts) {
+      return static_cast<int>(t) > static_cast<int>(result)
+                 ? common_trait_impl(t, ts...)
+                 : common_trait_impl(result, ts...);
+    }
+
+    template <typename... Traits>
+    inline constexpr Trait common_trait(Traits... ts) {
+      return common_trait_impl(Trait::TriviallyAvailable, ts...);
+    }
+#endif
+
+    template <typename... Ts>
+    struct traits {
+      static constexpr Trait copy_constructible_trait =
+          common_trait(trait<Ts,
+                             lib::is_trivially_copy_constructible,
+                             std::is_copy_constructible>()...);
+
+      static constexpr Trait move_constructible_trait =
+          common_trait(trait<Ts,
+                             lib::is_trivially_move_constructible,
+                             std::is_move_constructible>()...);
+
+      static constexpr Trait copy_assignable_trait =
+          common_trait(copy_constructible_trait,
+                       trait<Ts,
+                             lib::is_trivially_copy_assignable,
+                             std::is_copy_assignable>()...);
+
+      static constexpr Trait move_assignable_trait =
+          common_trait(move_constructible_trait,
+                       trait<Ts,
+                             lib::is_trivially_move_assignable,
+                             std::is_move_assignable>()...);
+
+      static constexpr Trait destructible_trait =
+          common_trait(trait<Ts,
+                             std::is_trivially_destructible,
+                             std::is_destructible>()...);
+    };
+
+    namespace access {
+
+      struct recursive_union {
+#ifdef MPARK_RETURN_TYPE_DEDUCTION
+        template <typename V>
+        inline static constexpr auto &&get_alt(V &&v, in_place_index_t<0>) {
+          return lib::forward<V>(v).head_;
+        }
+
+        template <typename V, std::size_t I>
+        inline static constexpr auto &&get_alt(V &&v, in_place_index_t<I>) {
+          return get_alt(lib::forward<V>(v).tail_, in_place_index_t<I - 1>{});
+        }
+#else
+        template <std::size_t I, bool Dummy = true>
+        struct get_alt_impl {
+          template <typename V>
+          inline constexpr AUTO_REFREF operator()(V &&v) const
+            AUTO_REFREF_RETURN(get_alt_impl<I - 1>{}(lib::forward<V>(v).tail_))
+        };
+
+        template <bool Dummy>
+        struct get_alt_impl<0, Dummy> {
+          template <typename V>
+          inline constexpr AUTO_REFREF operator()(V &&v) const
+            AUTO_REFREF_RETURN(lib::forward<V>(v).head_)
+        };
+
+        template <typename V, std::size_t I>
+        inline static constexpr AUTO_REFREF get_alt(V &&v, in_place_index_t<I>)
+          AUTO_REFREF_RETURN(get_alt_impl<I>{}(lib::forward<V>(v)))
+#endif
+      };
+
+      struct base {
+        template <std::size_t I, typename V>
+        inline static constexpr AUTO_REFREF get_alt(V &&v)
+#ifdef _MSC_VER
+          AUTO_REFREF_RETURN(recursive_union::get_alt(
+              lib::forward<V>(v).data_, in_place_index_t<I>{}))
+#else
+          AUTO_REFREF_RETURN(recursive_union::get_alt(
+              data(lib::forward<V>(v)), in_place_index_t<I>{}))
+#endif
+      };
+
+      struct variant {
+        template <std::size_t I, typename V>
+        inline static constexpr AUTO_REFREF get_alt(V &&v)
+          AUTO_REFREF_RETURN(base::get_alt<I>(lib::forward<V>(v).impl_))
+      };
+
+    }  // namespace access
+
+    namespace visitation {
+
+#if defined(MPARK_CPP14_CONSTEXPR) && !defined(_MSC_VER)
+#define MPARK_VARIANT_SWITCH_VISIT
+#endif
+
+      struct base {
+        template <typename Visitor, typename... Vs>
+        using dispatch_result_t = decltype(
+            lib::invoke(std::declval<Visitor>(),
+                        access::base::get_alt<0>(std::declval<Vs>())...));
+
+        template <typename Expected>
+        struct expected {
+          template <typename Actual>
+          inline static constexpr bool but_got() {
+            return std::is_same<Expected, Actual>::value;
+          }
+        };
+
+        template <typename Expected, typename Actual>
+        struct visit_return_type_check {
+          static_assert(
+              expected<Expected>::template but_got<Actual>(),
+              "`visit` requires the visitor to have a single return type");
+
+          template <typename Visitor, typename... Alts>
+          inline static constexpr DECLTYPE_AUTO invoke(Visitor &&visitor,
+                                                       Alts &&... alts)
+            DECLTYPE_AUTO_RETURN(lib::invoke(lib::forward<Visitor>(visitor),
+                                             lib::forward<Alts>(alts)...))
+        };
+
+#ifdef MPARK_VARIANT_SWITCH_VISIT
+        template <bool B, typename R, typename... ITs>
+        struct dispatcher;
+
+        template <typename R, typename... ITs>
+        struct dispatcher<false, R, ITs...> {
+          template <std::size_t B, typename F, typename... Vs>
+          MPARK_ALWAYS_INLINE static constexpr R dispatch(
+              F &&, typename ITs::type &&..., Vs &&...) {
+            MPARK_BUILTIN_UNREACHABLE;
+          }
+
+          template <std::size_t I, typename F, typename... Vs>
+          MPARK_ALWAYS_INLINE static constexpr R dispatch_case(F &&, Vs &&...) {
+            MPARK_BUILTIN_UNREACHABLE;
+          }
+
+          template <std::size_t B, typename F, typename... Vs>
+          MPARK_ALWAYS_INLINE static constexpr R dispatch_at(std::size_t,
+                                                             F &&,
+                                                             Vs &&...) {
+            MPARK_BUILTIN_UNREACHABLE;
+          }
+        };
+
+        template <typename R, typename... ITs>
+        struct dispatcher<true, R, ITs...> {
+          template <std::size_t B, typename F>
+          MPARK_ALWAYS_INLINE static constexpr R dispatch(
+              F &&f, typename ITs::type &&... visited_vs) {
+            using Expected = R;
+            using Actual = decltype(lib::invoke(
+                lib::forward<F>(f),
+                access::base::get_alt<ITs::value>(
+                    lib::forward<typename ITs::type>(visited_vs))...));
+            return visit_return_type_check<Expected, Actual>::invoke(
+                lib::forward<F>(f),
+                access::base::get_alt<ITs::value>(
+                    lib::forward<typename ITs::type>(visited_vs))...);
+          }
+
+          template <std::size_t B, typename F, typename V, typename... Vs>
+          MPARK_ALWAYS_INLINE static constexpr R dispatch(
+              F &&f, typename ITs::type &&... visited_vs, V &&v, Vs &&... vs) {
+#define MPARK_DISPATCH(I)                                                   \
+  dispatcher<(I < lib::decay_t<V>::size()),                                 \
+             R,                                                             \
+             ITs...,                                                        \
+             lib::indexed_type<I, V>>::                                     \
+      template dispatch<0>(lib::forward<F>(f),                              \
+                           lib::forward<typename ITs::type>(visited_vs)..., \
+                           lib::forward<V>(v),                              \
+                           lib::forward<Vs>(vs)...)
+
+#define MPARK_DEFAULT(I)                                                      \
+  dispatcher<(I < lib::decay_t<V>::size()), R, ITs...>::template dispatch<I>( \
+      lib::forward<F>(f),                                                     \
+      lib::forward<typename ITs::type>(visited_vs)...,                        \
+      lib::forward<V>(v),                                                     \
+      lib::forward<Vs>(vs)...)
+
+            switch (v.index()) {
+              case B + 0: return MPARK_DISPATCH(B + 0);
+              case B + 1: return MPARK_DISPATCH(B + 1);
+              case B + 2: return MPARK_DISPATCH(B + 2);
+              case B + 3: return MPARK_DISPATCH(B + 3);
+              case B + 4: return MPARK_DISPATCH(B + 4);
+              case B + 5: return MPARK_DISPATCH(B + 5);
+              case B + 6: return MPARK_DISPATCH(B + 6);
+              case B + 7: return MPARK_DISPATCH(B + 7);
+              case B + 8: return MPARK_DISPATCH(B + 8);
+              case B + 9: return MPARK_DISPATCH(B + 9);
+              case B + 10: return MPARK_DISPATCH(B + 10);
+              case B + 11: return MPARK_DISPATCH(B + 11);
+              case B + 12: return MPARK_DISPATCH(B + 12);
+              case B + 13: return MPARK_DISPATCH(B + 13);
+              case B + 14: return MPARK_DISPATCH(B + 14);
+              case B + 15: return MPARK_DISPATCH(B + 15);
+              case B + 16: return MPARK_DISPATCH(B + 16);
+              case B + 17: return MPARK_DISPATCH(B + 17);
+              case B + 18: return MPARK_DISPATCH(B + 18);
+              case B + 19: return MPARK_DISPATCH(B + 19);
+              case B + 20: return MPARK_DISPATCH(B + 20);
+              case B + 21: return MPARK_DISPATCH(B + 21);
+              case B + 22: return MPARK_DISPATCH(B + 22);
+              case B + 23: return MPARK_DISPATCH(B + 23);
+              case B + 24: return MPARK_DISPATCH(B + 24);
+              case B + 25: return MPARK_DISPATCH(B + 25);
+              case B + 26: return MPARK_DISPATCH(B + 26);
+              case B + 27: return MPARK_DISPATCH(B + 27);
+              case B + 28: return MPARK_DISPATCH(B + 28);
+              case B + 29: return MPARK_DISPATCH(B + 29);
+              case B + 30: return MPARK_DISPATCH(B + 30);
+              case B + 31: return MPARK_DISPATCH(B + 31);
+              default: return MPARK_DEFAULT(B + 32);
+            }
+
+#undef MPARK_DEFAULT
+#undef MPARK_DISPATCH
+          }
+
+          template <std::size_t I, typename F, typename... Vs>
+          MPARK_ALWAYS_INLINE static constexpr R dispatch_case(F &&f,
+                                                               Vs &&... vs) {
+            using Expected = R;
+            using Actual = decltype(
+                lib::invoke(lib::forward<F>(f),
+                            access::base::get_alt<I>(lib::forward<Vs>(vs))...));
+            return visit_return_type_check<Expected, Actual>::invoke(
+                lib::forward<F>(f),
+                access::base::get_alt<I>(lib::forward<Vs>(vs))...);
+          }
+
+          template <std::size_t B, typename F, typename V, typename... Vs>
+          MPARK_ALWAYS_INLINE static constexpr R dispatch_at(std::size_t index,
+                                                             F &&f,
+                                                             V &&v,
+                                                             Vs &&... vs) {
+            static_assert(lib::all<(lib::decay_t<V>::size() ==
+                                    lib::decay_t<Vs>::size())...>::value,
+                          "all of the variants must be the same size.");
+#define MPARK_DISPATCH_AT(I)                                               \
+  dispatcher<(I < lib::decay_t<V>::size()), R>::template dispatch_case<I>( \
+      lib::forward<F>(f), lib::forward<V>(v), lib::forward<Vs>(vs)...)
+
+#define MPARK_DEFAULT(I)                                                 \
+  dispatcher<(I < lib::decay_t<V>::size()), R>::template dispatch_at<I>( \
+      index, lib::forward<F>(f), lib::forward<V>(v), lib::forward<Vs>(vs)...)
+
+            switch (index) {
+              case B + 0: return MPARK_DISPATCH_AT(B + 0);
+              case B + 1: return MPARK_DISPATCH_AT(B + 1);
+              case B + 2: return MPARK_DISPATCH_AT(B + 2);
+              case B + 3: return MPARK_DISPATCH_AT(B + 3);
+              case B + 4: return MPARK_DISPATCH_AT(B + 4);
+              case B + 5: return MPARK_DISPATCH_AT(B + 5);
+              case B + 6: return MPARK_DISPATCH_AT(B + 6);
+              case B + 7: return MPARK_DISPATCH_AT(B + 7);
+              case B + 8: return MPARK_DISPATCH_AT(B + 8);
+              case B + 9: return MPARK_DISPATCH_AT(B + 9);
+              case B + 10: return MPARK_DISPATCH_AT(B + 10);
+              case B + 11: return MPARK_DISPATCH_AT(B + 11);
+              case B + 12: return MPARK_DISPATCH_AT(B + 12);
+              case B + 13: return MPARK_DISPATCH_AT(B + 13);
+              case B + 14: return MPARK_DISPATCH_AT(B + 14);
+              case B + 15: return MPARK_DISPATCH_AT(B + 15);
+              case B + 16: return MPARK_DISPATCH_AT(B + 16);
+              case B + 17: return MPARK_DISPATCH_AT(B + 17);
+              case B + 18: return MPARK_DISPATCH_AT(B + 18);
+              case B + 19: return MPARK_DISPATCH_AT(B + 19);
+              case B + 20: return MPARK_DISPATCH_AT(B + 20);
+              case B + 21: return MPARK_DISPATCH_AT(B + 21);
+              case B + 22: return MPARK_DISPATCH_AT(B + 22);
+              case B + 23: return MPARK_DISPATCH_AT(B + 23);
+              case B + 24: return MPARK_DISPATCH_AT(B + 24);
+              case B + 25: return MPARK_DISPATCH_AT(B + 25);
+              case B + 26: return MPARK_DISPATCH_AT(B + 26);
+              case B + 27: return MPARK_DISPATCH_AT(B + 27);
+              case B + 28: return MPARK_DISPATCH_AT(B + 28);
+              case B + 29: return MPARK_DISPATCH_AT(B + 29);
+              case B + 30: return MPARK_DISPATCH_AT(B + 30);
+              case B + 31: return MPARK_DISPATCH_AT(B + 31);
+              default: return MPARK_DEFAULT(B + 32);
+            }
+
+#undef MPARK_DEFAULT
+#undef MPARK_DISPATCH_AT
+          }
+        };
+#else
+        template <typename T>
+        inline static constexpr const T &at(const T &elem) noexcept {
+          return elem;
+        }
+
+        template <typename T, std::size_t N, typename... Is>
+        inline static constexpr const lib::remove_all_extents_t<T> &at(
+            const lib::array<T, N> &elems, std::size_t i, Is... is) noexcept {
+          return at(elems[i], is...);
+        }
+
+        template <typename F, typename... Fs>
+        inline static constexpr lib::array<lib::decay_t<F>, sizeof...(Fs) + 1>
+        make_farray(F &&f, Fs &&... fs) {
+          return {{lib::forward<F>(f), lib::forward<Fs>(fs)...}};
+        }
+
+        template <typename F, typename... Vs>
+        struct make_fmatrix_impl {
+
+          template <std::size_t... Is>
+          inline static constexpr dispatch_result_t<F, Vs...> dispatch(
+              F &&f, Vs &&... vs) {
+            using Expected = dispatch_result_t<F, Vs...>;
+            using Actual = decltype(lib::invoke(
+                lib::forward<F>(f),
+                access::base::get_alt<Is>(lib::forward<Vs>(vs))...));
+            return visit_return_type_check<Expected, Actual>::invoke(
+                lib::forward<F>(f),
+                access::base::get_alt<Is>(lib::forward<Vs>(vs))...);
+          }
+
+#ifdef MPARK_RETURN_TYPE_DEDUCTION
+          template <std::size_t... Is>
+          inline static constexpr auto impl(lib::index_sequence<Is...>) {
+            return &dispatch<Is...>;
+          }
+
+          template <typename Is, std::size_t... Js, typename... Ls>
+          inline static constexpr auto impl(Is,
+                                            lib::index_sequence<Js...>,
+                                            Ls... ls) {
+            return make_farray(impl(lib::push_back_t<Is, Js>{}, ls...)...);
+          }
+#else
+          template <typename...>
+          struct impl;
+
+          template <std::size_t... Is>
+          struct impl<lib::index_sequence<Is...>> {
+            inline constexpr AUTO operator()() const
+              AUTO_RETURN(&dispatch<Is...>)
+          };
+
+          template <typename Is, std::size_t... Js, typename... Ls>
+          struct impl<Is, lib::index_sequence<Js...>, Ls...> {
+            inline constexpr AUTO operator()() const
+              AUTO_RETURN(
+                  make_farray(impl<lib::push_back_t<Is, Js>, Ls...>{}()...))
+          };
+#endif
+        };
+
+#ifdef MPARK_RETURN_TYPE_DEDUCTION
+        template <typename F, typename... Vs>
+        inline static constexpr auto make_fmatrix() {
+          return make_fmatrix_impl<F, Vs...>::impl(
+              lib::index_sequence<>{},
+              lib::make_index_sequence<lib::decay_t<Vs>::size()>{}...);
+        }
+#else
+        template <typename F, typename... Vs>
+        inline static constexpr AUTO make_fmatrix()
+          AUTO_RETURN(
+              typename make_fmatrix_impl<F, Vs...>::template impl<
+                  lib::index_sequence<>,
+                  lib::make_index_sequence<lib::decay_t<Vs>::size()>...>{}())
+#endif
+
+        template <typename F, typename... Vs>
+        struct make_fdiagonal_impl {
+          template <std::size_t I>
+          inline static constexpr dispatch_result_t<F, Vs...> dispatch(
+              F &&f, Vs &&... vs) {
+            using Expected = dispatch_result_t<F, Vs...>;
+            using Actual = decltype(
+                lib::invoke(lib::forward<F>(f),
+                            access::base::get_alt<I>(lib::forward<Vs>(vs))...));
+            return visit_return_type_check<Expected, Actual>::invoke(
+                lib::forward<F>(f),
+                access::base::get_alt<I>(lib::forward<Vs>(vs))...);
+          }
+
+          template <std::size_t... Is>
+          inline static constexpr AUTO impl(lib::index_sequence<Is...>)
+            AUTO_RETURN(make_farray(&dispatch<Is>...))
+        };
+
+        template <typename F, typename V, typename... Vs>
+        inline static constexpr auto make_fdiagonal()
+            -> decltype(make_fdiagonal_impl<F, V, Vs...>::impl(
+                lib::make_index_sequence<lib::decay_t<V>::size()>{})) {
+          static_assert(lib::all<(lib::decay_t<V>::size() ==
+                                  lib::decay_t<Vs>::size())...>::value,
+                        "all of the variants must be the same size.");
+          return make_fdiagonal_impl<F, V, Vs...>::impl(
+              lib::make_index_sequence<lib::decay_t<V>::size()>{});
+        }
+#endif
+      };
+
+#if !defined(MPARK_VARIANT_SWITCH_VISIT) && \
+    (!defined(_MSC_VER) || _MSC_VER >= 1910)
+      template <typename F, typename... Vs>
+      using fmatrix_t = decltype(base::make_fmatrix<F, Vs...>());
+
+      template <typename F, typename... Vs>
+      struct fmatrix {
+        static constexpr fmatrix_t<F, Vs...> value =
+            base::make_fmatrix<F, Vs...>();
+      };
+
+      template <typename F, typename... Vs>
+      constexpr fmatrix_t<F, Vs...> fmatrix<F, Vs...>::value;
+
+      template <typename F, typename... Vs>
+      using fdiagonal_t = decltype(base::make_fdiagonal<F, Vs...>());
+
+      template <typename F, typename... Vs>
+      struct fdiagonal {
+        static constexpr fdiagonal_t<F, Vs...> value =
+            base::make_fdiagonal<F, Vs...>();
+      };
+
+      template <typename F, typename... Vs>
+      constexpr fdiagonal_t<F, Vs...> fdiagonal<F, Vs...>::value;
+#endif
+
+      struct alt {
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_alt(Visitor &&visitor,
+                                                        Vs &&... vs)
+#ifdef MPARK_VARIANT_SWITCH_VISIT
+          DECLTYPE_AUTO_RETURN(
+              base::dispatcher<
+                  true,
+                  base::dispatch_result_t<Visitor,
+                                          decltype(as_base(
+                                              lib::forward<Vs>(vs)))...>>::
+                  template dispatch<0>(lib::forward<Visitor>(visitor),
+                                       as_base(lib::forward<Vs>(vs))...))
+#elif !defined(_MSC_VER) || _MSC_VER >= 1910
+          DECLTYPE_AUTO_RETURN(base::at(
+              fmatrix<Visitor &&,
+                      decltype(as_base(lib::forward<Vs>(vs)))...>::value,
+              vs.index()...)(lib::forward<Visitor>(visitor),
+                             as_base(lib::forward<Vs>(vs))...))
+#else
+          DECLTYPE_AUTO_RETURN(base::at(
+              base::make_fmatrix<Visitor &&,
+                      decltype(as_base(lib::forward<Vs>(vs)))...>(),
+              vs.index()...)(lib::forward<Visitor>(visitor),
+                             as_base(lib::forward<Vs>(vs))...))
+#endif
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_alt_at(std::size_t index,
+                                                           Visitor &&visitor,
+                                                           Vs &&... vs)
+#ifdef MPARK_VARIANT_SWITCH_VISIT
+          DECLTYPE_AUTO_RETURN(
+              base::dispatcher<
+                  true,
+                  base::dispatch_result_t<Visitor,
+                                          decltype(as_base(
+                                              lib::forward<Vs>(vs)))...>>::
+                  template dispatch_at<0>(index,
+                                          lib::forward<Visitor>(visitor),
+                                          as_base(lib::forward<Vs>(vs))...))
+#elif !defined(_MSC_VER) || _MSC_VER >= 1910
+          DECLTYPE_AUTO_RETURN(base::at(
+              fdiagonal<Visitor &&,
+                        decltype(as_base(lib::forward<Vs>(vs)))...>::value,
+              index)(lib::forward<Visitor>(visitor),
+                     as_base(lib::forward<Vs>(vs))...))
+#else
+          DECLTYPE_AUTO_RETURN(base::at(
+              base::make_fdiagonal<Visitor &&,
+                        decltype(as_base(lib::forward<Vs>(vs)))...>(),
+              index)(lib::forward<Visitor>(visitor),
+                     as_base(lib::forward<Vs>(vs))...))
+#endif
+      };
+
+      struct variant {
+        private:
+        template <typename Visitor>
+        struct visitor {
+          template <typename... Values>
+          inline static constexpr bool does_not_handle() {
+            return lib::is_invocable<Visitor, Values...>::value;
+          }
+        };
+
+        template <typename Visitor, typename... Values>
+        struct visit_exhaustiveness_check {
+          static_assert(visitor<Visitor>::template does_not_handle<Values...>(),
+                        "`visit` requires the visitor to be exhaustive.");
+
+          inline static constexpr DECLTYPE_AUTO invoke(Visitor &&visitor,
+                                                       Values &&... values)
+            DECLTYPE_AUTO_RETURN(lib::invoke(lib::forward<Visitor>(visitor),
+                                             lib::forward<Values>(values)...))
+        };
+
+        template <typename Visitor>
+        struct value_visitor {
+          Visitor &&visitor_;
+
+          template <typename... Alts>
+          inline constexpr DECLTYPE_AUTO operator()(Alts &&... alts) const
+            DECLTYPE_AUTO_RETURN(
+                visit_exhaustiveness_check<
+                    Visitor,
+                    decltype((lib::forward<Alts>(alts).value))...>::
+                    invoke(lib::forward<Visitor>(visitor_),
+                           lib::forward<Alts>(alts).value...))
+        };
+
+        template <typename Visitor>
+        inline static constexpr AUTO make_value_visitor(Visitor &&visitor)
+          AUTO_RETURN(value_visitor<Visitor>{lib::forward<Visitor>(visitor)})
+
+        public:
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_alt(Visitor &&visitor,
+                                                        Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(alt::visit_alt(lib::forward<Visitor>(visitor),
+                                              lib::forward<Vs>(vs).impl_...))
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_alt_at(std::size_t index,
+                                                           Visitor &&visitor,
+                                                           Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(
+              alt::visit_alt_at(index,
+                                lib::forward<Visitor>(visitor),
+                                lib::forward<Vs>(vs).impl_...))
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_value(Visitor &&visitor,
+                                                          Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(
+              visit_alt(make_value_visitor(lib::forward<Visitor>(visitor)),
+                        lib::forward<Vs>(vs)...))
+
+        template <typename Visitor, typename... Vs>
+        inline static constexpr DECLTYPE_AUTO visit_value_at(std::size_t index,
+                                                             Visitor &&visitor,
+                                                             Vs &&... vs)
+          DECLTYPE_AUTO_RETURN(
+              visit_alt_at(index,
+                           make_value_visitor(lib::forward<Visitor>(visitor)),
+                           lib::forward<Vs>(vs)...))
+      };
+
+    }  // namespace visitation
+
+    template <std::size_t Index, typename T>
+    struct alt {
+      using value_type = T;
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4244)
+#endif
+      template <typename... Args>
+      inline explicit constexpr alt(in_place_t, Args &&... args)
+          : value(lib::forward<Args>(args)...) {}
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+      T value;
+    };
+
+    template <Trait DestructibleTrait, std::size_t Index, typename... Ts>
+    union recursive_union;
+
+    template <Trait DestructibleTrait, std::size_t Index>
+    union recursive_union<DestructibleTrait, Index> {};
+
+#define MPARK_VARIANT_RECURSIVE_UNION(destructible_trait, destructor)      \
+  template <std::size_t Index, typename T, typename... Ts>                 \
+  union recursive_union<destructible_trait, Index, T, Ts...> {             \
+    public:                                                                \
+    inline explicit constexpr recursive_union(valueless_t) noexcept        \
+        : dummy_{} {}                                                      \
+                                                                           \
+    template <typename... Args>                                            \
+    inline explicit constexpr recursive_union(in_place_index_t<0>,         \
+                                              Args &&... args)             \
+        : head_(in_place_t{}, lib::forward<Args>(args)...) {}              \
+                                                                           \
+    template <std::size_t I, typename... Args>                             \
+    inline explicit constexpr recursive_union(in_place_index_t<I>,         \
+                                              Args &&... args)             \
+        : tail_(in_place_index_t<I - 1>{}, lib::forward<Args>(args)...) {} \
+                                                                           \
+    recursive_union(const recursive_union &) = default;                    \
+    recursive_union(recursive_union &&) = default;                         \
+                                                                           \
+    destructor                                                             \
+                                                                           \
+    recursive_union &operator=(const recursive_union &) = default;         \
+    recursive_union &operator=(recursive_union &&) = default;              \
+                                                                           \
+    private:                                                               \
+    char dummy_;                                                           \
+    alt<Index, T> head_;                                                   \
+    recursive_union<destructible_trait, Index + 1, Ts...> tail_;           \
+                                                                           \
+    friend struct access::recursive_union;                                 \
+  }
+
+    MPARK_VARIANT_RECURSIVE_UNION(Trait::TriviallyAvailable,
+                                  ~recursive_union() = default;);
+    MPARK_VARIANT_RECURSIVE_UNION(Trait::Available,
+                                  ~recursive_union() {});
+    MPARK_VARIANT_RECURSIVE_UNION(Trait::Unavailable,
+                                  ~recursive_union() = delete;);
+
+#undef MPARK_VARIANT_RECURSIVE_UNION
+
+    using index_t = unsigned int;
+
+    template <Trait DestructibleTrait, typename... Ts>
+    class base {
+      public:
+      inline explicit constexpr base(valueless_t tag) noexcept
+          : data_(tag), index_(static_cast<index_t>(-1)) {}
+
+      template <std::size_t I, typename... Args>
+      inline explicit constexpr base(in_place_index_t<I>, Args &&... args)
+          : data_(in_place_index_t<I>{}, lib::forward<Args>(args)...),
+            index_(I) {}
+
+      inline constexpr bool valueless_by_exception() const noexcept {
+        return index_ == static_cast<index_t>(-1);
+      }
+
+      inline constexpr std::size_t index() const noexcept {
+        return valueless_by_exception() ? variant_npos : index_;
+      }
+
+      protected:
+      using data_t = recursive_union<DestructibleTrait, 0, Ts...>;
+
+      friend inline constexpr base &as_base(base &b) { return b; }
+      friend inline constexpr const base &as_base(const base &b) { return b; }
+      friend inline constexpr base &&as_base(base &&b) { return lib::move(b); }
+      friend inline constexpr const base &&as_base(const base &&b) { return lib::move(b); }
+
+      friend inline constexpr data_t &data(base &b) { return b.data_; }
+      friend inline constexpr const data_t &data(const base &b) { return b.data_; }
+      friend inline constexpr data_t &&data(base &&b) { return lib::move(b).data_; }
+      friend inline constexpr const data_t &&data(const base &&b) { return lib::move(b).data_; }
+
+      inline static constexpr std::size_t size() { return sizeof...(Ts); }
+
+      data_t data_;
+      index_t index_;
+
+      friend struct access::base;
+      friend struct visitation::base;
+    };
+
+    struct dtor {
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4100)
+#endif
+      template <typename Alt>
+      inline void operator()(Alt &alt) const noexcept { alt.~Alt(); }
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+    };
+
+#if !defined(_MSC_VER) || _MSC_VER >= 1910
+#define MPARK_INHERITING_CTOR(type, base) using base::base;
+#else
+#define MPARK_INHERITING_CTOR(type, base)         \
+  template <typename... Args>                     \
+  inline explicit constexpr type(Args &&... args) \
+      : base(lib::forward<Args>(args)...) {}
+#endif
+
+    template <typename Traits, Trait = Traits::destructible_trait>
+    class destructor;
+
+#define MPARK_VARIANT_DESTRUCTOR(destructible_trait, definition, destroy) \
+  template <typename... Ts>                                               \
+  class destructor<traits<Ts...>, destructible_trait>                     \
+      : public base<destructible_trait, Ts...> {                          \
+    using super = base<destructible_trait, Ts...>;                        \
+                                                                          \
+    public:                                                               \
+    MPARK_INHERITING_CTOR(destructor, super)                              \
+    using super::operator=;                                               \
+                                                                          \
+    destructor(const destructor &) = default;                             \
+    destructor(destructor &&) = default;                                  \
+    definition                                                            \
+    destructor &operator=(const destructor &) = default;                  \
+    destructor &operator=(destructor &&) = default;                       \
+                                                                          \
+    protected:                                                            \
+    destroy                                                               \
+  }
+
+    MPARK_VARIANT_DESTRUCTOR(
+        Trait::TriviallyAvailable,
+        ~destructor() = default;,
+        inline void destroy() noexcept {
+          this->index_ = static_cast<index_t>(-1);
+        });
+
+    MPARK_VARIANT_DESTRUCTOR(
+        Trait::Available,
+        ~destructor() { destroy(); },
+        inline void destroy() noexcept {
+          if (!this->valueless_by_exception()) {
+            visitation::alt::visit_alt(dtor{}, *this);
+          }
+          this->index_ = static_cast<index_t>(-1);
+        });
+
+    MPARK_VARIANT_DESTRUCTOR(
+        Trait::Unavailable,
+        ~destructor() = delete;,
+        inline void destroy() noexcept = delete;);
+
+#undef MPARK_VARIANT_DESTRUCTOR
+
+    template <typename Traits>
+    class constructor : public destructor<Traits> {
+      using super = destructor<Traits>;
+
+      public:
+      MPARK_INHERITING_CTOR(constructor, super)
+      using super::operator=;
+
+      protected:
+#ifndef MPARK_GENERIC_LAMBDAS
+      struct ctor {
+        template <typename LhsAlt, typename RhsAlt>
+        inline void operator()(LhsAlt &lhs_alt, RhsAlt &&rhs_alt) const {
+          constructor::construct_alt(lhs_alt,
+                                     lib::forward<RhsAlt>(rhs_alt).value);
+        }
+      };
+#endif
+
+      template <std::size_t I, typename T, typename... Args>
+      inline static T &construct_alt(alt<I, T> &a, Args &&... args) {
+        auto *result = ::new (static_cast<void *>(lib::addressof(a)))
+            alt<I, T>(in_place_t{}, lib::forward<Args>(args)...);
+        return result->value;
+      }
+
+      template <typename Rhs>
+      inline static void generic_construct(constructor &lhs, Rhs &&rhs) {
+        lhs.destroy();
+        if (!rhs.valueless_by_exception()) {
+          visitation::alt::visit_alt_at(
+              rhs.index(),
+#ifdef MPARK_GENERIC_LAMBDAS
+              [](auto &lhs_alt, auto &&rhs_alt) {
+                constructor::construct_alt(
+                    lhs_alt, lib::forward<decltype(rhs_alt)>(rhs_alt).value);
+              }
+#else
+              ctor{}
+#endif
+              ,
+              lhs,
+              lib::forward<Rhs>(rhs));
+          lhs.index_ = rhs.index_;
+        }
+      }
+    };
+
+    template <typename Traits, Trait = Traits::move_constructible_trait>
+    class move_constructor;
+
+#define MPARK_VARIANT_MOVE_CONSTRUCTOR(move_constructible_trait, definition) \
+  template <typename... Ts>                                                  \
+  class move_constructor<traits<Ts...>, move_constructible_trait>            \
+      : public constructor<traits<Ts...>> {                                  \
+    using super = constructor<traits<Ts...>>;                                \
+                                                                             \
+    public:                                                                  \
+    MPARK_INHERITING_CTOR(move_constructor, super)                           \
+    using super::operator=;                                                  \
+                                                                             \
+    move_constructor(const move_constructor &) = default;                    \
+    definition                                                               \
+    ~move_constructor() = default;                                           \
+    move_constructor &operator=(const move_constructor &) = default;         \
+    move_constructor &operator=(move_constructor &&) = default;              \
+  }
+
+    MPARK_VARIANT_MOVE_CONSTRUCTOR(
+        Trait::TriviallyAvailable,
+        move_constructor(move_constructor &&that) = default;);
+
+    MPARK_VARIANT_MOVE_CONSTRUCTOR(
+        Trait::Available,
+        move_constructor(move_constructor &&that) noexcept(
+            lib::all<std::is_nothrow_move_constructible<Ts>::value...>::value)
+            : move_constructor(valueless_t{}) {
+          this->generic_construct(*this, lib::move(that));
+        });
+
+    MPARK_VARIANT_MOVE_CONSTRUCTOR(
+        Trait::Unavailable,
+        move_constructor(move_constructor &&) = delete;);
+
+#undef MPARK_VARIANT_MOVE_CONSTRUCTOR
+
+    template <typename Traits, Trait = Traits::copy_constructible_trait>
+    class copy_constructor;
+
+#define MPARK_VARIANT_COPY_CONSTRUCTOR(copy_constructible_trait, definition) \
+  template <typename... Ts>                                                  \
+  class copy_constructor<traits<Ts...>, copy_constructible_trait>            \
+      : public move_constructor<traits<Ts...>> {                             \
+    using super = move_constructor<traits<Ts...>>;                           \
+                                                                             \
+    public:                                                                  \
+    MPARK_INHERITING_CTOR(copy_constructor, super)                           \
+    using super::operator=;                                                  \
+                                                                             \
+    definition                                                               \
+    copy_constructor(copy_constructor &&) = default;                         \
+    ~copy_constructor() = default;                                           \
+    copy_constructor &operator=(const copy_constructor &) = default;         \
+    copy_constructor &operator=(copy_constructor &&) = default;              \
+  }
+
+    MPARK_VARIANT_COPY_CONSTRUCTOR(
+        Trait::TriviallyAvailable,
+        copy_constructor(const copy_constructor &that) = default;);
+
+    MPARK_VARIANT_COPY_CONSTRUCTOR(
+        Trait::Available,
+        copy_constructor(const copy_constructor &that)
+            : copy_constructor(valueless_t{}) {
+          this->generic_construct(*this, that);
+        });
+
+    MPARK_VARIANT_COPY_CONSTRUCTOR(
+        Trait::Unavailable,
+        copy_constructor(const copy_constructor &) = delete;);
+
+#undef MPARK_VARIANT_COPY_CONSTRUCTOR
+
+    template <typename Traits>
+    class assignment : public copy_constructor<Traits> {
+      using super = copy_constructor<Traits>;
+
+      public:
+      MPARK_INHERITING_CTOR(assignment, super)
+      using super::operator=;
+
+      template <std::size_t I, typename... Args>
+      inline /* auto & */ auto emplace(Args &&... args)
+          -> decltype(this->construct_alt(access::base::get_alt<I>(*this),
+                                          lib::forward<Args>(args)...)) {
+        this->destroy();
+        auto &result = this->construct_alt(access::base::get_alt<I>(*this),
+                                           lib::forward<Args>(args)...);
+        this->index_ = I;
+        return result;
+      }
+
+      protected:
+#ifndef MPARK_GENERIC_LAMBDAS
+      template <typename That>
+      struct assigner {
+        template <typename ThisAlt, typename ThatAlt>
+        inline void operator()(ThisAlt &this_alt, ThatAlt &&that_alt) const {
+          self->assign_alt(this_alt, lib::forward<ThatAlt>(that_alt).value);
+        }
+        assignment *self;
+      };
+#endif
+
+      template <std::size_t I, typename T, typename Arg>
+      inline void assign_alt(alt<I, T> &a, Arg &&arg) {
+        if (this->index() == I) {
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4244)
+#endif
+          a.value = lib::forward<Arg>(arg);
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+        } else {
+          struct {
+            void operator()(std::true_type) const {
+              this_->emplace<I>(lib::forward<Arg>(arg_));
+            }
+            void operator()(std::false_type) const {
+              this_->emplace<I>(T(lib::forward<Arg>(arg_)));
+            }
+            assignment *this_;
+            Arg &&arg_;
+          } impl{this, lib::forward<Arg>(arg)};
+          impl(lib::bool_constant<
+                   std::is_nothrow_constructible<T, Arg>::value ||
+                   !std::is_nothrow_move_constructible<T>::value>{});
+        }
+      }
+
+      template <typename That>
+      inline void generic_assign(That &&that) {
+        if (this->valueless_by_exception() && that.valueless_by_exception()) {
+          // do nothing.
+        } else if (that.valueless_by_exception()) {
+          this->destroy();
+        } else {
+          visitation::alt::visit_alt_at(
+              that.index(),
+#ifdef MPARK_GENERIC_LAMBDAS
+              [this](auto &this_alt, auto &&that_alt) {
+                this->assign_alt(
+                    this_alt, lib::forward<decltype(that_alt)>(that_alt).value);
+              }
+#else
+              assigner<That>{this}
+#endif
+              ,
+              *this,
+              lib::forward<That>(that));
+        }
+      }
+    };
+
+    template <typename Traits, Trait = Traits::move_assignable_trait>
+    class move_assignment;
+
+#define MPARK_VARIANT_MOVE_ASSIGNMENT(move_assignable_trait, definition) \
+  template <typename... Ts>                                              \
+  class move_assignment<traits<Ts...>, move_assignable_trait>            \
+      : public assignment<traits<Ts...>> {                               \
+    using super = assignment<traits<Ts...>>;                             \
+                                                                         \
+    public:                                                              \
+    MPARK_INHERITING_CTOR(move_assignment, super)                        \
+    using super::operator=;                                              \
+                                                                         \
+    move_assignment(const move_assignment &) = default;                  \
+    move_assignment(move_assignment &&) = default;                       \
+    ~move_assignment() = default;                                        \
+    move_assignment &operator=(const move_assignment &) = default;       \
+    definition                                                           \
+  }
+
+    MPARK_VARIANT_MOVE_ASSIGNMENT(
+        Trait::TriviallyAvailable,
+        move_assignment &operator=(move_assignment &&that) = default;);
+
+    MPARK_VARIANT_MOVE_ASSIGNMENT(
+        Trait::Available,
+        move_assignment &
+        operator=(move_assignment &&that) noexcept(
+            lib::all<(std::is_nothrow_move_constructible<Ts>::value &&
+                      std::is_nothrow_move_assignable<Ts>::value)...>::value) {
+          this->generic_assign(lib::move(that));
+          return *this;
+        });
+
+    MPARK_VARIANT_MOVE_ASSIGNMENT(
+        Trait::Unavailable,
+        move_assignment &operator=(move_assignment &&) = delete;);
+
+#undef MPARK_VARIANT_MOVE_ASSIGNMENT
+
+    template <typename Traits, Trait = Traits::copy_assignable_trait>
+    class copy_assignment;
+
+#define MPARK_VARIANT_COPY_ASSIGNMENT(copy_assignable_trait, definition) \
+  template <typename... Ts>                                              \
+  class copy_assignment<traits<Ts...>, copy_assignable_trait>            \
+      : public move_assignment<traits<Ts...>> {                          \
+    using super = move_assignment<traits<Ts...>>;                        \
+                                                                         \
+    public:                                                              \
+    MPARK_INHERITING_CTOR(copy_assignment, super)                        \
+    using super::operator=;                                              \
+                                                                         \
+    copy_assignment(const copy_assignment &) = default;                  \
+    copy_assignment(copy_assignment &&) = default;                       \
+    ~copy_assignment() = default;                                        \
+    definition                                                           \
+    copy_assignment &operator=(copy_assignment &&) = default;            \
+  }
+
+    MPARK_VARIANT_COPY_ASSIGNMENT(
+        Trait::TriviallyAvailable,
+        copy_assignment &operator=(const copy_assignment &that) = default;);
+
+    MPARK_VARIANT_COPY_ASSIGNMENT(
+        Trait::Available,
+        copy_assignment &operator=(const copy_assignment &that) {
+          this->generic_assign(that);
+          return *this;
+        });
+
+    MPARK_VARIANT_COPY_ASSIGNMENT(
+        Trait::Unavailable,
+        copy_assignment &operator=(const copy_assignment &) = delete;);
+
+#undef MPARK_VARIANT_COPY_ASSIGNMENT
+
+    template <typename... Ts>
+    class impl : public copy_assignment<traits<Ts...>> {
+      using super = copy_assignment<traits<Ts...>>;
+
+      public:
+      MPARK_INHERITING_CTOR(impl, super)
+      using super::operator=;
+
+      template <std::size_t I, typename Arg>
+      inline void assign(Arg &&arg) {
+        this->assign_alt(access::base::get_alt<I>(*this),
+                         lib::forward<Arg>(arg));
+      }
+
+      inline void swap(impl &that) {
+        if (this->valueless_by_exception() && that.valueless_by_exception()) {
+          // do nothing.
+        } else if (this->index() == that.index()) {
+          visitation::alt::visit_alt_at(this->index(),
+#ifdef MPARK_GENERIC_LAMBDAS
+                                        [](auto &this_alt, auto &that_alt) {
+                                          using std::swap;
+                                          swap(this_alt.value,
+                                               that_alt.value);
+                                        }
+#else
+                                        swapper{}
+#endif
+                                        ,
+                                        *this,
+                                        that);
+        } else {
+          impl *lhs = this;
+          impl *rhs = lib::addressof(that);
+          if (lhs->move_nothrow() && !rhs->move_nothrow()) {
+            std::swap(lhs, rhs);
+          }
+          impl tmp(lib::move(*rhs));
+#ifdef MPARK_EXCEPTIONS
+          // EXTENSION: When the move construction of `lhs` into `rhs` throws
+          // and `tmp` is nothrow move constructible then we move `tmp` back
+          // into `rhs` and provide the strong exception safety guarantee.
+          try {
+            this->generic_construct(*rhs, lib::move(*lhs));
+          } catch (...) {
+            if (tmp.move_nothrow()) {
+              this->generic_construct(*rhs, lib::move(tmp));
+            }
+            throw;
+          }
+#else
+          this->generic_construct(*rhs, lib::move(*lhs));
+#endif
+          this->generic_construct(*lhs, lib::move(tmp));
+        }
+      }
+
+      private:
+#ifndef MPARK_GENERIC_LAMBDAS
+      struct swapper {
+        template <typename ThisAlt, typename ThatAlt>
+        inline void operator()(ThisAlt &this_alt, ThatAlt &that_alt) const {
+          using std::swap;
+          swap(this_alt.value, that_alt.value);
+        }
+      };
+#endif
+
+      inline constexpr bool move_nothrow() const {
+        return this->valueless_by_exception() ||
+               lib::array<bool, sizeof...(Ts)>{
+                   {std::is_nothrow_move_constructible<Ts>::value...}
+               }[this->index()];
+      }
+    };
+
+#undef MPARK_INHERITING_CTOR
+
+    template <std::size_t I, typename T>
+    struct overload_leaf {
+      using F = lib::size_constant<I> (*)(T);
+      operator F() const { return nullptr; }
+    };
+
+    template <typename... Ts>
+    struct overload_impl {
+      private:
+      template <typename>
+      struct impl;
+
+      template <std::size_t... Is>
+      struct impl<lib::index_sequence<Is...>> : overload_leaf<Is, Ts>... {};
+
+      public:
+      using type = impl<lib::index_sequence_for<Ts...>>;
+    };
+
+    template <typename... Ts>
+    using overload = typename overload_impl<Ts...>::type;
+
+    template <typename T, typename... Ts>
+    using best_match = lib::invoke_result_t<overload<Ts...>, T &&>;
+
+    template <typename T>
+    struct is_in_place_index : std::false_type {};
+
+    template <std::size_t I>
+    struct is_in_place_index<in_place_index_t<I>> : std::true_type {};
+
+    template <typename T>
+    struct is_in_place_type : std::false_type {};
+
+    template <typename T>
+    struct is_in_place_type<in_place_type_t<T>> : std::true_type {};
+
+  }  // detail
+
+  template <typename... Ts>
+  class variant {
+    static_assert(0 < sizeof...(Ts),
+                  "variant must consist of at least one alternative.");
+
+    static_assert(lib::all<!std::is_array<Ts>::value...>::value,
+                  "variant can not have an array type as an alternative.");
+
+    static_assert(lib::all<!std::is_reference<Ts>::value...>::value,
+                  "variant can not have a reference type as an alternative.");
+
+    static_assert(lib::all<!std::is_void<Ts>::value...>::value,
+                  "variant can not have a void type as an alternative.");
+
+    public:
+    template <
+        typename Front = lib::type_pack_element_t<0, Ts...>,
+        lib::enable_if_t<std::is_default_constructible<Front>::value, int> = 0>
+    inline constexpr variant() noexcept(
+        std::is_nothrow_default_constructible<Front>::value)
+        : impl_(in_place_index_t<0>{}) {}
+
+    variant(const variant &) = default;
+    variant(variant &&) = default;
+
+    template <
+        typename Arg,
+        typename Decayed = lib::decay_t<Arg>,
+        lib::enable_if_t<!std::is_same<Decayed, variant>::value, int> = 0,
+        lib::enable_if_t<!detail::is_in_place_index<Decayed>::value, int> = 0,
+        lib::enable_if_t<!detail::is_in_place_type<Decayed>::value, int> = 0,
+        std::size_t I = detail::best_match<Arg, Ts...>::value,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T, Arg>::value, int> = 0>
+    inline constexpr variant(Arg &&arg) noexcept(
+        std::is_nothrow_constructible<T, Arg>::value)
+        : impl_(in_place_index_t<I>{}, lib::forward<Arg>(arg)) {}
+
+    template <
+        std::size_t I,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline explicit constexpr variant(
+        in_place_index_t<I>,
+        Args &&... args) noexcept(std::is_nothrow_constructible<T,
+                                                                Args...>::value)
+        : impl_(in_place_index_t<I>{}, lib::forward<Args>(args)...) {}
+
+    template <
+        std::size_t I,
+        typename Up,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline explicit constexpr variant(
+        in_place_index_t<I>,
+        std::initializer_list<Up> il,
+        Args &&... args) noexcept(std::
+                                      is_nothrow_constructible<
+                                          T,
+                                          std::initializer_list<Up> &,
+                                          Args...>::value)
+        : impl_(in_place_index_t<I>{}, il, lib::forward<Args>(args)...) {}
+
+    template <
+        typename T,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline explicit constexpr variant(
+        in_place_type_t<T>,
+        Args &&... args) noexcept(std::is_nothrow_constructible<T,
+                                                                Args...>::value)
+        : impl_(in_place_index_t<I>{}, lib::forward<Args>(args)...) {}
+
+    template <
+        typename T,
+        typename Up,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        lib::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline explicit constexpr variant(
+        in_place_type_t<T>,
+        std::initializer_list<Up> il,
+        Args &&... args) noexcept(std::
+                                      is_nothrow_constructible<
+                                          T,
+                                          std::initializer_list<Up> &,
+                                          Args...>::value)
+        : impl_(in_place_index_t<I>{}, il, lib::forward<Args>(args)...) {}
+
+    ~variant() = default;
+
+    variant &operator=(const variant &) = default;
+    variant &operator=(variant &&) = default;
+
+    template <typename Arg,
+              lib::enable_if_t<!std::is_same<lib::decay_t<Arg>, variant>::value,
+                               int> = 0,
+              std::size_t I = detail::best_match<Arg, Ts...>::value,
+              typename T = lib::type_pack_element_t<I, Ts...>,
+              lib::enable_if_t<(std::is_assignable<T &, Arg>::value &&
+                                std::is_constructible<T, Arg>::value),
+                               int> = 0>
+    inline variant &operator=(Arg &&arg) noexcept(
+        (std::is_nothrow_assignable<T &, Arg>::value &&
+         std::is_nothrow_constructible<T, Arg>::value)) {
+      impl_.template assign<I>(lib::forward<Arg>(arg));
+      return *this;
+    }
+
+    template <
+        std::size_t I,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline T &emplace(Args &&... args) {
+      return impl_.template emplace<I>(lib::forward<Args>(args)...);
+    }
+
+    template <
+        std::size_t I,
+        typename Up,
+        typename... Args,
+        typename T = lib::type_pack_element_t<I, Ts...>,
+        lib::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline T &emplace(std::initializer_list<Up> il, Args &&... args) {
+      return impl_.template emplace<I>(il, lib::forward<Args>(args)...);
+    }
+
+    template <
+        typename T,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+    inline T &emplace(Args &&... args) {
+      return impl_.template emplace<I>(lib::forward<Args>(args)...);
+    }
+
+    template <
+        typename T,
+        typename Up,
+        typename... Args,
+        std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+        lib::enable_if_t<std::is_constructible<T,
+                                               std::initializer_list<Up> &,
+                                               Args...>::value,
+                         int> = 0>
+    inline T &emplace(std::initializer_list<Up> il, Args &&... args) {
+      return impl_.template emplace<I>(il, lib::forward<Args>(args)...);
+    }
+
+    inline constexpr bool valueless_by_exception() const noexcept {
+      return impl_.valueless_by_exception();
+    }
+
+    inline constexpr std::size_t index() const noexcept {
+      return impl_.index();
+    }
+
+    template <bool Dummy = true,
+              lib::enable_if_t<
+                  lib::all<Dummy,
+                           (lib::dependent_type<std::is_move_constructible<Ts>,
+                                                Dummy>::value &&
+                            lib::dependent_type<lib::is_swappable<Ts>,
+                                                Dummy>::value)...>::value,
+                  int> = 0>
+    inline void swap(variant &that) noexcept(
+        lib::all<(std::is_nothrow_move_constructible<Ts>::value &&
+                  lib::is_nothrow_swappable<Ts>::value)...>::value) {
+      impl_.swap(that.impl_);
+    }
+
+    private:
+    detail::impl<Ts...> impl_;
+
+    friend struct detail::access::variant;
+    friend struct detail::visitation::variant;
+  };
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr bool holds_alternative(const variant<Ts...> &v) noexcept {
+    return v.index() == I;
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr bool holds_alternative(const variant<Ts...> &v) noexcept {
+    return holds_alternative<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  namespace detail {
+    template <std::size_t I, typename V>
+    struct generic_get_impl {
+      constexpr generic_get_impl(int) noexcept {}
+
+      constexpr AUTO_REFREF operator()(V &&v) const
+        AUTO_REFREF_RETURN(
+            access::variant::get_alt<I>(lib::forward<V>(v)).value)
+    };
+
+    template <std::size_t I, typename V>
+    inline constexpr AUTO_REFREF generic_get(V &&v)
+      AUTO_REFREF_RETURN(generic_get_impl<I, V>(
+          holds_alternative<I>(v) ? 0 : (throw_bad_variant_access(), 0))(
+          lib::forward<V>(v)))
+  }  // namespace detail
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr variant_alternative_t<I, variant<Ts...>> &get(
+      variant<Ts...> &v) {
+    return detail::generic_get<I>(v);
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr variant_alternative_t<I, variant<Ts...>> &&get(
+      variant<Ts...> &&v) {
+    return detail::generic_get<I>(lib::move(v));
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr const variant_alternative_t<I, variant<Ts...>> &get(
+      const variant<Ts...> &v) {
+    return detail::generic_get<I>(v);
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr const variant_alternative_t<I, variant<Ts...>> &&get(
+      const variant<Ts...> &&v) {
+    return detail::generic_get<I>(lib::move(v));
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr T &get(variant<Ts...> &v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr T &&get(variant<Ts...> &&v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(lib::move(v));
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr const T &get(const variant<Ts...> &v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr const T &&get(const variant<Ts...> &&v) {
+    return get<detail::find_index_checked<T, Ts...>::value>(lib::move(v));
+  }
+
+  namespace detail {
+
+    template <std::size_t I, typename V>
+    inline constexpr /* auto * */ AUTO generic_get_if(V *v) noexcept
+      AUTO_RETURN(v && holds_alternative<I>(*v)
+                      ? lib::addressof(access::variant::get_alt<I>(*v).value)
+                      : nullptr)
+
+  }  // namespace detail
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr lib::add_pointer_t<variant_alternative_t<I, variant<Ts...>>>
+  get_if(variant<Ts...> *v) noexcept {
+    return detail::generic_get_if<I>(v);
+  }
+
+  template <std::size_t I, typename... Ts>
+  inline constexpr lib::add_pointer_t<
+      const variant_alternative_t<I, variant<Ts...>>>
+  get_if(const variant<Ts...> *v) noexcept {
+    return detail::generic_get_if<I>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr lib::add_pointer_t<T>
+  get_if(variant<Ts...> *v) noexcept {
+    return get_if<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  template <typename T, typename... Ts>
+  inline constexpr lib::add_pointer_t<const T>
+  get_if(const variant<Ts...> *v) noexcept {
+    return get_if<detail::find_index_checked<T, Ts...>::value>(v);
+  }
+
+  namespace detail {
+    template <typename RelOp>
+    struct convert_to_bool {
+      template <typename Lhs, typename Rhs>
+      inline constexpr bool operator()(Lhs &&lhs, Rhs &&rhs) const {
+        static_assert(std::is_convertible<lib::invoke_result_t<RelOp, Lhs, Rhs>,
+                                          bool>::value,
+                      "relational operators must return a type"
+                      " implicitly convertible to bool");
+        return lib::invoke(
+            RelOp{}, lib::forward<Lhs>(lhs), lib::forward<Rhs>(rhs));
+      }
+    };
+  }  // namespace detail
+
+  template <typename... Ts>
+  inline constexpr bool operator==(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using equal_to = detail::convert_to_bool<lib::equal_to>;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (lhs.index() != rhs.index()) return false;
+    if (lhs.valueless_by_exception()) return true;
+    return variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs);
+#else
+    return lhs.index() == rhs.index() &&
+           (lhs.valueless_by_exception() ||
+            variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator!=(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using not_equal_to = detail::convert_to_bool<lib::not_equal_to>;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (lhs.index() != rhs.index()) return true;
+    if (lhs.valueless_by_exception()) return false;
+    return variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs);
+#else
+    return lhs.index() != rhs.index() ||
+           (!lhs.valueless_by_exception() &&
+            variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator<(const variant<Ts...> &lhs,
+                                  const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using less = detail::convert_to_bool<lib::less>;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (rhs.valueless_by_exception()) return false;
+    if (lhs.valueless_by_exception()) return true;
+    if (lhs.index() < rhs.index()) return true;
+    if (lhs.index() > rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), less{}, lhs, rhs);
+#else
+    return !rhs.valueless_by_exception() &&
+           (lhs.valueless_by_exception() || lhs.index() < rhs.index() ||
+            (lhs.index() == rhs.index() &&
+             variant::visit_value_at(lhs.index(), less{}, lhs, rhs)));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator>(const variant<Ts...> &lhs,
+                                  const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using greater = detail::convert_to_bool<lib::greater>;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (lhs.valueless_by_exception()) return false;
+    if (rhs.valueless_by_exception()) return true;
+    if (lhs.index() > rhs.index()) return true;
+    if (lhs.index() < rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), greater{}, lhs, rhs);
+#else
+    return !lhs.valueless_by_exception() &&
+           (rhs.valueless_by_exception() || lhs.index() > rhs.index() ||
+            (lhs.index() == rhs.index() &&
+             variant::visit_value_at(lhs.index(), greater{}, lhs, rhs)));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator<=(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using less_equal = detail::convert_to_bool<lib::less_equal>;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (lhs.valueless_by_exception()) return true;
+    if (rhs.valueless_by_exception()) return false;
+    if (lhs.index() < rhs.index()) return true;
+    if (lhs.index() > rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs);
+#else
+    return lhs.valueless_by_exception() ||
+           (!rhs.valueless_by_exception() &&
+            (lhs.index() < rhs.index() ||
+             (lhs.index() == rhs.index() &&
+              variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs))));
+#endif
+  }
+
+  template <typename... Ts>
+  inline constexpr bool operator>=(const variant<Ts...> &lhs,
+                                   const variant<Ts...> &rhs) {
+    using detail::visitation::variant;
+    using greater_equal = detail::convert_to_bool<lib::greater_equal>;
+#ifdef MPARK_CPP14_CONSTEXPR
+    if (rhs.valueless_by_exception()) return true;
+    if (lhs.valueless_by_exception()) return false;
+    if (lhs.index() > rhs.index()) return true;
+    if (lhs.index() < rhs.index()) return false;
+    return variant::visit_value_at(lhs.index(), greater_equal{}, lhs, rhs);
+#else
+    return rhs.valueless_by_exception() ||
+           (!lhs.valueless_by_exception() &&
+            (lhs.index() > rhs.index() ||
+             (lhs.index() == rhs.index() &&
+              variant::visit_value_at(
+                  lhs.index(), greater_equal{}, lhs, rhs))));
+#endif
+  }
+
+  struct monostate {};
+
+  inline constexpr bool operator<(monostate, monostate) noexcept {
+    return false;
+  }
+
+  inline constexpr bool operator>(monostate, monostate) noexcept {
+    return false;
+  }
+
+  inline constexpr bool operator<=(monostate, monostate) noexcept {
+    return true;
+  }
+
+  inline constexpr bool operator>=(monostate, monostate) noexcept {
+    return true;
+  }
+
+  inline constexpr bool operator==(monostate, monostate) noexcept {
+    return true;
+  }
+
+  inline constexpr bool operator!=(monostate, monostate) noexcept {
+    return false;
+  }
+
+#ifdef MPARK_CPP14_CONSTEXPR
+  namespace detail {
+
+    inline constexpr bool all(std::initializer_list<bool> bs) {
+      for (bool b : bs) {
+        if (!b) {
+          return false;
+        }
+      }
+      return true;
+    }
+
+  }  // namespace detail
+
+  template <typename Visitor, typename... Vs>
+  inline constexpr decltype(auto) visit(Visitor &&visitor, Vs &&... vs) {
+    // NOTE: fix for nvcc, see https://github.com/mpark/variant/issues/63
+//    return (detail::all({!vs.valueless_by_exception()...})
+    return (detail::all(std::initializer_list<bool>({!vs.valueless_by_exception()...}))
+                ? (void)0
+                : throw_bad_variant_access()),
+           detail::visitation::variant::visit_value(
+               lib::forward<Visitor>(visitor), lib::forward<Vs>(vs)...);
+  }
+#else
+  namespace detail {
+
+    template <std::size_t N>
+    inline constexpr bool all_impl(const lib::array<bool, N> &bs,
+                                   std::size_t idx) {
+      return idx >= N || (bs[idx] && all_impl(bs, idx + 1));
+    }
+
+    template <std::size_t N>
+    inline constexpr bool all(const lib::array<bool, N> &bs) {
+      return all_impl(bs, 0);
+    }
+
+  }  // namespace detail
+
+  template <typename Visitor, typename... Vs>
+  inline constexpr DECLTYPE_AUTO visit(Visitor &&visitor, Vs &&... vs)
+    DECLTYPE_AUTO_RETURN(
+        (detail::all(
+             lib::array<bool, sizeof...(Vs)>{{!vs.valueless_by_exception()...}})
+             ? (void)0
+             : throw_bad_variant_access()),
+        detail::visitation::variant::visit_value(lib::forward<Visitor>(visitor),
+                                                 lib::forward<Vs>(vs)...))
+#endif
+
+  template <typename... Ts>
+  inline auto swap(variant<Ts...> &lhs,
+                   variant<Ts...> &rhs) noexcept(noexcept(lhs.swap(rhs)))
+      -> decltype(lhs.swap(rhs)) {
+    lhs.swap(rhs);
+  }
+
+  namespace detail {
+
+    template <typename T, typename...>
+    using enabled_type = T;
+
+    namespace hash {
+
+      template <typename H, typename K>
+      constexpr bool meets_requirements() noexcept {
+        return std::is_copy_constructible<H>::value &&
+               std::is_move_constructible<H>::value &&
+               lib::is_invocable_r<std::size_t, H, const K &>::value;
+      }
+
+      template <typename K>
+      constexpr bool is_enabled() noexcept {
+        using H = std::hash<K>;
+        return meets_requirements<H, K>() &&
+               std::is_default_constructible<H>::value &&
+               std::is_copy_assignable<H>::value &&
+               std::is_move_assignable<H>::value;
+      }
+
+    }  // namespace hash
+
+  }  // namespace detail
+
+#undef AUTO
+#undef AUTO_RETURN
+
+#undef AUTO_REFREF
+#undef AUTO_REFREF_RETURN
+
+#undef DECLTYPE_AUTO
+#undef DECLTYPE_AUTO_RETURN
+
+}  // namespace mpark
+
+namespace std {
+
+  template <typename... Ts>
+  struct hash<mpark::detail::enabled_type<
+      mpark::variant<Ts...>,
+      mpark::lib::enable_if_t<mpark::lib::all<mpark::detail::hash::is_enabled<
+          mpark::lib::remove_const_t<Ts>>()...>::value>>> {
+    using argument_type = mpark::variant<Ts...>;
+    using result_type = std::size_t;
+
+    inline result_type operator()(const argument_type &v) const {
+      using mpark::detail::visitation::variant;
+      std::size_t result =
+          v.valueless_by_exception()
+              ? 299792458  // Random value chosen by the universe upon creation
+              : variant::visit_alt(
+#ifdef MPARK_GENERIC_LAMBDAS
+                    [](const auto &alt) {
+                      using alt_type = mpark::lib::decay_t<decltype(alt)>;
+                      using value_type = mpark::lib::remove_const_t<
+                          typename alt_type::value_type>;
+                      return hash<value_type>{}(alt.value);
+                    }
+#else
+                    hasher{}
+#endif
+                    ,
+                    v);
+      return hash_combine(result, hash<std::size_t>{}(v.index()));
+    }
+
+    private:
+#ifndef MPARK_GENERIC_LAMBDAS
+    struct hasher {
+      template <typename Alt>
+      inline std::size_t operator()(const Alt &alt) const {
+        using alt_type = mpark::lib::decay_t<Alt>;
+        using value_type =
+            mpark::lib::remove_const_t<typename alt_type::value_type>;
+        return hash<value_type>{}(alt.value);
+      }
+    };
+#endif
+
+    static std::size_t hash_combine(std::size_t lhs, std::size_t rhs) {
+      return lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
+    }
+  };
+
+  template <>
+  struct hash<mpark::monostate> {
+    using argument_type = mpark::monostate;
+    using result_type = std::size_t;
+
+    inline result_type operator()(const argument_type &) const noexcept {
+      return 66740831;  // return a fundamentally attractive random value.
+    }
+  };
+
+}  // namespace std
+
+#endif  // MPARK_VARIANT_HPP
diff --git a/src/Tools/tnl-dicom-reader.cpp b/src/Tools/tnl-dicom-reader.cpp
index c0f770e497b95ba2758cb852a36a3c3ccf562069..52eb7f7786d1847eb7be452b21497210d94de2d7 100644
--- a/src/Tools/tnl-dicom-reader.cpp
+++ b/src/Tools/tnl-dicom-reader.cpp
@@ -8,9 +8,8 @@
 
 /* See Copyright Notice in tnl/Copyright */
 
-#include <TNL/Config/ConfigDescription.h>
-#include <TNL/Config/ParameterContainer.h>
-#include <TNL/Images//DicomSeries.h>
+#include <TNL/Config/parseCommandLine.h>
+#include <TNL/Images/DicomSeries.h>
 #include <TNL/FileName.h>
 
 using namespace TNL;
@@ -85,15 +84,12 @@ int main( int argc, char* argv[] )
    Config::ConfigDescription configDescription;
    setupConfig( configDescription );
    if( ! parseCommandLine( argc, argv, configDescription, parameters ) )
-   {
-      configDescription.printUsage( argv[ 0 ] );
       return EXIT_FAILURE;
-   }
    if( ! parameters.checkParameter( "dicom-files" ) &&
        ! parameters.checkParameter( "dicom-series") )
    {
        std::cerr << "Neither DICOM series nor DICOM files are given." << std::endl;
-       configDescription.printUsage( argv[ 0 ] );
+       Config::printUsage( configDescription, argv[ 0 ] );
        return EXIT_FAILURE;
    }
 #ifdef HAVE_DCMTK_H
diff --git a/src/Tools/tnl-diff.cpp b/src/Tools/tnl-diff.cpp
index 7e442c982fe777abc2d02259a881f28d945e682f..84d7a08096e8b89e3ecc8278f82bcb586f5cbfe6 100644
--- a/src/Tools/tnl-diff.cpp
+++ b/src/Tools/tnl-diff.cpp
@@ -9,6 +9,7 @@
 /* See Copyright Notice in tnl/Copyright */
 
 #include "tnl-diff.h"
+#include <TNL/Config/parseCommandLine.h>
 #include <TNL/Meshes/DummyMesh.h>
 #include <TNL/Meshes/Grid.h>
 
@@ -37,10 +38,7 @@ int main( int argc, char* argv[] )
    Config::ConfigDescription conf_desc;
    setupConfig( conf_desc );
    if( ! parseCommandLine( argc, argv, conf_desc, parameters ) )
-   {
-      conf_desc.printUsage( argv[ 0 ] );
-      return 1;
-   }
+      return EXIT_FAILURE;
 
    String meshFile = parameters.getParameter< String >( "mesh" );
    /*if( meshFile == "" )
diff --git a/src/Tools/tnl-grid-setup.cpp b/src/Tools/tnl-grid-setup.cpp
index 261f1cf716d6f7b453b27a7acb3f2bfd7d6daa9a..115f02065920e50507acef551b68128d5be90dd4 100644
--- a/src/Tools/tnl-grid-setup.cpp
+++ b/src/Tools/tnl-grid-setup.cpp
@@ -9,7 +9,7 @@
 /* See Copyright Notice in tnl/Copyright */
 
 #include "tnl-grid-setup.h"
-#include <TNL/Config/ParameterContainer.h>
+#include <TNL/Config/parseCommandLine.h>
 
 void configSetup( Config::ConfigDescription& config )
 {
diff --git a/src/Tools/tnl-image-converter.cpp b/src/Tools/tnl-image-converter.cpp
index 493ff503c8fd035681cadb608cecd5161429d110..d1ef8fa92b4dfceac9a8d8426c66b823020cc87e 100644
--- a/src/Tools/tnl-image-converter.cpp
+++ b/src/Tools/tnl-image-converter.cpp
@@ -8,8 +8,7 @@
 
 /* See Copyright Notice in tnl/Copyright */
 
-#include <TNL/Config/ConfigDescription.h>
-#include <TNL/Config/ParameterContainer.h>
+#include <TNL/Config/parseCommandLine.h>
 #include <TNL/FileName.h>
 #include <TNL/Meshes/Grid.h>
 #include <TNL/Pointers/SharedPointer.h>
@@ -206,15 +205,12 @@ int main( int argc, char* argv[] )
    Config::ConfigDescription configDescription;
    configSetup( configDescription );
    if( ! parseCommandLine( argc, argv, configDescription, parameters ) )
-   {
-      configDescription.printUsage( argv[ 0 ] );
       return EXIT_FAILURE;
-   }
    if( ! parameters.checkParameter( "input-images" ) &&
        ! parameters.checkParameter( "input-files") )
    {
        std::cerr << "Neither input images nor input .tnl files are given." << std::endl;
-       configDescription.printUsage( argv[ 0 ] );
+       Config::printUsage( configDescription, argv[ 0 ] );
        return EXIT_FAILURE;
    }
    if( parameters.checkParameter( "input-images" ) )
diff --git a/src/Tools/tnl-init.cpp b/src/Tools/tnl-init.cpp
index 9accb36342d2c37e4979ef24deb2d56ce67d6b7d..220874ebc9d549b5923078f33b9bd119d5a29af5 100644
--- a/src/Tools/tnl-init.cpp
+++ b/src/Tools/tnl-init.cpp
@@ -11,8 +11,7 @@
 #include "tnl-init.h"
 
 #include <TNL/File.h>
-#include <TNL/Config/ConfigDescription.h>
-#include <TNL/Config/ParameterContainer.h>
+#include <TNL/Config/parseCommandLine.h>
 #include <TNL/Functions/TestFunction.h>
 #include <TNL/Meshes/DummyMesh.h>
 #include <TNL/Meshes/Grid.h>
diff --git a/src/Tools/tnl-lattice-init.cpp b/src/Tools/tnl-lattice-init.cpp
index f9d746f89f20b66d89614df910aa8aae0b76cd75..a4aa1ba0cfdeb05dd97f168d62f0069edd9159a9 100644
--- a/src/Tools/tnl-lattice-init.cpp
+++ b/src/Tools/tnl-lattice-init.cpp
@@ -10,8 +10,7 @@
 
 #include "tnl-lattice-init.h"
 
-#include <TNL/Config/ConfigDescription.h>
-#include <TNL/Config/ParameterContainer.h>
+#include <TNL/Config/parseCommandLine.h>
 
 
 using namespace TNL;
diff --git a/src/Tools/tnl-mesh-converter.cpp b/src/Tools/tnl-mesh-converter.cpp
index cd9de1a597c266722092d1851ac41e2c8dfa7217..ac8f9921966ba96d915c479056111729fd9b2dad 100644
--- a/src/Tools/tnl-mesh-converter.cpp
+++ b/src/Tools/tnl-mesh-converter.cpp
@@ -8,7 +8,7 @@
 
 /* See Copyright Notice in tnl/Copyright */
 
-#include <TNL/Config/ParameterContainer.h>
+#include <TNL/Config/parseCommandLine.h>
 #include <TNL/Meshes/TypeResolver/TypeResolver.h>
 #include <TNL/Meshes/Writers/VTKWriter.h>
 #include <TNL/Meshes/Writers/NetgenWriter.h>
@@ -124,9 +124,8 @@ int main( int argc, char* argv[] )
  
    configSetup( conf_desc );
 
-   if( ! parseCommandLine( argc, argv, conf_desc, parameters ) ) {
+   if( ! parseCommandLine( argc, argv, conf_desc, parameters ) )
       return EXIT_FAILURE;
-   }
 
    const String inputFileName = parameters.getParameter< String >( "input-file" );
    const String outputFileName = parameters.getParameter< String >( "output-file" );
diff --git a/src/Tools/tnl-view.cpp b/src/Tools/tnl-view.cpp
index 32fac87d9645c066fe111eef1fb2c66961ae1c5c..a2774699166e84a8063daf9bc10a2f93480d8bc6 100644
--- a/src/Tools/tnl-view.cpp
+++ b/src/Tools/tnl-view.cpp
@@ -11,8 +11,7 @@
 #include "tnl-view.h"
 #include <cstdlib>
 #include <TNL/File.h>
-#include <TNL/Config/ConfigDescription.h>
-#include <TNL/Config/ParameterContainer.h>
+#include <TNL/Config/parseCommandLine.h>
 #include <TNL/Meshes/DummyMesh.h>
 #include <TNL/Meshes/Grid.h>
 #include <TNL/Meshes/TypeResolver/TypeResolver.h>